Skip to content

Instantly share code, notes, and snippets.

@Dreaming381
Last active August 10, 2026 06:57
Show Gist options
  • Select an option

  • Save Dreaming381/3aec938455f5e6f393a24e4febabd777 to your computer and use it in GitHub Desktop.

Select an option

Save Dreaming381/3aec938455f5e6f393a24e4febabd777 to your computer and use it in GitHub Desktop.
Continuous Feedback for the ECS Team

Latios Framework Unity ECS Wishlist

Last Updated: 2026-8-9

As the original author and primary developer of the Latios Framework for Unity’s ECS, I regularly run into bugs, inadequate functionalities, and pitfalls within the ECS ecosystem. This has resulted in lots of “hacks” in the framework to patch up problems. This is a living document describing the issues and hacks. My hope is that the Unity ECS team will find this to be a valuable reference to help improve the quality of their packages.

This document does not include all possible features the Latios Framework may eventually implement if no official solution is provided. If you would like to learn about such features, it is best to ask me. I’ve focused this list specifically on things I would like to see. There are a lot more things I could list that I know many users have asked for, and arrive to the Latios Framework due to these missing features or other issues. If you would like to get a list of these things in a specific area, or if you have any questions about anything listed here, feel free to reach out to me!

Ship Stoppers

These are high-severity items that are preventing the Latios Framework from releasing new major features or advancing to new versions of packages.

InstanceID -> EntityID Between Unity 6.3 -> 6.4

The Entities package still relies on InstanceID for Unity 6.3 projects. This divergence causes problems with the framework targeting newer editor versions. Because 6.3 is an LTS, I am currently choosing to only support this version right now.

RayTracingAccelerationStructure API

The RayTracingAccelerationStructure has no APIs that allow setting multiple different meshes and materials nor updating transforms in batch. I have to make a sequence of managed calls, which is way too slow at ECS scale.

Resolution Unknown During Culling

I can’t trivially build an occlusion culling solution without knowing the native resolution of the framebuffer being rendered to. While I can obtain that for the camera, I can’t for shadows.

Major Regression or Compatibility Concerns

These are issues that are compromising my ability to provide the best possible API and experience for users of the Latios Framework.

GPU Resident Drawer Coupling

The Scriptable Render Pipelines currently call directly into GPU Resident Drawer for critical features such as double-pass occlusion culling. GPU Resident Drawer has significant performance design flaws, and I want to replace it. However, shipping an entire replacement for the Scriptable Render Pipeline Core package does not sound like a good idea. Support for Unity’s URP and HDRP in the future is uncertain.

Lack of Graphics Features

SCGI is the latest graphics feature not supported by entities.

New Big Features I Want

These are features that are deep in the backlog of things I may develop for the framework, but I’d really rather these be developed by Unity first.

Support for Unmanaged Lights

Companion Game Object lights are bad for any dynamic entities that are spawned and despawned. It would be awesome if we could feed unmanaged light data into the SRP, since that’s how it uses it internally.

Non-Heightmap Environment

This one is a bit wishful on my part. But I would love some built-in tools for an environment system not tied to heightmaps.

Cinemachine Integration

Cinemachine does not play nice with the job system, or ECS physics. I’d like to see better integration.

Stumbling Points

These are areas that make it harder for me to develop features for the framework.

Numerics as a Separate Package

I’d like to see the Numerics assembly inside Unity Physics be split into a separate package. Otherwise, I’m going to have to copy it if I want to use it, since I have my own physics solution.

Chunk Fragmentation

I’d like structural changes made on entire chunk in-place to first check if there is a destination chunk that can house all of the entities contained in the source chunk, and then move all the entities there. This would solve a lot of fragmentation problems I am constantly having to dodge with cleanup and chunk components.

Burst Inspector Function Navigation

When Burst compiles a job, it often breaks the job up into multiple functions. I want an outline of all these functions that I can click on to jump to them.

