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!
These are high-severity items that are preventing the Latios Framework from releasing new major features or advancing to new versions of packages.
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.
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.
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.
These are issues that are compromising my ability to provide the best possible API and experience for users of the Latios Framework.
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.
SCGI is the latest graphics feature not supported by entities.
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.
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.
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 does not play nice with the job system, or ECS physics. I’d like to see better integration.
These are areas that make it harder for me to develop features for the framework.
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.
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.
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.
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.
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.
It seems Burst really only likes string interpolation for the fixed string types. I’d like the range to be covered a little more.
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.
I want a per-world deterministic chunk ID that I can use to sort chunks deterministically.
These are issues that are preventing me from getting the most performance out of the framework.
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.
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.
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.
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)
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.
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 { }
}
}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.
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.
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.
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.
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.
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
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
These are the big features that the Latios Framework provides some kind of substitute for, and the oversimplified reasons why.
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.
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.
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.
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.
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.