准备写一篇BlobAsset的用法

#if !UNITY_DOTSPLAYER
using UnityEngine;
using NUnit.Framework;
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Entities;
using Unity.Entities.Tests;
using Unity.Collections.LowLevel.Unsafe;
using Assert = NUnit.Framework.Assert;

public class BlobTests : ECSTestsFixture
{
    //@TODO: Test Prevent NativeArray and other containers inside of Blob data
    //@TODO: Test Prevent BlobPtr, BlobArray onto job struct
    //@TODO: Various tests trying to break the Allocator. eg. mix multiple BlobAllocator in the same BlobRoot...

    struct MyData
    {
        public BlobArray<float> floatArray;
        public BlobPtr<float> nullPtr;
        public BlobPtr<Vector3> oneVector3;
        public float embeddedFloat;
        public BlobArray<BlobArray<int>> nestedArray;
        public BlobString str;
        public BlobString emptyStr;
    }

    static unsafe BlobAssetReference<MyData> ConstructBlobData()
    {
        var builder = new BlobBuilder(Allocator.Temp);

        ref var root = ref builder.ConstructRoot<MyData>();

        var floatArray = builder.Allocate(ref root.floatArray, 3);
        ref Vector3 oneVector3 = ref builder.Allocate(ref root.oneVector3);
        var nestedArrays = builder.Allocate(ref root.nestedArray, 2);

        var nestedArray0 = builder.Allocate(ref nestedArrays[0], 1);
        var nestedArray1 = builder.Allocate(ref nestedArrays[1], 2);

        builder.AllocateString(ref root.str, "Blah");
        builder.AllocateString(ref root.emptyStr, "");

        nestedArray0[0] = 0;
        nestedArray1[0] = 1;
        nestedArray1[1] = 2;

        floatArray[0] = 0;
        floatArray[1] = 1;
        floatArray[2] = 2;

        root.embeddedFloat = 4;
        oneVector3 = new Vector3(3, 3, 3);
        
        var blobAsset = builder.CreateBlobAssetReference<MyData>(Allocator.Persistent);

        builder.Dispose();

        return blobAsset;
    }

    static void ValidateBlobData(ref MyData root)
    {
        // not using Assert.AreEqual here because the asserts have to execute in burst jobs 
        
        if (3 != root.floatArray.Length)
            throw new AssertionException("ValidateBlobData didn't match");
        if (0 != root.floatArray[0])
            throw new AssertionException("ValidateBlobData didn't match");
        if (1 != root.floatArray[1])
            throw new AssertionException("ValidateBlobData didn't match");
        if (2 != root.floatArray[2])
            throw new AssertionException("ValidateBlobData didn't match");
        if (new Vector3(3, 3, 3) != root.oneVector3.Value)
            throw new AssertionException("ValidateBlobData didn't match");
        
        if (4 != root.embeddedFloat)
            throw new AssertionException("ValidateBlobData didn't match");
        
        if (1 != root.nestedArray[0].Length)
            throw new AssertionException("ValidateBlobData didn't match");
        if (2 != root.nestedArray[1].Length)
            throw new AssertionException("ValidateBlobData didn't match");
        if (0 != root.nestedArray[0][0])
            throw new AssertionException("ValidateBlobData didn't match");
        if (1 != root.nestedArray[1][0])
            throw new AssertionException("ValidateBlobData didn't match");
        if (2 != root.nestedArray[1][1])
            throw new AssertionException("ValidateBlobData didn't match");
        ValidateBlobString(ref root);
    }

    [BurstDiscard]
    static void ValidateBlobString(ref MyData root)
    {
        var str = root.str.ToString();
        if ("Blah" != str)
            throw new AssertionException("ValidateBlobData didn't match");
        var emptyStr = root.emptyStr.ToString();
        if ("" != emptyStr)
            throw new AssertionException("ValidateBlobData didn't match");
    }