ID Management

It is very difficult to enforce unique identifiers to external resources, and correctly handle instantiation deep-copying those external resources and garbage-collecting dead entities. I want to see this area improved.

Runtime Inspectors

I want a better way to make more data visible to the runtime inspectors, such as fields I specify as internal. I also want to be able to override the entire DynamicBuffer drawer, rather than each element.

BlobStrings, UnsafeText, and String Interpolation in Burst

It seems Burst really only likes string interpolation for the fixed string types. I’d like the range to be covered a little more.

SharedStatic Index Acquisition

I want to assign each SharedStatic<int> a unique array index at creation time, so that I can use these as named indexers into some other resource, without having to try and gather all these instances at application startup.

Chunk Determinism

I want a per-world deterministic chunk ID that I can use to sort chunks deterministically.

Performance Pains

These are issues that are preventing me from getting the most performance out of the framework.

BatchRendererGroup Batch Buffer Issue

There is an issue where setting the Batch Buffer in the OnFinishedCulling() callback of BatchRendererGroup causes the screen to flicker. I have reported this here: https://issuetracker.unity3d.com/issues/entities-flicker-when-setting-batchrenderergroup-buffer-inside-onfinishedculling-callback

Claude believes that the batch buffer is being locked to the batches inside ScriptableRenderContext.Cull() prior to the first OnPerformCulling(), so I am now using a different callback, but it isn’t optimal, and does not match the documentation.

Fixed 16 kB Chunk Capacity

While I think the first chunk of each archetype could be the fixed 16 kB layout, if there are archetypes that need more than one chunk, I’d like to see them switch to a design that allows far more entities to be iterated linearly at once, so that we can benefit more from hardware prefetching.

Shadow Casting Light Culling

SRP allows all shadow-casting lights to be culled and processed in batch. Yet the culling callbacks sent back to BatchRendererGroup happen one-by-one. I’d love to perform culling for all lights at once.

Structural Change Performance

I’d like the option to allow the component data moving part to be deferred to a parallel job during large structural changes, and for DynamicBuffer memory freeing operations to also get deferred to some later point off the main thread (unless mimalloc solves this)

Bitwise Operations on Component Enabled Bits

I want to use SIMD (or pairs of 64-bit ops) to process enabled states of components in a chunk, both for reading and writing.

API Gotchas

Here’s some examples of API holes that are a bit inconsistent, or just commonly encountered. Commented out lines are lines that I would expect to compile, but don’t.

Last updated for Unity 6000.7.0a4

using System.Collections.Generic;
using Unity.Burst.Intrinsics;
using Unity.Collections;
using Unity.Entities;
using Unity.Entities.Graphics;
using Unity.Rendering;
using Unity.Transforms;

partial struct ApiProblemsSystem : ISystem
{
    public void OnUpdate(ref SystemState state)
    {
        var list         = new NativeList<int>(state.WorldUpdateAllocator);
        state.Dependency = list.Dispose(state.Dependency);

        var array = CollectionHelper.CreateNativeArray<int>(8, state.WorldUpdateAllocator);
        //state.Dependency = CollectionHelper.Dispose(array, state.Dependency);

        Entity anEntity = Entity.Null;
        foreach ((var transform, var entity) in SystemAPI.Query<LocalTransform>().WithEntityAccess())
        {
            anEntity = entity;
        }

        //foreach (var entity in SystemAPI.Query().WithEntityAccess().WithAll<LocalTransform>())
        {
        }

        // Why do we even have custom SystemHandle APIs for things rather than just expose an Entity property on SystemHandle?
        var dstBuffer = state.EntityManager.GetBuffer<LinkedEntityGroup>(state.SystemHandle);
        //state.EntityManager.GetSharedComponentData<RenderFilterSettings>(state.SystemHandle, default);

        state.EntityManager.CompleteDependencyBeforeRO<LocalTransform>();
        //state.EntityManager.CompleteDependencyBeforeRO(ComponentType.ReadOnly<LocalTransform>());

        var srcBuffer = state.EntityManager.GetBuffer<LinkedEntityGroup>(anEntity);
        dstBuffer.CopyFrom(srcBuffer);
        //dstBuffer.AddRange(srcBuffer);
        dstBuffer.AddRange(srcBuffer.AsNativeArray());  // Safety violation

        var query    = SystemAPI.QueryBuilder().WithAll<MaterialMeshInfo>().Build();
        var entities = query.ToEntityArray(Allocator.Temp);
        var chunks   = query.ToArchetypeChunkArray(Allocator.Temp);
        //var archetypes = query.ToArchetypeArray(Allocator.Temp);

        query.AddChangedVersionFilter(ComponentType.ReadOnly<MaterialMeshInfo>());
        //query.AddChangedVersionFilter<MaterialMeshInfo>();
        query.SetChangedVersionFilter(ComponentType.ReadOnly<MaterialMeshInfo>());
        //query.SetChangedVersionFilter(ComponentType.ReadOnly<MaterialMeshInfo>(), state.GlobalSystemVersion); // Can't customize when to compare changes to
    }

