#archived-dots
1 messages Β· Page 288 of 1
im aware of those methods
not that I think this is a great implementation for the problem π
i still wonder why they save it in ecs
what a wasted chunk
wish they actually read from the ecs
so i could manipulate the component directly ^_^'
they don't actually read current time from the component
once a DB has been moved outside a chunk it never gets reintegrated into the chunk, right?
you can reintegrate it
with TrimExcess()
/// Removes any excess capacity in the buffer.
/// </summary>
/// <remarks>Sets the buffer capacity to the current length.
/// If the buffer memory size changes, the current contents
/// of the buffer are copied to a new block of memory and the
/// old memory is freed. If the buffer now fits in the space in the
/// chunk reserved with <see cref="InternalBufferCapacityAttribute"/>,
/// then the buffer contents are moved to the chunk.</remarks>```
thanks
hm, m_InternalCapacity is private. how would i get that? :/
i can't remember the other method
TypeManager.GetTypeInfo(typeIndex).BufferCapacity;
where would i get the typeIndex best? it's also private in the handle
oh it's only private in BufferFromEntity. they made it difficult -.-
cool, i have a method for BufferTypeHandle now but not for BufferFromEntity
TypeManager.GetTypeIndex<T>()
i just wanted to post that! haha, thanks anyway
now i'm doing the proper way and just use a constant
Hold on. Let me get this straight. Is this basically how dots works?
Take an instance of an object.
Add it to a list with only a specific set of variables like position and rotation.
And create instances of that, which its only unique data to itself are the predescribed variables?
there are several packages in DOTS
each works differently
and all together it's smth totally different from classic unity
Yeah, figured that was a thing
hmmm, I am trying to figure out how I can simplify user input
I have many different screens which are responsible for different controls
Space controls, Map controls, UI controls (which can be different on same screens)
I wanted to try assigning mouse events to UIElement's VisualElement
but it lacks way too many features from New Input System
Is there a way to implement smth like RequireNoSingleton<T>()?
Basically I need system to run only if certain query doesn't exist
Update would still be called but there you can check for HasSingleton
Or could just have a singleton that signals the opposite condition I guess
yeah, the query could be used for that
the problem is that it's all somewhat should be independent from each other
I have normal game overview and Map game overview, in which Action maps should switch (normal gets disabled, map gets enabled)
And I also don't want to be attached to same entity
OnUpdate() { if(!myQuery.IsEmpty) {return;}
internal EntityQuery CreateEntityQuery(ComponentType* requiredComponents, int count)
internally Unity has this
hehe
meaning you could specify count for query
but it's internal
What is a good way to make update orders easier to see in code?
All these [UpdateBefore] and [UpdateAfter] attributes are really confusing π
Is it normal to have such bad physics world performance in editor mode? This is with 5000 physics bodies.
in code? I don't think there's any way to see it at all
generally systems should not depend on other at all
instead just know their own position relative to other
hmm
I wonder if it's a good idea to have a special entity
for all singletons
in game kek
my previous ecs project i created a bunch of custom groups like this:
then in my systems i would use these groups and their ecb systems like this:
i just found it a bit more organized, so i could then go back to my custom groups file and change orders and ecb dependencies that way
rather than going into each individual system and doing edits
hmmm, sorting NativeList
ooh, there's actually SortJobs
welp nvm
seems like they aren't ready yet
kek
Just use sort from within a job?
yeah, that's one way
Also what do you mean by not ready yet
latest forum posts say that it's just not implemented yet
from devs
and collections manual has 0 mentions of it
Have you tried it?
I couldn't even find info on how to use it
usually when I look it up, I just find your post on forum with examples
xD
i haven't used the job in like 2 years
but i'm pretty sure it worked back then
pretty sure you just call .SortJob()
where T : unmanaged
where U : IComparer<T>```
btw, in order to convert array of smth to array of <smth4> you simple reinterpret to that type?
I am thinking about implementing smid a bit, kek
yes
but your size must be right
you can't convert an array of ints of length 5
into a int4 array
why not?
because your last 3 ints would be someone elses memory
and you would be writing / reading random memory
oooh, and we don't want to touch that
I guess that would require implementing 2 algorithms
1 for x4
other for whatever is left per 1
which is what burst does btw
{
var maxValue4 = new int4(int.MinValue);
var numSamples4 = length >> 2;
for (var iValue = 0; iValue < numSamples4; iValue++)
{
var value4 = ((int4*)values)[iValue];
maxValue4 = math.max(maxValue4, value4);
}
var maxValue = math.cmax(maxValue4);
for (var iValue = numSamples4 << 2; iValue < length; iValue++)
{
maxValue = math.max(maxValue, values[iValue]);
}
return maxValue;
}
an example of doing it by hand
sadge
hmmm
I wonder what would be a better pattern
- if I want some global action I simply create entity with trigger event component.
em.CreateEntity(ComponentType.ReadOnly<TriggerUpdate>());
Once event is done, entity is destroyed. - I keep global world reference to some entity with systemState component and instead just add/remove components to it
while that's technically true, burst builds a second scalar for loop for the remaining items when auto vectorizing is detected
i said this!
which is what burst does btw
you can't reinterpret yourself to float4/int4
which is what he was asking
mh, i see
wouldn't for (var iValue = 0; iValue < numSamples4; iValue++) be for (var iValue = 0; iValue < numSamples4; iValue+=4)?
i mean, doesn't work with the divided by 4 numSamples but the index isn't offset by 4
ah nvm π
if i have int4*
abcd|efgh|ijkl|mnop|q
index 0 is a
index 1 is e
index 4 is q
you'd be skipping a lot ^_^'
the (int4*) cast makes sure of that
i never used [AssumeRange(0, int.MaxValue)], does that actually help burst is such a case?
yes - it can
if you notice the collections package uses this extensively
{
get
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
#endif
return CollectionHelper.AssumePositive(m_ListData->Length);
}
set
{
m_ListData->Resize(value, NativeArrayOptions.ClearMemory);
}
}```
internal static int AssumePositive(int value)
{
return value;
}```
lol, never inspected this one. what will even happen when value is -1?
is this like an assert?
assumes do no checks
you will get very unexpected behaviour
I like to use a lot of asserts in my code with Hint.Assume
because Hint.Assume can just magically make your code a lot faster under weird circumstances
but it's so unsafe
so i have a little wrapper for it
public static class Check
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Assume(bool assumption)
{
IsTrue(assumption);
Hint.Assume(assumption);
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
public static void IsTrue(bool condition)
{
Debug.Assert(condition);
}
i remember you meantioning this once
this way i have actual asserts when collection checks are on
this way I get code safety and random free speedups
what i find weird, wouldn't every static analyzer find that out anyway when i have a for loop starting with 0 and an integer?
if there's nothing stopping you passing in a negative value it has to defensively code for it
ah i get it, okay π
swear the had a better example at one point
but yeah constrained ranges would be even better
they only work on parameters for methods, right?
^
you can mark the return type which is the nice way of reusing it
only works on scalar-integer values
static uint WithConstrainedRange([AssumeRange(0, 26)] int x)
{
return (uint)x / 2u;
}```
btw this is really a next level over optimization thing
i rarely use it
the effort (high) vs benefit (extremely low) not really worth it
i mainly want to see if burst makes a different assembly and what's different about it
there is a simple example of what it can do
{
// The compiler will always replace this with the constant false!
return na.Length < 0;
}```
yeah, that's also the part of micro-optimisation i've never dealt with yet.
something like hint assume also doesn't do anything like 99% of the time
but i had 1 really complex algorithm that it sped up 10%+ (voxel generation)
and if I'm using asserts anyway I may as well throw it in
sure
is it any different to allocating temp memory?
it is
hm, i need to start utilising it then π
this is why FixedList etc
exist
because they effectively stackalloc
but give you list/etc functionality
what the ... i need that!
man, there's always something new i uncover from you π
just remember you only get 4MB (i think it's 4MB x64, 1MB x86) on the stack per thread
yeah, funky. it's not that FixedList is new to me. i just never made the connection that it's stack based
which makes sense when looking at the implementation
more than enough! π
i can replace some temp allocated native arrays
but yeah, definitely used FixedList over Temp allocations if you can
which get allocated, god knows where
damn, i asked that a few weeks ago but was probably phrasing it confusingly
anyway, thanks! π
also if you're using stack
[SkipLocalsInit]
don't forget you can use this
to stop stack allocations being initialized to 0
but be careful otherwise you will have some really unexpected and hard to track down bugs
everyone assumes that new stack allocations are zeroed
And note the call to memset is gone - because the developer has promised the compiler that it is fine. Note that this is a power user feature for experienced developers - developers that are certain they won't run into undefined behaviour bugs as a result of this change.
i have just read that. yeah, i know what to do. that one can be REALLY nasty though. this only affects stackallocs then and not other structs, right?
but you're a power user
no no
everything (pretty sure)
uhh boy
then better use a method for that then. that shit is dangerous! haha
also funny, i required that at some point. i think my ParallelListHashMap
yeah, new array and vales are set to -1 with memset
then i can skip the init to 0 hehe
oh wait nvm. that'a for uninitialised nativearray
would this not be done with malloc?
this ref SpellBlobRoot spellData,
//ref ArchetypeChunk chunk,
//int indexInChunk,
in SpellOwner spellOwner,
byte teamId,
in TargetInfo targetInfo,
in SpellStats spellStats,
bool fromMainEffect,
AttackResult forceSpellResultHit = AttackResult.None)
{
var createData = new CreateBasicSpellDataComplex
{
spellOwner = spellOwner,
sourceTeamId = teamId,
forceSpellResultHit = forceSpellResultHit,
targetInfo = targetInfo,
//spellBlob = spellBlob,
//chunk = UnsafeUtility.AddressOf(ref chunk),
//indexInChunk = indexInChunk,
spellBlob = UnsafeUtility.AddressOf(ref spellData),
spellStats = spellStats,
fromMainEffect = fromMainEffect
};
return createData;
}``` good candidate for [SkipLocalsInit] right?
would this even make a difference? hm
well, i can't find any memset in the burst assembly for it.
i would not really worry about this except for stackalloc
oh FixedList has no AsNativeArray, only To with a memcpy
that doesn't play nice with AddRange to a buffer
huh, i wonder why it doesn't. seems possible to me
i am shocked you don't have a ptr extension for AddRange already π
where T : unmanaged
{
CheckWriteAccess(buffer);
int elemSize = UnsafeUtility.SizeOf<T>();
int oldLength = buffer.Length;
buffer.ResizeUninitialized(oldLength + length);
var basePtr = (byte*)buffer.GetUnsafePtr();
UnsafeUtility.MemCpy(basePtr + ((long)oldLength * elemSize), ptr, (long)elemSize * length);
}```
i wondered where's the catch
just a minor question but why do you have access to CheckWriteAccess(buffer)? have you copied the method too?
private static void CheckWriteAccess<T>(DynamicBuffer<T> buffer)
where T : unmanaged
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndThrow(buffer.m_Safety0);
AtomicSafetyHandle.CheckWriteAndThrow(buffer.m_Safety1);
#endif
}```
oh thats just a standalone method in my DynamicBufferExtensions class
just tested FixedListX vs NativeList. seems on par in my case
what made the most difference in my job is moving out the NativeArray temp alloc and only allocate 1 list per thread. halfed the time π
temp allocs are really not that great in loops.
writing such patterns is a little weird. it's okay but eh
feels like it could be better
I've generated an entity's mesh from scratch. The mesh seems to be complete- but is not rendering in the scene. Anyone thoughts on why this would be? Thank you!
yeah theres a lot less benefit if you do this
i wanted to say check translation but that also seems okay. hybrid renderer is installed?
Translation seems to be correct- and yes it is. Is there something i have to do IN CODE to apply it in some way?
Not much documentation on the matter
you're missing a dozen components
Like?
no idea, you shouldn't build mesh entitites from hand
I'm missing a dozen components but you have 'no idea' what? π§
It's a voxel game. I have to generate them from scratch
i have a voxel engine, my meshes are all authored
i just author a prefab then instantiate + set mesh/bounds
you can use RenderMeshUtility
to add the required components if you insist on doing it at runtime
excluding the scene components this is the type of stuff you need
using stackalloc to replace these makes 0 difference
must be bottlenecked somewhere else
where did you learn or find the help to get info like this on what's required to render an entity mesh? Thanks.
right, and then it just added those. I see.
I just create an entity prefab with my voxel material/etc and just instantiate that and set the mesh when generated
gotcha. I mean, I do PLAN on making it copy themselves after one immediately exists so I image it's process would be similar.
but thanks for the help.
Hi, I was stopping by and wanted to know if this would be the place to ask about the progress of DOTS?
sure, but we also get our info from the forum π
since you said please π https://forum.unity.com/threads/dots-development-status-and-next-milestones-june-2022.1291766/
read the stickies in this subforum
thank you
@rotund token any idea how I can get the BufferHeader* from a DB. my idea was to cast it but that's not going well
var b = (DynamicBufferExposed<T>*)UnsafeUtility.AddressOf(ref a);```
ah! thanks
where T : unmanaged
{
var tmp = (DynamicBufferExposed<T>*)UnsafeUtility.AddressOf(ref buffer);
return (BufferHeaderExposed*) tmp->m_Buffer;
}
[StructLayout(LayoutKind.Explicit)]
[NoAlias]
public unsafe struct BufferHeaderExposed
{
[NoAlias]
[FieldOffset(0)] public byte* Pointer;
[FieldOffset(8)] public int Length;
[FieldOffset(12)] public int Capacity;
public static byte* GetElementPointer(BufferHeaderExposed* header)
{
if (header->Pointer != null)
return header->Pointer;
return (byte*)(header + 1);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct DynamicBufferExposed<T>
{
[NativeDisableUnsafePtrRestriction] [NoAlias]
internal BufferHeader* m_Buffer;
// Stores original internal capacity of the buffer header, so heap excess can be removed entirely when trimming.
private int m_InternalCapacity;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
internal AtomicSafetyHandle m_Safety0;
internal AtomicSafetyHandle m_Safety1;
internal int m_SafetyReadOnlyCount;
internal int m_SafetyReadWriteCount;
[MarshalAs(UnmanagedType.U1)] internal bool m_IsReadOnly;
[MarshalAs(UnmanagedType.U1)] internal bool m_useMemoryInitPattern;
internal byte m_memoryInitPattern;
#endif
}```
var tmp = UnsafeUtility.As<DynamicBuffer<T>, DynamicBufferExposed<T>>(ref buffer)```
thats better imo
yep, gonna use that
now I have a safe way to save the ptr without relinking. basePtr + index is enough
it was such a mess with buffers being moved
i see no other way
now I don't have direct ptr access to elements but it's good enough
noice, this BufferHeaderExposed madness works flawless π
as I am using the BufferHeader now I also had the same problem as you with bumping the version. had to save the chunk too
Shit. My editor keeps bloating up memory until something on the pc crashes at random. Profiler didnβt tell me anything useful. Any tips on how to track down this leak? I need to know if it was me, or if itβs a bug. Please help.
Try the memory profiler https://docs.unity3d.com/Packages/com.unity.memoryprofiler@latest/
do you have leak detection enabled?
Yes systemgroups definetly make organizing things a little bit easier to reason with
Unfortunately that's not always possible for me
You can make groups that are sorted then
I personally have groups like this
They are either before ecb or after
Usually I use them for systems that aren't supposed to be scheduled
But instead just do mainthread stuff
It's like my own personal ECB
Hehe
I actually just came up with an interesting option to make my reactice systems faster by merging them
Allthough not sure if it's worth it
How would you handle sth like a toggle group with ecs? Hacing each toggle have its on/off state but having a group which handles that only one or n toggles can be active at once?
I could group them via SharedComponents I guess. But that would potentially create a lot of mostly empty chunks
what's the actual use case?
it depends on how many groups you'd have for shared comps being viable
somethign that would also work is a hierarchy. have the parent group as entity and the other entities as child. disabling the group, disables the childs. it's just not very performant when this enabling/disabling occurs often and there are a lot of entities
^
The actual use case I have is leg IK. An entity can have multiple legs. But only one leg should lift of the ground at a time. But there are potentially many entities that could have legs.
So the legs need to be grouped somehow and there needs to either be a system that handles that only one leg of a group can move at one time
I'd personally just store all leg entities on hip or smth in dynamic buffer
and then process hip
instead of leg parts
where all leg parts would be accessed through random access
i basically have a 'planted' bool on my ik chain system, if it's not planted it runs the ik solver code
similar to what Issue suggested, the ik system loops over all of the character 'root' entities, which have an IK component with various parameters and also a buffer holding a reference to the bone and joint entities for all the ik chains on the character
i pass in a few native containers with all of the bone ltw's and the ik system reads from that to do the solver code and writes the results using ecb back to the bones
Thanks for your input @rustic rain and @devout prairie. I think I might be going with the dynamic leg buffer but maybe just lock/unlock the legs from the leg syncing system
Have this strange problem with vs 2022 : whenever i start vs community 2022(unity 2021.3.6) in console i get this warnings : Internal: deleting an allocation that is older than its permitted lifetime of 4 frames (age = 5)
UnityEngine.StackTraceUtility:ExtractStackTrace ()
Unity.Collections.NativeArrayDispose:Dispose ()
Unity.Collections.NativeArrayDisposeJob:Execute ()
Unity.Jobs.IJobExtensions/JobStruct`1<Unity.Collections.NativeArrayDisposeJob>:Execute (Unity.Collections.NativeArrayDisposeJob&,intptr,intptr,Unity.Jobs.LowLevel.Unsafe.JobRanges&,int)
i do not start the game at all - just try to edit files on vs 2022 and then these warning are flooding my console and cpu core activity is almost all the time at 100%. Changing api compatibility level to .NET standard 2.1 or .NET framework doesn't help. Also whenever i install new Unity Editor VS 2022 does not recognize unity libraries and the when i change api compatibility level this problem is solved but still i get those warnings above. I use latest DOTS v0.51 - can this be fixed somehow?
a) is that the full stack?
b) are you using netcode?
c) have you turned on full stack trace?
a) I also have this warning and nothing else : Internal: JobTempAlloc has allocations that are more than 4 frames old - this is not allowed and likely a leak b) no i do not use netcode c) hmm i think i did i'll check that - I created forum thread with full stack trace here : https://forum.unity.com/threads/leak-when-using-vs-2022.1310399/
do you have [ExecuteInEditMode] somwhere?
you mean as an attribute of an system - no i think i do not have that
[AlwaysUpdate]
I think this is the one
i dont have that attribute also nowhere in my code
possibly a package versions issue? have you tested using a different version of VS and not had this issue?
i didn't try that(have only vs 2022 installed and use latest packages for dots) - also must add that i have a bigger project which doesn't suffer from the problem explained above which is very strange( i use the same packages in both projects) - but also on that project when ever i install new Unity version VS 2022 doesn't recognize unity libraries but when i change api compatibility levels everything works fine - very strange
Since the project is not too big i will later this day try to create new and port code and assets and will report here if problem is fixed somehow - thx for your contribution in discussion to all
Thx. Iβll look into it today.
Under Jobs menu right? If so, then yeah.
oh god, I am so confused in game managed
I tried to implement ECS way
but I soon realised I can't open UI that way
brrrrruh
what?
I have world overview, when user can look around world.
And I have map overview, where user sees map.
Thing is, they have different keybinds
meaning if you are currently in world overview
you obviously shouldn't trigger anything that is related to map
and vice versa
I am trying to implement it with trigger entities
with reactive system that requires singleton OpenMap to update
and then I enable/disable action maps in OnStart and OnStop
but here comes a problem, where removing that singleton results in closing map aaaand...
you potentially don't know what UI is supposed to be open during that moment
which makes whatever system I tried to implement somewhat garbage
New input system has easy config of multiple map inputs you can sswitch
+1
wha?
Wdym, how you suggest to use that?
I have it this way
General should work always
Game general should only work when it's loaded
Space should only work when game is loaded and map is not opened
Map should only work when game is loaded and map is opened
Player Space/Map same as previous, but also both require Player entity
kind of like this
and I'm so confused how to implement it all
yeah. just enable / disable on demand
I made a super huge method of if structures
and I quickly realised
it's not the way I want to implement it
I couldn't figure how I implemented it the moment I wrote it xD
that would be somewhat simple if I had a way to require query with 0 entities to run
but that's not an option
or at least, implementation is hidden by internal protection
and probably has unknown consequences
Job.WithCode()
why tho?
how else would you create a reaction on closing map?
i'd just have an entity with bools for every control scheme and update this entity, then only run my query when ChunkHasChanged() and apply the state to the control schemes.
i can't say that's exactly how i would do it, but that was the first immediate though
I have a feeling that means my jobs are completed as soon as they are scheduled
it just means you have overhead in your systems
i very much doubt that main thread cost is (primarily) scheduling
but it literally is
show me huggerAI
people always blame scheduling without ever having profiled it and it's never scheduling -_-
job handle combining and data setup is always slower
there's so many things inn here
if (targetCount == 0)
{
return;
}
_hashMap.Clear();
if (_hashMap.Capacity < targetCount)
{
_hashMap.Capacity = targetCount;
}```
you think CalculateEntityCount is free?
is this 0.51?
yeah
_hashMap.Clear();
can also be expensive if you have a large hash map
{
UnsafeUtility.MemSet(data->buckets, 0xff, (data->bucketCapacityMask + 1) * 4);
UnsafeUtility.MemSet(data->next, 0xff, (data->keyCapacity) * 4);
for (int tls = 0; tls < JobsUtility.MaxJobThreadCount; ++tls)
{
data->firstFreeTLS[tls * UnsafeParallelHashMapData.IntsPerCacheLine] = -1;
}
data->allocatedIndexLength = 0;
}```
i always run clear in a job
anyway your systems are only like 0.05ms each?
apart from this minor main thread overhead if you want to reduce this you either need to
combine systems or switch to ISystem
ISystem would mean that everything will be bursted?
yes
i don't really use it because reasons - i'm trying to write my libraries archetype change free so i only use it for destroy and happy to have 1 systembase for that
and gets ISystem from it?
interesting
but yeah, use at own risk i only tested it enough to ensure it worked - 1.0 has native support for this
Entities 1.0?
yes
(key, value)
it's stored in key, value pairs
it's not 1 key 1234 values
its 1234 of the same key, 1234 values
seems kinda wasteful no?
huh, turns out you can do so many stuff in jobs
var map = _hashMap;
var targetQuery = _targetQuery;
Job.WithCode(() =>
{
int targetCount = targetQuery.CalculateEntityCount();
map.Clear();
if (map.Capacity < targetCount)
{
map.Capacity = targetCount;
}
})
.Run();
Turned starting part into this, to make it bursted
in fact I can even Schedule it
bruh
[BurstCompile]
private struct MapJob : IJob
{
[NativeDisableUnsafePtrRestriction] public NativeParallelMultiHashMap<int, TargetData> map;
[NativeDisableUnsafePtrRestriction] public EntityQuery targetQuery;
public void Execute()
{
int targetCount = targetQuery.CalculateEntityCount();
map.Clear();
if (map.Capacity < targetCount)
{
map.Capacity = targetCount;
}
}
}
I guess I can't
yeah it's actually an annoyance
EntityQuery is burstable (most of it)
but not safety friendly
Sooo, any way I can make it pass?
not without turning off safety for the whole job
[BurstCompile(DisableSafetyChecks = true)]
for the record i have not tested the safety of using a query in a thread like this
as far as I know though, it should be safe as long as you have your handles setup properly
any structural to the data you're querying should sync it so it finishes before that happens
actually i'm not sure this will even help
this is likely the jobs debugger not the safety system within burst triggering
best bet would be to just pass in chunk array and sum it yourself
or just not bother
[NativeDisableUnsafePtrRestriction] won't do anything. I think you want the disablesafety instead
nope, doesn't help sadly
I guess I need to figure out a solution
I could potentially update hash capacity from other system
just pass inthe size
just do targetQuery.CalculateEntityCount(); on the main thread and pass it into the job
to set capacity
or simply do targetQuery.CreateArchetypeChunkArrayAsync
and pass that into the job
and calculate the entity count yourself
can even do it async
but that has entity array overhead
hm
I guess I can combine 2 jobs into one with this approach
btw
I guess if I do IJobEntityBatch
That means that some Batches will be calculated in parallel?
if you scheduleParallel, yes
ah, it's not gonna work anyway
since I need chunk array
ok, so
I guess that would do entity count
var chunks = _targetQuery.CreateArchetypeChunkArrayAsync(Allocator.TempJob, out var handle);
Dependency = new FillMapJob { chunks = chunks }.Schedule(JobHandle.CombineDependencies(handle, Dependency));
private struct FillMapJob : IJob
{
public NativeArray<ArchetypeChunk> chunks;
public NativeParallelMultiHashMap<int, TargetData> map;
public void Execute()
{
int count = 0;
for (int i = 0; i < chunks.Length; i++)
{
count += chunks[i].Count;
}
}
}
i mean, yeah. but WHY? π
because I need to fill a large hashMap
multihashmap
private struct FillMapJob : IJob
{
public NativeArray<ArchetypeChunk> chunks;
public NativeParallelMultiHashMap<int, TargetData> map;
[ReadOnly] public EntityTypeHandle entityHandle;
[ReadOnly] public ComponentTypeHandle<LocalToWorld> ltwHandle;
[ReadOnly] public ComponentTypeHandle<StarNormal> starHandle;
public void Execute()
{
int count = 0;
for (int i = 0; i < chunks.Length; i++)
{
count += chunks[i].Count;
}
map.Clear();
if (map.Capacity < count) map.Capacity = count;
for (int i = 0; i < chunks.Length; i++)
{
var chunk = chunks[i];
var entities = chunk.GetNativeArray(entityHandle);
var ltws = chunk.GetNativeArray(ltwHandle);
var stars = chunk.GetNativeArray(starHandle);
for (int j = 0; j < entities.Length; j++)
{
map.Add(stars[i].systemID,
new TargetData { entity = entities[i], position = ltws[i].Position });
}
}
}
}
looks like this rn
and since that large hashMap is unknown size, I do it manually
In short: I create a map per starSystem/potential target for AI
since AI shouldn't be affected by targets from other star systems
hm ok, at least you reuse the chunks for the later iteration. you don't need to pre allocate a hashmap when it's not a parallel writer btw
still cleaner though with less allocations
I don't?
from what I remember
the sole reason I did it
because errors forced me to
kek
single threaded lists/hashmaps can resize themself
only parallel writer have to be set beforehand
because in multi threaded you can't resize, makes sense, right?
yeah it's fine. even better because you only have 1 big allocation
oh god, I need to write ToArray method for multihashmap
I can't bear doing iterator manually anymore
lol what? π iterators are easy?
if (map.TryGetFirstValue(star.systemID,
out var targetData,
out NativeParallelMultiHashMapIterator<int> iterator))
{
do
{
} while (map.TryGetNextValue(out targetData, ref iterator));
}
so much code
don't optimize and then screw it up with a ToArray π
so ugly
that can be done much nicer
{
while (enumerator.MoveNext())
{
var item = enumerator.Current;
...
}
}```
haha
not sure about the reason they haven't. it's good enough for me. biggest upside, i have written a custom enumerator that supports ref values. so no copy process on getting .Current
public NativeArray<ArchetypeChunk> chunks;
public NativeParallelMultiHashMap<int, TargetData> map;
[ReadOnly] public EntityTypeHandle entityHandle;
[ReadOnly] public ComponentTypeHandle<LocalToWorld> ltwHandle;
[ReadOnly] public ComponentTypeHandle<StarNormal> starHandle;
public void Execute()
{
int count = 0;
for (int i = 0; i < chunks.Length; i++)
{
count += chunks[i].Count;
}
map.Clear();
if (map.Capacity < count) map.Capacity = count;
for (int i = 0; i < chunks.Length; i++)
{
var chunk = chunks[i];
var entities = chunk.GetNativeArray(entityHandle);
var ltws = chunk.GetNativeArray(ltwHandle);
var stars = chunk.GetNativeArray(starHandle);
for (int j = 0; j < entities.Length; j++)
{
map.Add(stars[i].systemID,
new TargetData { entity = entities[i], position = ltws[i].Position });
}
}
}
hmmm, I get out of range exception somewhere here
for (int j = 0; j < entities.Length; j++)
{
map.Add(stars[i].systemID,
new TargetData { entity = entities[i], position = ltws[i].Position });
}
j
i
oh
this is such a common issue at work we basically self banned i/j in double iterations
what are you using?
was gonna say. use i/k or i/ii π
just have to name at least 1
i mean i'd jsut use foreach on the chunks here
foreach (var chunk in chunks)
foreach is fine on native arrays
Lists too?
sure, lists can cast to nativearray very fast
^
what about vice versa?
[1.4.0-preview.1] - 2020-06-26
- Add support for try/finally and using/foreach for IDisposable patterns.
basically you can do this you just can't catch
all right, let's do some stress tests, kek
oh well, 6k entities and I'm already in trouble
most of it is in ECB
hmm
most I do with buffer:
buffer.RemoveComponent<DoWorkTag>(entityInQueryIndex, e);
buf.AddComponent(sortKey, e, a.actionType);
var queue = buf.AddBuffer<QueuedAction>(sortKey, e);
and this
is it actually that expensive?
idk, it's AI
I have 3k huggers
and 3k potential targets
tbh, I think I can optimize it
a lot
i would say at max you can do a couple of hundred per frame before shit starts going down hill
Instead of doing it per entity
I can do it for query
but I'd need additional buffer for that
that knows how to do delayed entityQuery changes
luckily normal ECB knows that, heh
oh god, I'm stuck with dependency problems again
var entityHandle = GetEntityTypeHandle();
_ltwHandle.Update(this);
_starHandle.Update(this);
When I literally do this in OnUpdate
it says I haven't resolved LocalToWorld dependency
kek
The system SpaceTycoon.Runtime.AI.HuggerAISystem reads Unity.Transforms.LocalToWorld via HuggerAISystem:FillMapJob but that type was not assigned to the Dependency property. To ensure correct behavior of other systems, the job or a dependency must be assigned to the Dependency property before returning from the OnUpdate method. UnityEngine.Debug:LogError (object)
var entityHandle = GetEntityTypeHandle();
_ltwHandle.Update(this);
_starHandle.Update(this);
// First we gather data of potential targets
var map = _hashMap;
var chunks = _targetQuery.CreateArchetypeChunkArrayAsync(Allocator.TempJob, out var handle);
Dependency = new FillMapJob
{
chunks = chunks,
map = map,
entityHandle = entityHandle,
ltwHandle = _ltwHandle,
starHandle = _starHandle
}.Schedule(JobHandle.CombineDependencies(handle, Dependency));
I don't get it
what's wrong
uugh, I couldn't figure what's wrong
but apparently
mixing ForEach codegen and EntityBatch
is causing dependency problems
welp, this is way better
from 30 fps to 120
meanwhile FixedStep is running 120 iterations
per second
ah crap, nvm all above. Turns out my job just didn't work correctly
bruh, I hate automatic Dependency handling through codegen
Can I assign dependencies myself?
you are already assigning them yourself (in the above code)
the only dependency thing that code gen does is add the Dependency
Job.WithCode(() => {}).Schedule();
Dependency = Job.WithCode(() => {}).Schedule(Dependency);
that's because you've broken something
protected override void OnUpdate()
{
var buffer = _bufferSystem.CreateCommandBuffer();
var entityHandle = GetEntityTypeHandle();
_ltwHandle.Update(this);
_starHandle.Update(this);
_actionWeightHandleRw.Update(this);
_actionWeightHandleRo.Update(this);
// First we gather data of potential targets
var map = _hashMap;
var chunks = _targetQuery.CreateArchetypeChunkArrayAsync(Allocator.TempJob, out var handle);
Dependency = new FillMapJob
{
chunks = chunks,
map = map,
entityHandle = entityHandle,
ltwHandle = _ltwHandle,
starHandle = _starHandle
}.Schedule(JobHandle.CombineDependencies(handle, Dependency));
//Then we set weights to those who look for job
Dependency = new FindTargetJob
{
map = map,
// entityHandle = entityHandle,
starHandle = _starHandle,
ltwHandle = _ltwHandle,
actionWeightHandle = _actionWeightHandleRw
}.ScheduleParallel(_setWeightsQuery, Dependency);
// Now we assign target to those who want it
if (!_assignTargetQuery.IsEmpty)
{
_cacheHandle.Update(this);
Dependency = new AssignTargetJob
{
entityHandle = entityHandle, cacheHandle = _cacheHandle, buffer = buffer.AsParallelWriter(),
}.ScheduleParallel(_assignTargetQuery, Dependency);
buffer.RemoveComponentForEntityQuery<DoWorkTag>(_assignTargetQuery);
}
_bufferSystem.AddJobHandleForProducer(Dependency);
}
here full code of system
The system SpaceTycoon.Runtime.AI.HuggerAISystem reads Unity.Transforms.LocalToWorld via HuggerAISystem:FillMapJob but that type was not assigned to the Dependency property. To ensure correct behavior of other systems, the job or a dependency must be assigned to the Dependency property before returning from the OnUpdate method.
UnityEngine.Debug:LogError (object)
buffer.RemoveComponentForEntityQuery<DoWorkTag>(_assignTargetQuery);
is not allowed
i dont think
why not?
because _assignTargetQuery
does not have your dependencies from the previous systems
try do
_assignTargetQuery.AddDependency(Dependency)
before it
or do it before teh job
remember, automatic dependency management is only between systems
if (!_assignTargetQuery.IsEmpty)
{
_cacheHandle.Update(this);
Dependency = new AssignTargetJob
{
entityHandle = entityHandle, cacheHandle = _cacheHandle, buffer = buffer.AsParallelWriter(),
}.ScheduleParallel(_assignTargetQuery, Dependency);
_assignTargetQuery.AddDependency(Dependency);
buffer.RemoveComponentForEntityQuery<DoWorkTag>(_assignTargetQuery);
}
Like this?
dependency management within OnUpdate is ALWAYS managed by you
there is no dependency management done for you
is there a second error in unity
The system SpaceTycoon.Runtime.AI.HuggerAISystem reads Unity.Transforms.LocalToWorld via HuggerAISystem:FindTargetJob but that type was not assigned to the Dependency property. To ensure correct behavior of other systems, the job or a dependency must be assigned to the Dependency property before returning from the OnUpdate method.
UnityEngine.Debug:LogError (object)
that comes after this
oh
{
_cacheHandle.Update(this);
Dependency = new AssignTargetJob
{
entityHandle = entityHandle, cacheHandle = _cacheHandle, buffer = buffer.AsParallelWriter(),
}.ScheduleParallel(_assignTargetQuery, Dependency);
buffer.RemoveComponentForEntityQuery<DoWorkTag>(_assignTargetQuery);
}```
can you comment this entire thing out
just for my sake of testing something
if (!_assignTargetQuery.IsEmpty)
is your problem
remember how i said change filters cause sync points
its triggering a sync point there but it doesn't have your Dependency of the new jobs that should be part of it
welp, without IsEmpty it's also seems to trigger sync point
I removed query check
I do have it, though
buffer.RemoveComponentForEntityQuery<DoWorkTag>(_assignTargetQuery);
now makes a copy of all entities on the spot
huh
I guess I'll make my own Query ecb, for this kek
but still
that doesn't explain
what's wrong with dependencies
I'll try to remove it
to check
all RemoveComponentForEntityQuery does
is ToEntityArray
and write them all to the buffer
well ToEntityArray is a sync point
Where is the new Entity list tool? all I see is Entity Debugger, but I can't search it by name
Stuff is getting fun, patches out in my game 1-2 times a week gonna keep increasing. I'm adding asteroid shooting(actually breaks into many) and selling of the components.
in dots folder of windows
which option is it? I don't see it
wow
mine is up higher
Thank you. I didn't see it. Got moved around.
This is very helpful. Thank you Issue for solving my issue.
Hmmm
I wonder how I can implement such mechanic:
After searching entity has found it's target entity it applies special tag to it, making target invisible to all other searching entities
kind of busy indicator
by itself it's not hard, but here's the hard part:
What if searching entity gets destroyed during it's job
and now that tag left unhandled
via a hashset
wdym?
that's the way to store data
but what about handling it
in OOP that would OnJobFinish callback
if you need any kind of unique tagging a hashset is best as datatype
which is obviously not an option
yeah, I get it
but I simply don't get
how to even handle that kind of thing
hm, it could be broken down to 2 hashmaps. gives you more options to do whatever. first is hashmap of key: searching entity value: found entity then a hashset of found entities so they are blocked for other searching entities
in case a searching entity gets destroyed, lookup the value, remove from hashset
so you suggest just make a system that checks for legality of blocking tag every update?
thing is
I am kind of looking forward having this kind of mechanic for a lot of searching queries
since it's AI simulation
so that would require blocking search through several systems, that potentially don't know about each other
so that's why I'd want to have it through component
but how to handle releasing of entities from it is a question
would recommend having a single place where you destroy entities - e.g. at the end of frame
if they need to be 'dead' or unprocessed by systems before end of frame then either a tag, associated entry in an array/nhm etc or a bool flipped on a tag that already exists on the entity that the systems check against. They all have trade-offs.
it doesnt really matter if you have a comp or a native container for it. the upside of the container is, no structural changes, which is huge
but this way how would other systems know about such blocks
remember the native container is just a pointer
you can share it between systems in several ways
for example AI is looking for free slot in some station. It found it, it blocks it.
How for example would some totally other AI figure whether station is free or not, if it's not called by same AI system
yeah...
and this is where OOP comes in
and ruins fun
hmmm
why is that oop?
maybe I can keep singleton entity with blob
have you considered inverting your logic?
could you elaborate?
you don't find targets, targets find things to target them
nah, that's not an option
an empty station finds an entity to fill it
it's about entities in space, living life
yep, that's also always good to think about
meaning one spaceship might have hundreds of different things to do
how do you find out what can be targeted? via range check? sphere cast?
it's pretty complex
I have a lot of star systems
between which entites can travel only through special warp jump or smth (haven't really thought of lore part kek)
meanwhile they all exist in same world space
so all entities that exist in it have special component
with ID of current system
and while doing any algorithm on AI
that's all fine but doesn't answer my question π
I just make a hashmap of ID/potential targets with data
which later than used for all AI finders
well, in this example algorithm of hugging AI (where AI just find closest target to hug, but not closer than 2f)
I do exactly what I mentioned above + range check
you don't need to share the hashmap between systems btw
you just need it to validate writing data is safe
ok, you can possibly invert the logic. doesn't really matter from where the range check is started, right?
just saying, to think about it. it's possible to dig quite a deep hole and then being stuck
but it's simply not smth that will work the way it's all organized
{
if (alreadyTargetted.Add(entity))
{
isTargetted[entity] = new IsTargeted { Value = true };
break;
}
}```
``` NativeParallelHashSet<Entity>.ParallelWriter alreadyTargetted;
NativeArray<Entity> potentialTargets;
ComponentDataFromEntity<IsTargeted> isTargetted;```
it's never going to be amazing but this does work if you need unique targeting in a parallel job
hmm
That seems to make sense btw, I just need to figure a good pattern of how to access it
between systems
you dont need to access it between systems
in the next system Value = true will have already been set
the only problem is doing this in parallel because 2 things could target the same thing in the same frame
another easy solution is simply just creating a single queue of all operation requests
and just resolve it linearly after everything has written to it
if AI fails because something beat it, so what
it waits 1 frame to find something else
(this isnt going to scale to 6 figures of entities)
i'd just share the alreadyTargetted hashset. why write the data back to ecs. it's essentially just overhead - temporary processing data
wait a second
I forgot the main problem with that
it's when whoever blocked target entity
might be gone, poof
and block is not released
that's why i suggested the additional hashmap
you mean having pair of Entity,Entity?
Target,Blocked
key: searching entity value: targeted entity
personally i just make things clean up after themselves
and how to implement smth like this though
that's a callback, basically
well my effect system for example
{
// If true will run the destroy routine on this attribute next Initialization
public bool Value;
}```
i dont destroy entities, i just write to a component
and change filters trigger
hmm
then the effect is responsible for cleaning up any references it's setup etc
before it destroys itself
also avoids having issues being destroyed from multiple places on the same frame
and crashing
that's always a fun one
i basically have a fix life cycle management of every entity in my project
can only be created and destroyed from 1 specific spot
(different entity types will be in different spots depending on their requirements)
that's good design
but i've spent 2.5 years at work fixing crashes in production code related to ECB errors
and i never want to do this in my own project
unity recommends systemstate for removal and it's garbage because essentially significant data can be lost which you would need to save in the systemstate or in the worst case, even keep updating
so i've also went into destroy comps
it's not really about actually destroying entitiy though
it's about when AI for some reason instantly changes it's current task
for example, some ship was doing smth
and then suddenly it got attacked
and obviously that means it has to instantly react and change it's current task
while cleaning up whatever is related to it's previous task
but that's more like a task queue?
you were talking about searching entities that get destroyed
and the target to be released
I do have task queue already though, kek
anyway, on that note. be careful - what happens when a ship gets attacked, switch targets and then gets attacked by another. it switches targets again?
well, I haven't really thought anything about exactly this behaviour yet, but I'd assume I would simply have a special tag for my Utility AI system, that will force entity to reconsider it's current task even it already has one
cause rn, AI looks for task only if it has no task
On collision, set the ship hit a target of last hit him, or an array of last hit then choose targets later
which is QueuedAction buffer
I just need to figure out a way to have cleaning up callbacks I guess
is that some new post or?
posted july 12th, so it's kinda new
link?
very bottom is the DOTS message
seems positive, nice
Is there a way to have EntityQuery with conditional exclusion?
GetEntityQuery(ComponentType.Exclude<QueuedAction>(),
ComponentType.ReadOnly<ActionWeight>(),
ComponentType.ReadWrite<AICache>());
Currently I have this query
No. Optional components should be passed as componenttypehandles
Meanwhile QueuedAction might exist, only if there's also some TriggerComponent
hmm, what if I combine 2 entity query description?
Possibly? Though not the recommended way
I guess, I better just schedule 2 jobs
_findTaskQuery = GetEntityQuery(
new EntityQueryDesc
{
None = new[] { ComponentType.ReadOnly<QueuedAction>() },
All = new[] { ComponentType.ReadWrite<AICache>(), ComponentType.ReadOnly<ActionWeight>() }
},
new EntityQueryDesc
{
All = new[]
{
ComponentType.ReadWrite<AICache>(),
ComponentType.ReadOnly<ActionWeight>(),
ComponentType.ReadOnly<FindTaskTag>()
}
});
This approach game me weird and wrong result
Can I somehow check whether EntityQuery has changed since last update?
GetCombinedComponentOrderVersion
will that one do?
WithChangeFilter?
why not?
you can do that in ForEach()
sorry dont really use them but you can also use change filters with queries https://docs.unity3d.com/Packages/com.unity.entities@0.51/manual/ecs_entity_query.html
like it says in the docs
.SetChangedFilterVersion()
ah, I didn't scroll low enough
hmmm
that's actually not what I want tbh
I need to know whether query changed
added/removed entities
I don't care for their components itself
tbh
CalculateEntityCount ?
well, I do know count, but I don't know whether it's actually same entities
it might be added 2, removed 2
well store the previous number in the system or go crazy and store it in another component on a different archetype
well then track it yourself. keep track of the Entity themselves
i doubt unity is doing any ultra fancy tracking on them like you want to
I figured
I can do it by checking all chunks
hmmm
but it only works with component type handles
oh
nvm
there's a check for structural changes
all right, that works
hmmm
I assume
chunk will only contain entities with same shared component?
In other words: can chunk contain entities with 2 different shared components?
yes. shared components are saved elsewhere
actually it seems like chunk contains only 1 version of shared component
of course. because it's SHARED
but, you answered yes, kek
I meant 2 different versions of same T of shared component
is there some easy to work with function like Transform.RotateAround for entities?
I'd say the easiest is just to reimplement child transform
allthough...
no. there are no convenience methods in DOTS, for best performance. implement it yourself with the Unity.Mathematics package
I solved rotate around by literally creating child and parent matrix4x4
and then just multiplying
^^
in your case, you can get parent's (entity you want to rotate around) LTW matrix
and rotate it how you want
and then multiply with LocalToParent matrix of how you want your entity to be rotated around
could i make a system set component data by reading data from another object, like i was thinking of replacing my database with value objects if that makes sense?
could probably. does it make sense? questionable.
public class person { public int id {get; private set; } = 1;
or something like that
well the data is static and doesn't change
it's game obj data
why not simply have an array then?
hm wouldn't that be messy when i have arrays of objects with arrays of objects etc
well the entities im concerned with are not in a parent child relationship though. so id need to get the relative position etc and ignore scale. sounds like i just gonna copy Transform.RotateAround and rewrite it with the math lib
you just said you have this: public class person { public int id {get; private set; } = 1;
no, you don't need one
you make them yourself
you need LTW of parent
and LTP of child
LTP would be offset position to parent
transform.rotatearound() is an internal unity cpp method. you won't find the source code for it
ah damn
you can get best idea of what I mean
by playing with GOs in editor
just drop 2 random cubes somewhere in space
and assign 1 to other as child
and watch how position/rotation/scale is changed
this is how you'll get your LTP matrix
and then you just need to rotate LTW of parent
and multiply with that LTP
and you'll get LTW of child with that rotation applied
yes its easy to get relative coordinates to other entities. the second step im not so sure about
what's great is that since it's matrix multiplication: everything is figured for you
scale/rotation/position
ill try!
goddamnit. no fun memes allowed
nooooooooooooooooo
sure, either way, using arrays or dictionaries or objects, it would still be game data in the code, and not game data in json or a database table. that's the important distinction to my question. second part would be about DOTS and using these arrays or whatever. i want to read from these arrays or w/e and set my icomponentdata to those values
or would this be some kinda anti-pattern i hope not
can you elaborate, what kind of type of data you want
you can read from them no problem
that's a bad pattern. use the filters provided. either orderchange of chunks or DidChange on comps
orderchange of chunks works, yes
especially orderchange is nice because it keeps track of structural changes
this how I'll basically can see whether data is invalidated
and I need to recreate my hash lookup
but seriously, start investing into proper change filters. it'll make sooo much easier
for potential targets
and don't rely on add/remove comps
+1
well, filters require components
and I only have entities, kek
that match query
that's all I care about
relational offline static data. it's not an online game, the data changes 0 time or almost never. it's relational in the sense that an object can contain references to let's say a list of other objects
add a small state comp and bump the version accordinly
just use a database then
i don't know if versions exist on empty comps/tags. i don't think they do
I don't think you understand my use case, here
I simply store int,Entity hash
oh ok that's what im doing rn
like, you can do it with entities, but that would be just a pain to scale and sql server is already data optimized
using a db would be faster than using code generation that creates dots compatible c# data?
oh i see you're still on the hashmap π so the hashmap is not frame based?
(im noob so i don't know how to do optimization and stuff besides caching ig)
i don't know how much data you have, but if you put it all as entities it will even affect your game performance since unity has to iterate over all those as well when any query is run
use blobs π
:/
I'm looking into my options
this exact one does, but future ones won't
for example when it would be related to static entities
if you can keep track of the entities and keep a previous array you could do a fast memcmp to detect changes
I wouldn't want to keep previous array
but I can store chunk, version map
which would probably be way cheaper
in cost of memory
you'd need a lot of entities to really matter
thousands is like nothing π
it's for AI simulation of whole galaxy, kek
an entity struct has 8 bytes. so even with 10k you are just looking at 80k bytes
meaning you lose l1 cpu cache potential
hmmmm, I am currently looking into way to have cleanup for my AI system, which is based of action queues (go to point, do action X, idle for 300 ticks and etc)
so that some actions could potentially reserve certain entities
making them for other AIs
rn if some AI is assigned to some task, for example: go and hug X entity.
in OOP I'd just assign reserved tag to X entity and remove it through callback once hug action is finished
but since there's no callbacks in ECS
I need some other solution
instead of making those systems read from something, i could use code generation to create a system and set the data immediately. what i dislike about this is that the hard-code approach wouldnt be modular. since i already did 35% of my db tables i think ill just keep it as my storage location, mostly due to bias and ease of use, and it appears from what you're saying that databases are probably more optimized for this anyway
Some of my code uses World.AllWorlds in a for loop, but that is now an internal variable - how do I retrieve all worlds?
you could of course do code generation. and you don't even need to hardcode it
would it be faster?
there's practically no use case for world.AllWorlds
just keep references to them when you create them
assuming im not gonna write the most optimized stuff
it was done in the old netcode examples
it's a one time thing btw. the data is created once and then you don't have to do anything but read from it
well, since it doesn't have to read from database at startup and has all the data available already....probably? slightly.
but if you ever want to change it you WILL have to recompile your game
like foreach(var world in World.AllWorlds) { var network = world.GetExistingSystem<NetworkStreamReceiveSystem>(); if(world.GetExistingSystem<ClientSimulationSystemGroup>() != null) etc
okay thank you @haughty rampart (: that helps me and i feel more confident in just using a db now
that has zero to do with L1 cache π
i'll just say this on databases: database data are not a good fit for ecs mainly because you generate a lot of archetypes with chunks that potentially are mostly empty. then, database data is most likely not read in a linear fashion in game code. it's mostly, read this row, read that row. as entities is SoA, there is zero benefit from cache lines reading 64 bytes as you're reading the next value of other table rows essentially. what's really good for database data are blobs. they are linear in memory, easy to read single entries fast, and can build pretty much any data except pointer data. they are not that good for relational data but when built at runtime, even relational data i.e entity references work. building at runtime is really fast. they are easy to store on an entity because they are just a pointer and easily mappable via a hashmap and a key for fast lookups.
When designing systems would you consider optimizing for chunk utilization over avoiding random accesses(GetComponentDataFromEntity)?
I mean you either [create more different chunks] or [have less different chunks, but you access some additional data through GetComponentDataFromEntity]?
whenever you can you utilize chunks, whenever you can't you use random access
it's that simple
chunks iteration is the fastest out of all options
random access is for cases when chunks are just not an option
for example when you need to some data by Entity reference
why do you need more chunks to avoid random access?
Hmm, I don't think you get my question completely. I'm designing some system. I can either have a combination of Logical Triggering data on a single Entity of A,B,C,D,E (which could anything from [A,B] [D,E] [A,B,E] etc, thus utlizing chunks badly). Or I can move the Triggering data into their own entities, but doing so adds requirement of using GetComponentDataFromEntity when doing the logic.
what is ABCDE?
Components
if you have single Entity you are not utilizing chunks though
There can be multiple entities of the combinations, which includes some that can be same combination.
CDFE is never that great but it's also fast enough. unless you are simd'ing your code don't worry about that stuff too much. just make archetypes as small as they can be and stay away from structural changes or entities that only live for 1 frame. in scalar code, the amount of read/writes is what's crucial (throughput), not really in which memory location it is. without simd you'll have L1 cache misses left and right. the good thing about it is that there's a high chance the data you need is still in some cache (L2/3)
honestly i wouldnt worry about bad chunk utilization here. either you have alot of entities which means your chunks are full anyway or you have a small amount of diffrent combinations of entities and dont hit cache but also you dont have much work to do anyways.
Thank you : )
if you have huge entities where for example only 2 can fit in a chunk that is where id worry about chunk utilization. cause that would mean alot of cache misses while iterating over chunks in any system.
when scalar you will have a cache miss on every new comp you read from the chunk unless the cpu prefetches, which has a low chance of happening because scalar
i brought this problem up once in the forum but AoS has a near 0 chance if being implemented
huh i thought the whole chunk is beeing brought into L1 cache when iterating over it. thus no matter which component you get from it its already there.
guess i need to check out some talks again to get it right
isnt it layed out as Array of Structs? Each component of one chunk is in an Array.
nvm just confused. its SoA
1 comp read turns into 1 cache line of 64 bytes that read compA of entity1/2/3/4/5 ... until 64 bytes are read. perfect for simd. not that great for scalar, especially when the archetype is quite big.
after you've read every comp chances are quite high that on the next iteration you'll have a cache hit though.
but the cache hits are still not linear
the rest is really up to cpu implementation and this is where it gets really hard to predict anything
on that note. i had ideas where i have a stage where i read a whole chunk linear and then the rest of the code π
most caches are 32k data and 16k chunk leaves me enough room with other data to do what i want and still have cache hits
but i've never tested this. still on my mind
with the job scheduler there's a good chance the l1 thread cache belongs only to you. the other caches are shared so once you're in L2 land, chances are quite high you never have a hit because you're not on a console and kther processes push out your data
there's a really good talk about concurrency. i'll post the link then here
L2 on modern gen is still per core on amd
that'd be great! hmm cache miss per component would mean its actually not always better to split up data into more components. interesting.
i really need to check out the DOTS best practices guide now. havent gotten to it yet. maybe i have a few other misconceptions i can get rid of
its theory. last time i merged comps into a 64 byte comp i got a huge performance loss.
pretty entertaining too
the video is from this guide
Think you're over thinking this. If your concerned about it not being prefetched, Unity.Burst.Intrinsics.Common.Prefetch you can do it yourself but I'm pretty sure the compiler has you sorted for native containers
probably. i just want my theory to be correct, otherwise it leads to wrong assumptions
I'll try the prefetch. do you know more about or have tried it?
Hello, I'm rather new to unity and especially dots. I was wondering if dots and the ecs infrastructure is currently production ready or if I should be waiting for an official finished release.
Burst and jobs is considered production ready but entities is not yet by unity
That being said there have been multiple games released using entities
alright thanks, I'll check out the pinned messages, seems to cover a lot
not that familiar sorry, just aware it exists
@lavish wharf dots currently doesnt include native animation, pathfinding or audio and 1.0(ie "production ready" in unity's terms) wont either. unknown when those things will have dots native equivalents
using Common.Prefetch(avatarStats, Common.ReadWrite.Read, Common.Locality.NoTemporalLocality); on my final stats array shaved off ~7ms overall thread ms time. overall this also means nothing with 23 worker threads
on my blob that is reused over and over again it changed absolutely nothing. pretty sure the cpu figures it out
it mostly works as expected. the hardware knows best :) my concern above wasn't really the lack of prefetching but that prefetching can't be applied well when the data layout and code isn't suited for it. just putting data in chunks isn't good enough
just putting data in chunks isn't good enough
I guess i just don't understand this bit
if i turned off auto vectorization and just let it run scalar i would expect no difference (except being ~4x slower)
as i said, possibly i overthink l1 and cache misses. the thing about auto vec or turned off. the data is already near perfect to even run in scalar with no cache misses. so that use case guves the expected 4x difference.
I'm mostly refering to data that isnt laid out that nice or has very complex code compared to what usually runs in a simd loop. lots of branching, bad locality
yep sure
funny enough though, that's what my AI system does and it runs magnitudes faster than our nicely laid out data implementation at work
achieving better locality is key here otherwise the l1 cache looks like a fragmented mess
huh, any idea why that is?
because my entire ai is 1 job instead of dozens and i can early out
it was designed around near perfect thread scaling
yeah, the job scheduling overhead is significant
not just running the job but everything up to that point
anyway, your ai system could run even faster π i'm not saying it's possible to achieve. i'm pretty stuck since months on one of my jobs. ^^
it would need a redesign but i also can't find any other way to do the same things but faster. i've reached some kind of equilibrium with it, yet, i'm still looking for improvements like a mad man π
i'm not sure my ai could run faster with it's design tbh
i think it hit its limit for what this design could do
that's also my humble conclusion. every improvement would mean a heavy cut in the game design and that's not something that can change
biggest benefit of my AI is i can do partial updates