    static void ValidateBlobDataBurst(ref MyData root)
    {
        Assert.AreEqual(3, root.floatArray.Length);
        Assert.AreEqual(0, root.floatArray[0]);
        Assert.AreEqual(1, root.floatArray[1]);
        Assert.AreEqual(2, root.floatArray[2]);
        Assert.AreEqual(new Vector3(3, 3, 3), root.oneVector3.Value);
        Assert.AreEqual(4, root.embeddedFloat);

        Assert.AreEqual(1, root.nestedArray[0].Length);
        Assert.AreEqual(2, root.nestedArray[1].Length);

        Assert.AreEqual(0, root.nestedArray[0][0]);
        Assert.AreEqual(1, root.nestedArray[1][0]);
        Assert.AreEqual(2, root.nestedArray[1][1]);
    }


    [Test]
    public unsafe void CreateBlobData()
    {
        var blob = ConstructBlobData();
        ValidateBlobData(ref blob.Value);

        blob.Dispose();
    }

    [Test]
    public unsafe void BlobAccessAfterReleaseThrows()
    {
        var blob = ConstructBlobData();
        var blobCopy = blob;
        blob.Dispose();
        
        Assert.Throws<InvalidOperationException>(() => { blobCopy.GetUnsafePtr(); });
        Assert.IsTrue(blob.GetUnsafePtr() == null);

        Assert.Throws<InvalidOperationException>(() => { var p = blobCopy.Value.embeddedFloat; });
        Assert.Throws<InvalidOperationException>(() => { var p = blobCopy.Value.embeddedFloat; });

        Assert.Throws<InvalidOperationException>(() => { blobCopy.Dispose(); });
        Assert.Throws<InvalidOperationException>(() => { blob.Dispose(); });
    }

    struct ComponentWithBlobData : IComponentData
    {
        public bool DidSucceed;
        public BlobAssetReference<MyData> blobAsset;
    }


    [BurstCompile(CompileSynchronously = true)]
    struct ConstructAccessAndDisposeBlobData : IJob
    {
        public void Execute()
        {
            var blobData = ConstructBlobData();
            ValidateBlobData(ref blobData.Value);
            blobData.Dispose();            
        }
    }

    [Ignore("DisposeSentinel in NativeList prevents this. Fix should be in 19.3")]
    [Test]
    public  void BurstedConstructionAndAccess()
    {
        new ConstructAccessAndDisposeBlobData().Schedule().Complete();
    }

    [BurstCompile(CompileSynchronously = true)]
    struct AccessAndDisposeBlobDataBurst : IJobForEach<ComponentWithBlobData>
    {
        public void Execute(ref ComponentWithBlobData data)
        {
            ValidateBlobData(ref data.blobAsset.Value);
            data.blobAsset.Dispose();
            data.DidSucceed = true;
        }
    }

    [Test]
    public  void ReadAndDestroyBlobDataFromBurstJob()
    {
        var entities = CreateUniqueBlob();

        new AccessAndDisposeBlobDataBurst().Schedule(EmptySystem).Complete();

        foreach (var e in entities)
        {
            Assert.IsTrue(m_Manager.GetComponentData<ComponentWithBlobData>(e).DidSucceed);
            Assert.IsFalse(m_Manager.GetComponentData<ComponentWithBlobData>(e).blobAsset.IsCreated);
        }
    }

    struct ValidateBlobInComponentJob : IJobForEach<ComponentWithBlobData>
    {
        public bool ExpectException;

        public unsafe void Execute(ref ComponentWithBlobData component)
        {
            if (ExpectException)
            {
                var blobAsset = component.blobAsset;
                Assert.Throws<InvalidOperationException>(() => { blobAsset.GetUnsafePtr(); });
            }
            else
            {
                ValidateBlobData(ref component.blobAsset.Value);
            }

            component.DidSucceed = true;
        }
    }


    [Test]
    public unsafe void ParallelBlobAccessFromEntityJob()
    {
        var blob = CreateSharedBlob();

        var jobData = new ValidateBlobInComponentJob();

        var jobHandle = jobData.Schedule(EmptySystem);

        ValidateBlobData(ref blob.Value);

        jobHandle.Complete();

        blob.Dispose();
    }

    [Test]
    public void DestroyedBlobAccessFromEntityJobThrows()
    {
        var blob = CreateSharedBlob();

        blob.Dispose();

        var jobData = new ValidateBlobInComponentJob();
        jobData.ExpectException = true;
        jobData.Schedule(EmptySystem).Complete();
    }

    [Test]
    public void BlobAssetReferenceIsComparable()
    {
        var blob1 = ConstructBlobData();
        var blob2 = ConstructBlobData();
        var blobNull = new BlobAssetReference<MyData>();

        var temp1 = blob1;

        Assert.IsTrue(blob1 != blob2);
        Assert.IsTrue(blob1 != BlobAssetReference<MyData>.Null);
        Assert.IsTrue(blobNull == BlobAssetReference<MyData>.Null);
        Assert.IsTrue(blob1 == temp1);
        Assert.IsTrue(blob2 != temp1);

        blob1.Dispose();
        blob2.Dispose();
    }

    [Test]
    public void SourceBlobArrayThrowsOnIndex()
    {
        var builder = new BlobBuilder(Allocator.Temp);

        Assert.Throws<IndexOutOfRangeException>(() =>
        {
            //can't access ref variable if it's created outside of the lambda
            ref var root = ref builder.ConstructRoot<MyData>();
            builder.Allocate(ref root.floatArray, 3);
            
            // Throw on access expected here
            root.floatArray[0] = 7;
        });

        builder.Dispose();
    }

    [Test]
    public void BlobArrayToArrayCopiesResults()
    {
        var blob = ConstructBlobData();
        ref MyData root = ref blob.Value;

        var floatArray = root.floatArray.ToArray();
        Assert.AreEqual(new float[]{ 0, 1, 2 }, floatArray);

        blob.Dispose();
    }

    [Test]
    public void SourceBlobPtrThrowsOnDereference()
    {
        var builder = new BlobBuilder(Allocator.Temp);

        Assert.Throws<InvalidOperationException>(() =>
        {
            //can't access ref variable if it's created outside of the lambda
            ref var root = ref builder.ConstructRoot<MyData>();
            builder.Allocate(ref root.oneVector3);
            
            // Throw on access expected here
            root.oneVector3.Value = Vector3.zero;
        });

        builder.Dispose();
    }

    struct AlignmentTest
    {
        public BlobPtr<short> shortPointer;
        public BlobPtr<int> intPointer;
        public BlobPtr<byte> bytePointer;
        public BlobArray<int> intArray;
    }

    static unsafe void AssertAlignment(void* p, int alignment)
    {
        ulong mask = (ulong) alignment - 1;
        Assert.IsTrue(((ulong) (IntPtr) p & mask) == 0);
    }

    [Test]
    public unsafe void BasicAlignmentWorks()
    {
        var builder = new BlobBuilder(Allocator.Temp);
        ref var root = ref builder.ConstructRoot<BlobArray<AlignmentTest>>();
        Assert.AreEqual(4, UnsafeUtility.AlignOf<int>());

        const int count = 100;
        var topLevelArray = builder.Allocate(ref root, count);
        for (int x = 0; x < count; ++x)
        {
            builder.Allocate(ref topLevelArray[x].shortPointer);
            builder.Allocate(ref topLevelArray[x].intPointer);
            builder.Allocate(ref topLevelArray[x].bytePointer);
            builder.Allocate(ref topLevelArray[x].intArray, x + 1);
        }

        var blob = builder.CreateBlobAssetReference<BlobArray<AlignmentTest>>(Allocator.Temp);
        builder.Dispose();

        for (int x = 0; x < count; ++x)
        {
            AssertAlignment(blob.Value[x].shortPointer.GetUnsafePtr(), 2);
            AssertAlignment(blob.Value[x].intPointer.GetUnsafePtr(), 4);
            AssertAlignment(blob.Value[x].intArray.GetUnsafePtr(), 4);
        }

        blob.Dispose();
    }

    [Test]
    public unsafe void CreatedBlobsAre16ByteAligned()
    {
        var blobAssetReference = BlobAssetReference<int>.Create(42);
        AssertAlignment(blobAssetReference.GetUnsafePtr(), 16);
    }

    [Test]
    public void BlobBuilderArrayThrowsOnOutOfBoundsIndex()
    {
        using (var builder = new BlobBuilder(Allocator.Temp, 128))
        {
            Assert.Throws<IndexOutOfRangeException>(() =>
            {
                ref var root = ref builder.ConstructRoot<BlobArray<int>>();
                var array = builder.Allocate(ref root, 100);
                array[100] = 7;
            });
        }
    }