    struct Job : IJobChunk
    {
        public ComponentTypeHandle<EntitiesGraphicsChunkInfo>  chunkComponentHandle;
        public ComponentTypeHandle<MaterialMeshInfo>           componentHandle;
        public BufferTypeHandle<LinkedEntityGroup>             bufferHandle;
        public DynamicComponentTypeHandle                      dynamicHandle;
        public SharedComponentTypeHandle<RenderFilterSettings> sharedHandle;
        public DynamicSharedComponentTypeHandle                dynamicSharedHandle;
        public ComponentLookup<ChunkWorldRenderBounds>         chunkComponentLookup;

        public EntityQueryMask queryMask;

        public unsafe void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
        {
            var bits = chunk.GetEnableableBits(ref componentHandle);
            //chunk.GetEnableableBits(ref bufferHandle);
            //chunk.SetEnableableBits(ref componentHandle, bits);

            chunk.GetChunkComponentDataRW(ref chunkComponentHandle);
            //chunk.GetChunkComponentDataRO(ref chunkComponentHandle);
            chunk.GetChunkComponentData(ref chunkComponentHandle);
            //chunk.GetChunkComponentData(ref chunkComponentLookup);
        }
    }

    static class ExternalUse
    {
        public static void MoreBad()
        {
            var em = World.DefaultGameObjectInjectionWorld.EntityManager;
            em.GetComponentTypeHandle<MaterialMeshInfo>(true);
            //em.GetComponentLookup<MaterialMeshInfo>(true);
            em.GetEntityTypeHandle();
            //em.GetEntityStorageInfoLookup();
            em.GetBufferTypeHandle<LinkedEntityGroup>(true);
            //em.GetBufferLookup<LinkedEntityGroup>(true);
            var                         mask       = TypeManager.GetTypeInfo<MaterialMeshInfo>().BloomFilterMask;
            NativeList<EntityArchetype> archetypes = new NativeList<EntityArchetype>(Allocator.Temp);
            em.GetAllArchetypes(archetypes);
            //bool matches = archetypes[0].BloomFilterMask == mask;

            var entity = em.CreateEntity();
            em.AddComponent<CleanupComponent>(entity);
            em.DestroyEntity(entity);
            var thisIsTrue = em.Exists(entity);
            //var entityIsAlive = em.IsAlive(entity);
            //var entityIsBeingDestroyed = em.IsBeingDestroyed(entity);
            //var isCleanupArchetype = archetypes[0].IsCleanup();
        }

        struct CleanupComponent : IComponentData { }
    }
}

Hacky Solutions I’d Like to Replace

Sometimes, I need to do hacky things to accomplish the desired functionality. While this isn’t a complete list, here are a few areas to look at to see if things can be improved.

