docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Create a blob asset

    To create a blob asset, perform the following steps:

    1. Create a BlobBuilder. This needs to allocate some memory internally.
    2. Use BlobBuilder.ConstructRoot to construct the root of the blob asset.
    3. Fill the structure with your data.
    4. Use BlobBuilder.CreateBlobAssetReference to create a BlobAssetReference. This copies the blob asset to its final location.
    5. Dispose the BlobBuilder.

    The following example stores a struct with primitive members as a blob asset:

    struct MarketData
    {
        public float PriceOranges;
        public float PriceApples;
    }
    
    BlobAssetReference<MarketData> CreateMarketData()
    {
        // Create a new builder that will use temporary memory to construct the blob asset
        var builder = new BlobBuilder(Allocator.Temp);
    
        // Construct the root object for the blob asset. Notice the use of `ref`.
        ref MarketData marketData = ref builder.ConstructRoot<MarketData>();
    
        // Now fill the constructed root with the data:
        // Apples compare to Oranges in the universally accepted ratio of 2 : 1 .
        marketData.PriceApples = 2f;
        marketData.PriceOranges = 4f;
    
        // Now copy the data from the builder into its final place, which will
        // use the persistent allocator
        var result = builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);
    
        // Make sure to dispose the builder itself so all internal memory is disposed.
        builder.Dispose();
        return result;
    }
    

    The BlobBuilder constructs the data stored in the blob asset, makes sure that all internal references are stored as offsets, and then copies the finished blob asset into a single allocation referenced by the returned BlobAssetReference<T>.

    Arrays in blob assets

    You must use the BlobArray type to create an array within a blob asset. This is because arrays are implemented with relative offsets internally. The following is an example of how to allocate an array of blob data and fill it:

    struct Hobby
    {
        public float Excitement;
        public int NumOrangesRequired;
    }
    
    struct HobbyPool
    {
        public BlobArray<Hobby> Hobbies;
    }
    
    BlobAssetReference<HobbyPool> CreateHobbyPool()
    {
        var builder = new BlobBuilder(Allocator.Temp);
        ref HobbyPool hobbyPool = ref builder.ConstructRoot<HobbyPool>();
    
        // Allocate enough room for two hobbies in the pool. Use the returned BlobBuilderArray
        // to fill in the data.
        const int numHobbies = 2;
        BlobBuilderArray<Hobby> arrayBuilder = builder.Allocate(
            ref hobbyPool.Hobbies,
            numHobbies
        );
    
        // Initialize the hobbies.
    
        // An exciting hobby that consumes a lot of oranges.
        arrayBuilder[0] = new Hobby
        {
            Excitement = 1,
            NumOrangesRequired = 7
        };
    
        // A less exciting hobby that conserves oranges.
        arrayBuilder[1] = new Hobby
        {
            Excitement = 0.2f,
            NumOrangesRequired = 2
        };
    
        var result = builder.CreateBlobAssetReference<HobbyPool>(Allocator.Persistent);
        builder.Dispose();
        return result;
    }
    

    Strings in blob assets

    You must use the BlobString type to create a string within a blob asset. The following is an example of a string allocated with the BlobBuilder API.

    struct CharacterSetup
    {
        public float Loveliness;
        public BlobString Name;
    }
    
    BlobAssetReference<CharacterSetup> CreateCharacterSetup(string name)
    {
        var builder = new BlobBuilder(Allocator.Temp);
        ref CharacterSetup character = ref builder.ConstructRoot<CharacterSetup>();
    
        character.Loveliness = 9001; // it's just a very lovely character
    
        // Create a new BlobString and set it to the given name.
        builder.AllocateString(ref character.Name, name);
    
        var result = builder.CreateBlobAssetReference<CharacterSetup>(Allocator.Persistent);
        builder.Dispose();
        return result;
    }
    

    Internal pointers

    To manually set an internal pointer, use the BlobPtr<T> type.

    struct FriendList
    {
        public BlobPtr<BlobString> BestFriend;
        public BlobArray<BlobString> Friends;
    }
    
    BlobAssetReference<FriendList> CreateFriendList()
    {
        var builder = new BlobBuilder(Allocator.Temp);
        ref FriendList friendList = ref builder.ConstructRoot<FriendList>();
    
        const int numFriends = 3;
        var arrayBuilder = builder.Allocate(ref friendList.Friends, numFriends);
        builder.AllocateString(ref arrayBuilder[0], "Alice");
        builder.AllocateString(ref arrayBuilder[1], "Bob");
        builder.AllocateString(ref arrayBuilder[2], "Joachim");
    
        // Set the best friend pointer to point to the second array element.
        builder.SetPointer(ref friendList.BestFriend, ref arrayBuilder[2]);
    
        var result = builder.CreateBlobAssetReference<FriendList>(Allocator.Persistent);
        builder.Dispose();
        return result;
    }
    

    Accessing blob assets on a component

    Once you've made a BlobAssetReference<T> to a blob asset, you can store this reference on component and access it. You must access all parts of a blob asset that contain internal pointers by reference.

    struct Hobbies : IComponentData
    {
        public BlobAssetReference<HobbyPool> Blob;
    }
    
    float GetExcitingHobby(ref Hobbies component, int numOranges)
    {
        // Get a reference to the pool of available hobbies. Note that it needs to be passed by
        // reference, because otherwise the internal reference in the BlobArray would be invalid.
        ref HobbyPool pool = ref component.Blob.Value;
    
        // Find the most exciting hobby we can participate in with our current number of oranges.
        float mostExcitingHobby = 0;
        for (int i = 0; i < pool.Hobbies.Length; i++)
        {
            // This is safe to use without a reference, because the Hobby struct does not
            // contain internal references.
            var hobby = pool.Hobbies[i];
            if (hobby.NumOrangesRequired > numOranges)
                continue;
            if (hobby.Excitement >= mostExcitingHobby)
                mostExcitingHobby = hobby.Excitement;
        }
    
        return mostExcitingHobby;
    }
    

    Dispose a blob asset reference

    Any blob assets that you allocate at runtime with BlobBuilder.CreateBlobAssetReference need to be disposed manually.

    However, you don't need to manually dispose of any blob assets that were loaded as part of an entity scene loaded from disk. All of these blob assets are reference counted and automatically released once no component references them anymore.

    public partial struct BlobAssetInRuntimeSystem : ISystem
    {
        private BlobAssetReference<MarketData> _blobAssetReference;
    
        public void OnCreate(ref SystemState state)
        {
            using (var builder = new BlobBuilder(Allocator.Temp))
            {
                ref MarketData marketData = ref builder.ConstructRoot<MarketData>();
                marketData.PriceApples = 2f;
                marketData.PriceOranges = 4f;
                _blobAssetReference =
                    builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);
            }
        }
    
        public void OnDestroy(ref SystemState state)
        {
            // Calling Dispose on the BlobAssetReference will destroy the referenced
            // BlobAsset and free its memory
            _blobAssetReference.Dispose();
        }
    }
    

    Debugging blob asset contents

    Blob assets use relative offsets to implement internal references. This means that copying a BlobString struct, or any other type with these internal references, copies the relative offset contained, but not what it's pointing to. The result of this is an unusable BlobString that represents a random string of characters. While this is easy to avoid in your own code, debugging utilities often do exactly that. Therefore, the contents of a BlobString aren't displayed correctly in a debugger.

    However, there is support for displaying the values of a BlobAssetReference<T> and all its contents. If you want to look up the contents of a BlobString, navigate to the containing BlobAssetReference<T> and start debugging from there.

    Blob assets in baking

    You can use bakers and baking systems to create blob assets offline and have them be available in runtime.

    To handle blob assets, the BlobAssetStore is used. The BlobAssetStore keeps internal ref counting and ensures that blob assets are disposed if nothing references it anymore. The Bakers internally have access to a BlobAssetStore, but to create blob assets in a Baking System, you need to retrieve the BlobAssetStore from the Baking System.

    Register a blob asset with a baker

    Because bakers are deterministic and incremental, you need to follow some extra steps to use blob assets in baking. As well as creating a BlobAssetReference with the BlobBuilder, you need to register the BlobAsset to the baker.

    To register the blob asset to the baker, you call AddBlobAsset with the BlobAssetReference:

    struct MarketData
    {
        public float PriceOranges;
        public float PriceApples;
    }
    
    struct MarketDataComponent : IComponentData
    {
        public BlobAssetReference<MarketData> Blob;
    }
    
    public class MarketDataAuthoring : MonoBehaviour
    {
        public float PriceOranges;
        public float PriceApples;
    }
    
    class MarketDataBaker : Baker<MarketDataAuthoring>
    {
        public override void Bake(MarketDataAuthoring authoring)
        {
            // Create a new builder that will use temporary memory to construct the blob asset
            var builder = new BlobBuilder(Allocator.Temp);
    
            // Construct the root object for the blob asset. Notice the use of `ref`.
            ref MarketData marketData = ref builder.ConstructRoot<MarketData>();
    
            // Now fill the constructed root with the data:
            // Apples compare to Oranges in the universally accepted ratio of 2 : 1 .
            marketData.PriceApples = authoring.PriceApples;
            marketData.PriceOranges = authoring.PriceOranges;
    
            // Now copy the data from the builder into its final place, which will
            // use the persistent allocator
            var blobReference =
                builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);
    
            // Make sure to dispose the builder itself so all internal memory is disposed.
            builder.Dispose();
    
            // Register the Blob Asset to the Baker for de-duplication and reverting.
            AddBlobAsset<MarketData>(ref blobReference, out var hash);
            var entity = GetEntity(TransformUsageFlags.None);
            AddComponent(entity, new MarketDataComponent() {Blob = blobReference});
        }
    }
    
    Important

    If you don't register a blob asset to the baker, the ref counting doesn't update and the blob asset could be de-allocated unexpectedly.

    The baker uses BlobAssetStore to de-duplicate and refcount the blob assets. It also decreases the ref counting of the associated blob assets to revert the blob assets when the baker is re-run. Without this step, the bakers would break incremental behaviour. Because of this, the BlobAssetStore isn't available directly from the baker, and only through baker methods.

    De-duplication with custom hashes

    The previous example let the baker handle all de-duplication, but that means you have to create the blob asset first before the baker de-duplicates and disposes the extra blob asset. In some cases you might want to de-duplicate before the blob asset is created in the baker.

    To do this, you can use a custom hash instead of letting the baker generate one. If multiple bakers either have access to, or generate the same hash for the same blob assets, you can use this hash to de-duplicate before generating a blob asset. Use TryGetBlobAssetReference to check if the custom hash is already registered to the baker:

    class MarketDataCustomHashBaker : Baker<MarketDataAuthoring>
    {
        public override void Bake(MarketDataAuthoring authoring)
        {
            var customHash = new Unity.Entities.Hash128(
                (uint) authoring.PriceOranges.GetHashCode(),
                (uint) authoring.PriceApples.GetHashCode(), 0, 0);
    
            if (!TryGetBlobAssetReference(customHash,
                    out BlobAssetReference<MarketData> blobReference))
            {
                // Create a new builder that will use temporary memory to construct the blob asset
                var builder = new BlobBuilder(Allocator.Temp);
    
                // Construct the root object for the blob asset. Notice the use of `ref`.
                ref MarketData marketData = ref builder.ConstructRoot<MarketData>();
    
                // Now fill the constructed root with the data:
                // Apples compare to Oranges in the universally accepted ratio of 2 : 1 .
                marketData.PriceApples = authoring.PriceApples;
                marketData.PriceOranges = authoring.PriceOranges;
    
                // Now copy the data from the builder into its final place, which will
                // use the persistent allocator
                blobReference =
                    builder.CreateBlobAssetReference<MarketData>(Allocator.Persistent);
    
                // Make sure to dispose the builder itself so all internal memory is disposed.
                builder.Dispose();
    
                // Register the Blob Asset to the Baker for de-duplication and reverting.
                AddBlobAssetWithCustomHash<MarketData>(ref blobReference, customHash);
            }
    
            var entity = GetEntity(TransformUsageFlags.None);
            AddComponent(entity, new MarketDataComponent() {Blob = blobReference});
        }
    }
    
    In This Article
    • Create a blob asset
    • Arrays in blob assets
    • Strings in blob assets
    • Internal pointers
    • Accessing blob assets on a component
    • Dispose a blob asset reference
    • Debugging blob asset contents
    • Blob assets in baking
    • Register a blob asset with a baker
      • De-duplication with custom hashes
    Back to top
    Copyright © 2024 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)