    [Test]
    public void AllocationsLargerThanChunkSizeWorks()
    {
        var builder = new BlobBuilder(Allocator.Temp, 128);
        ref var root = ref builder.ConstructRoot<BlobArray<int>>();
        const int count = 100;
        var array = builder.Allocate(ref root, count);
        for (int i = 0; i < count; i++)
            array[i] = i;

        var blob = builder.CreateBlobAssetReference<BlobArray<int>>(Allocator.Temp);
        builder.Dispose();

        for (int i = 0; i < count; i++)
            Assert.AreEqual(i, blob.Value[i]);

        blob.Dispose();
    }

    [Test]
    public void CreatingLargeBlobAssetWorks()
    {
        var builder = new BlobBuilder(Allocator.Temp, 512);
        ref var root = ref builder.ConstructRoot<BlobArray<BlobArray<BlobArray<BlobPtr<int>>>>>();

        const int topLevelCount = 100;

        int expectedValue = 42;
        var level0 = builder.Allocate(ref root, topLevelCount);
        for (int x = 0; x < topLevelCount; x++)
        {
            var level1 = builder.Allocate(ref level0[x], x + 1);
            for (int y = 0; y < x + 1; y++)
            {
                var level2 = builder.Allocate(ref level1[y], y + 1);
                for (int z = 0; z < y + 1; z++)
                {
                    ref var i = ref builder.Allocate(ref level2[z]);
                    i = expectedValue++;
                }
            }
        }

        var blob = builder.CreateBlobAssetReference<BlobArray<BlobArray<BlobArray<BlobPtr<int>>>>>(Allocator.Temp);
        builder.Dispose();

        expectedValue = 42;
        for (int x = 0; x < topLevelCount; x++)
        {
            for (int y = 0; y < x + 1; y++)
            {
                for (int z = 0; z < y + 1; z++)
                {
                    int value = blob.Value[x][y][z].Value;

                    if (expectedValue != value)
                        Assert.AreEqual(expectedValue, value);
                    expectedValue++;
                }
            }
        }

        blob.Dispose();
    }


    public unsafe struct TestStruct256bytes
    {
        public BlobArray<int> intArray;
        public fixed int array[61];
        public BlobPtr<int> intPointer;
    }

    
    [Test]
    public void BlobAssetWithRootLargerThanChunkSizeWorks()
    {
        Assert.AreEqual(256, UnsafeUtility.SizeOf<TestStruct256bytes>());
        var builder = new BlobBuilder(Allocator.Temp, 128);
        ref var root = ref builder.ConstructRoot<TestStruct256bytes>();

        var array = builder.Allocate(ref root.intArray, 100);
        for (int i = 0; i < array.Length; ++i)
        {
            array[i] = i;
        }

        builder.Allocate(ref root.intPointer);

        var blob = builder.CreateBlobAssetReference<TestStruct256bytes>(Allocator.Temp);
        builder.Dispose();

        for (int i = 0; i < blob.Value.intArray.Length; ++i)
        {
            if (i != blob.Value.intArray[i])
                Assert.AreEqual(i, blob.Value.intArray[i]);
        }

        blob.Dispose();
    }

    BlobAssetReference<MyData> CreateSharedBlob()
    {
        var blob = ConstructBlobData();

        for (int i = 0; i != 32; i++)
        {
            var entity = m_Manager.CreateEntity();
            m_Manager.AddComponentData(entity, new ComponentWithBlobData() {blobAsset = blob});
        }
        return blob;
    }
    
    NativeArray<Entity> CreateUniqueBlob()
    {
        var entities = new NativeArray<Entity>(32, Allocator.Temp);
        for (int i = 0; i != entities.Length; i++)
        {
            entities[i] = m_Manager.CreateEntity();
            m_Manager.AddComponentData(entities[i], new ComponentWithBlobData() {blobAsset = ConstructBlobData()});
        }

        return entities;
    }
}
#endif

猜你喜欢

转载自www.cnblogs.com/sifenkesi/p/12610079.html