Exposed APIs

I use this asmref directory to expose various extra APIs from the Entities package that I need. https://github.com/Dreaming381/Latios-Framework/tree/master/EntitiesExposed

The ILPostProcessing next to it is also hacks into the Entities package for where I needed to add extra hooks.

Please ask what I use each of these for.

Custom Bootstraps for Non-Runtime Worlds and Bakers

I have custom bootstrap types that define other worlds, such as the editor world and baking worlds. I eventually plan to add a bootstrap for subscene post-processing. But it would be really nice if I didn’t have to do crazy hacks to make these things work.

Callback in Player Loop After All Rendering

Somehow, I can’t seem to find a PlayerLoop point that happens after all the rendering including SRP Render Graph execution. I need to use an end-of-frame coroutine on a hidden GameObject to run systems at this point. And then making those systems show up in the Systems window also required a bit of hackery.

Blob Baking

Right now, I have my Smart Blobber feature, which makes blob baking with deduplication safer and easier, while offering parallel baking for fast live editing. However, I’d much rather see an approach that allows blobs to be baked in bakers with the same benefits. This would require two things:

First, it would require the ability to specify separate per-bake and per session hashes for potential blob assets. And second, it would require async awaiting on scheduled jobs so that other bakers can run while jobs are processing a blob.

Interfaces in Baking

Everything in here I would have expected to be provided out-of-the-box. https://github.com/Dreaming381/Latios-Framework/blob/master/Core/Authoring/BakerInterfaceSupport.cs

Decals are a Mess

I had a heck of a time hacking packages in order to make Entity projected decals a thing. Related bug reports were all rejected. I wrote the details here: https://github.com/Dreaming381/Latios-Framework-Documentation/blob/main/Kinemation%20Animation%20and%20Rendering/Decals.md#deep-hackery

Big Feature Problems that Caused Me to Do My Own Thing

These are the big features that the Latios Framework provides some kind of substitute for, and the oversimplified reasons why.

Code Gen

IAspect and ENTITY_STORE_V1 removal introduced significant problems. I don’t mind those going away, but I needed the following to replace them.

I wanted to define custom type handles that SystemAPI can populate for me. I wanted to define custom types that can be iterated in idiomatic foreach and IJobEntity. I wanted to extract the EntityQuery auto-created by an IJobEntity definition. I wished the IJobEntityChunkBeginEnd interface was just part of IJobEntity and were default interface methods. And I wanted a method I can call that will populate all the type handles and indices arrays of an IJobEntity so that I can pass it into a generic method constrained by IJobChunk to schedule the job in a custom way.

I waited long enough, so with the help of LLMs I did it myself.

Transforms

I wanted animation-friendly squash-and-stretch hierarchies. And I also wanted much better performance. Unity Transforms did catch up performance-wise in a lot of ways since I started this endeavor. However, it still has this weird LocalToWorld and LocalTransform relationship which consume a lot of chunk space and can get out of sync.

Entities Graphics

I needed much better performance for skinned mesh rendering. Turns out, this path led me to discover optimizations for all projects in general. And after that, I went on a quest to support LODs for dense worlds as well. The more I and others pushed big worlds full of entities, the more I kept pushing my solution to meet the demand. The Entities Graphics package has been mostly stagnant in regards to features, getting the occasional bugfix every few DOTS releases. And GPU Resident Drawer has been repeating all the same mistakes (and even missing out on some optimizations). Therefore, my solution has become significantly faster than both.

Physics

I needed much more direct spatial query capabilities. And nowadays, I need more customizability over the simulation. Also, I needed hierarchies.

This unfortunately meant that I don’t get to directly use the character controller and vehicles packages either. But I’m not too disappointed by that, as neither meets my own needs.

NetCode

I needed much more control over the interpolation of synchronized objects. And I needed better hierarchy support. I just could not achieve a good motion feel in NetCode for motions that felt great when not networked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment