#archived-dots
1 messages Β· Page 118 of 1
well first of all, my tile array is a 2 dimensional array so that gets complicated with the index
You can easily represent a 2D array with a 1D array, and convert a 1D index to 2D and vice versa. So inside Execute you can derive your x/y position from the index it's passing
not sure if it'll be worth it though
Dunno, based on your description this is exactly what a bursted job is good at, transforming data in a linear array. Up to you if you actually want the performance or not
I suppose I'll keep trying but I dont really understand the syntax or how to use it in a way that works with my code so
Just study that page I linked carefully. A big part of switching to using burst is rethinking how you structure your data. If you want that performance, you need to represent your data in a cpu-friendly way, no abstractions. When you get it into a nice linear array and rewrite your logic to work inside jobs, you will be amazed at how fast it moves compared to the kind of OOP most people write.
Also make sure you actually enable burst on your jobs with the [BurstCompile] attribute
@zenith wyvern Thank you, so just to be clear, what you are saying is that I call the job once instead of creating one for every 40,000 tiles?
Yes, just like the example in the link I posted
@zenith wyvern Alright, hopefully I'm not annoying you but how did you say I could convert the array to 1D?
Should be plenty of material on google on how to represent a 2d array in a 1d array
@zenith wyvern One problem is that I can't SetPixels in a separate thread
Yes, you would complete the job then write the array all at once with one of the batch versions of SetPixels
SetPixelData even takes a NativeArray directly
There's the added complexity that the data needs to be formatted correctly for the texture format (not colors) it may have to be represented as bytes
Tho u could convert in a final job
As far as perf being 800ms, the editor has safety checks that slow things down
(safety can be disabled in burst drop down menu, but is good idea to keep on when developing)
if your writing a texture in jobs/ecs land I'd be using a dynamic buffer <yourrgbstruct>
and only binding / doing the texture upload though a final call
that way only that last step has to be on main thread
@zenith wyvern well back to this error, I'm pretty sure it appears because the NativeArray is being edited in multiple threads at once Index 0 is out of restricted IJobParallelFor range [36663...36663] in ReadWriteBuffer.
Inside execute you're trying to access index 0. You can only access the index passed by the execute method
dont get what you mean
From the page I linked you: Execute(int index) will be executed once for each index from 0 to the provided length. Each iteration must be independent from other iterations (The safety system enforces this rule for you). The indices have no guaranteed order and are executed on multiple cores in parallel.
It's just really confusing
Not sure how it could be any more clear than the example on that page. You can always try taking a step back and working on a simpler test project
Just to get a better feel for how to properly write jobs and structure your data
It's like a for loop and each Execute is just the body of the loop, so you are only supposed to operate on the index given by the argument.
think of it as a for loop where based on your batch sizes and number of cores in your machine it will give one job thread all the job data and say run(0-100) a random other one (400-500) and then another one (300-400) and so on
the safety system is just enforcing you work on the right indices
Hello, I'm making a game with procedural terrain. Instantiating tons of terrain objects is pretty slow. Is dots something that could speed up the instantiation of lots of prefabs? (Sorry if this is a dumb question)
prefabs of gameobjects, no, best to pool that kind of thing
what kind of procedural terrain is important
are you sure that its the spawning thats slow and not generating the data?
Is there a simple way to change a value type in a job without putting it in a nativearray?
if its 'global' i use shared static for that, thats what i do with Random for example, but no if that value belongs to an entity or system probably
but perhaps you could take the unsafe pointer of that value and send it to your job ? never tried it before but perhaps there is something like that
.Reinterpret<T>() @zenith wyvern
most native things support it, and its free, but make sure the type your converting to is the same size (or you know the difference)
It's more like what Curly said I just want to store a single value associated with my struct, putting it in a nativearray is annoying but I have no idea how to work with pointers in C# so I'll just use a NativeArray with a size of 1
you mean, so that your job accesses the same as another job?
One of the problems I'm having is that the index cant be used in addition for some reason; this works cs for (int i = 0; i < c.Length; i++) { colors[index] = c[i]; } but this doesnt cs for (int i = 0; i < c.Length; i++) { colors[index + i] = c[i]; }
what kind of error does it give ?
if you are doing this in parallel job safety system wont let you
you shouldn't be traversing the array manually with a for loop
Index 23328 is out of restricted IJobParallelFor range [23324...23327] in ReadWriteBuffer. etc etc
but if you need to do something like i+0 i+1 i+2 as a stride then thats something different
yeah because your trying to walk outside of your allowed index
soo you cant do for loops in a IJobParallelFor?
you can
but not on the base data when writing
you can only write to your data at index as a race condition safety
you can turn this off but I don't think you are doing something that needs that yet
if you explain a bit more of what you are doing I'll show you
@low tangle Yeah, basically I'm making a NativeContainer and I have an associated length. I write to the internal NativeArray so the changes are reflected outside the job, but if I change the "length" int inside the job, it's not reflected outside the job
Yeah it's my own variable
yeah, thats because the values there are thread local memory
everything inside the job struct is thread local copy of the inital data when constructing the struct on the main thread
Just an int inside my container struct. And yeah that's what I figured, I was wondering if there's a way to make that data shared outside of putting it inside a nativearray
Well, before I was doing all these Job things I had a function that got called 40,000 times (for each tile) at startup or when you regenerate a world, and that function did everything like neighbour checking and setting 8x8 colors at tile position in a Texture2D. Now with Jobs I pretty much want to do the same thing but in a more optimized manner, but I cant get anything to really work as I want it to. Mainly because I pretty much cant edit a Texture2D in a different thread
well, when I ran into that kind of problem before, I just stuffed it onto a singleton entity with a known type. in the job struct I grab the entity, paste it in, grab a ComponentDataFromEntity<singletonT> and shove that in with safetys off
you dont need to use ECS for that, you can just use a bursted job if its computation heavy @odd cipher
Yeah I know I dont need ECS for that, I am talking about just Jobs
thats just to paste in the data at the end in bulk
instead your job will be operating on a NativeArray<Color> of [Width*Height]
your for loop operates on the 1d values, so you would then index % width to get your x and index / width to get your y
then your inside your for loop with X, Y and the rest is your algo
any procedural data will be brought in as NativeArrays, or generated there
so, that NativeArray has the whole Texture2D or one NativeArray for every tile?
one native array is the colors that will be set onto the texture2d when done
its your placeholder for the texture2d, you do all work on it, then later on you would paste it onto the texture2d in one memcpy and call it a day
then I'm pretty sure the problem I'm having is just that final loop. because thats supposed to set a 8x8 area of colors in a tile but currently it doesnt
I havn't since its rather large and most of it doesnt have anything to do with the Job part
That's fine, you can just point us to where the problem arises
We're big boys, we can manage it
well pretty much this part in the Job cs for (int i = 0; i < c.Length; i++) { colors[index] = c[i]; } because currently that will just set one pixel. not a 8x8 area of pixels which is what I had before I tried using this Job system
Can't help you much without the full code though
okay, I'll send a simplified version then https://pastebin.com/VkNU0brC
@odd cipher I'm not sure why you would loop over it like that. the index variable isn't going to change within the for loop. So that would be the same as removing the for loop and setting colors[index] = c.Length - 1
I just said why It's like that and why I need to change it somehow but I dont know how
Well, if you post the whole code it would be easier
But it's not all the code
If you refuse to let me help you, then I won't try, sorry man
instance.tiles1D
yeah that might a lil problem aswell
this isn't passed into the job
string defaultName = t.terrainType.name + "_";
avoid strings
instance.GetSprite(
this likely wont work
I dont see this function nearby as a static function
also sprites are objects in unity mono land
no instance is the script that this is in
for (int i = 0; i < c.Length; i++) {
this doesn't work because your already in the body of a for loop doing this for you
IJobParallelFor
You can't use any reference types or any mutable static data inside a job. Basically you should think as your job as being entirely self contained.
And I mean entirely. No exceptions.
Anything the job needs, you pass in.
And again, no reference types.
That means no "normal" arrays, only NativeArrays
And no strings, and etc etc
seems really annoying to work with then
only at first
Like I explained earlier. You need to entirely rethink how you are structuring your data. You are making it CPU friendly.
if you get over the small hump its easier for some people to build this way
and a lot faster for the cpu
And the performance gains are truly massive compared to how most people write OOP.
Alright well, the first thing I still dont understand is the index
i still dont know what/how burst do but it works π€·
in IJobParallelFor yes
its a bunch of for loops
each running on batch size index's
one will run on 0->batchsize another on batchsize->batchsize*2
if batchsize is 8
that would be two different for loops running on
for(int i = 0; i < 8; i++)
and a totally different one running on
for(int i=8; i<16; i++)
yeah but hes not getting the simple fact of the slicing
needs to connect the index to that for loop thats done for him
IJobPrallelFor is to process an array in a parallel manner.
Lets say i have int NativeArray that has 10 elements, then what will this IJobParallelFor will do is process each element in a parallel manner, that "index" variable in the execute function is the index of the array that is currenty being executed basically
I'll let you take over though, I'll get back to work
you can think of IJobParallelFor is the loop itself, and execute function determines what it does with it's elements
I just need to figure out how to set a 8x8 pixel area per loop and that is what is confusing me at this point, for that loop that I showed a couple of times
no, they are pretty much tiles
Oh I see, each of your tiles are represented by 8x8 pixels in the texture, right?
yeah
Hmm I'm trying to think of the best way to do it
longer size / 8
@digital scarab Is it possible to make it safe to access a certain sub-section of the array within a ParallelForJob without disabling the safety system?
actually wait width / 8 + height / 8 for the number of tiles to loop though
that gives you the tile index you are working on
the only problem is the saftey system will have to be disabled on the nativearray
or cut your array into pieces and process each with their own jobs and and combine those jobs and wait for them ?
more complex for no gain
Cut my array into pieces....this is my last resort.
but yeah, why would disabling safety be a problem π€
suffocation no indexing
because we made a big deal about it and it will be confusing why the 180 flip and disabling it
If you have the tile index though you may not even need to access adjacent indices
Whatever logic you were going to do could be figured out from which pixel within which tile you're in
thats... thats pretty deep
void Execute(int index)
{
int tileSize = 8 * 8;
var tileColors = new NativeSlice(texColors, index * tileSize, tileSize);
for(int i = 0; i < tileColors.Length; ++i )
tileColors[i] = whatever
}
Something like that I guess? And you would have to disable the safety restrictions on your container
@odd cipher So basically instead of doing the foreach over every pixel, you want to do it over every set of pixels representing your tiles. You get the subset of pixels representing your tiles using a NativeSlice like the code snippet and you can change the slice to affect the pixel of that tile
It's a bit complicated but that is probably how I would do it
don't think you can create the slice in the job but I haven't actually tried
I now can't get over a mental image of baby yoga trying to ride a tiny bicycle
## [Havok Physics 0.2.0-preview] - 2020-03-12
### Changed
- `HavokSimulation.ScheduleStepJobs()` now takes `SimulationCallbacks` and `threadCountHint` separately as input arguments.
- `Simulation.CollisionEvents` and `Simulation.TriggerEvents` can now be used to iterate through the events directly using a `foreach` loop, rather than only via `ICollisionEventsJob` and `ITriggerEventsJob`.
### Fixed
- Android ARM64 platform is now supported. Please see [Supported platforms](Documentation~/platforms.md) for details.
- Jobs implementing ICollisionEventsJob, ITriggerEventsJob, IContactsJob, IBodyPairsJob or IJacobiansJob can now be Burst-compiled in both editor and standalone player.
- Duplicate jobs for debug display (like FinishDisplayCollisionEventsJob) are no longer being scheduled, since only HavokPhysics variant of their systems (DisplayCollisionEventsSystem) is now running and covers both HavokPhysics and UnityPhysics simulation.
@warped trail ^
just tested the new havok package, at least the joints don't explode on it anymore like in the earlier version π
that being said, I don't have 100% same setup here but it should be close to identical for the joints themselves
the previous exploding suggested some forced rigidbody overlap tho and that could indeed be different now on my test scene as I use different mesh
how can i use this Simulation.CollisionEvents ?π€
Yeah, i asked the same question yesterday but couldnt figure out
Simulation property from StepPhysics returns the interface not Simulation class which holds the trigger events and collision events
and Burst does not support foreach π€
generally yeah, it does with a ref struct though.
new hybrid renderer v2 test projects on srp branch now
or I'd think so anyway, https://github.com/Unity-Technologies/ScriptableRenderPipeline/blob/hybrid/hybrid-samples-srp/TestProjects/HDRP_HybridTests/Packages/manifest.json says these use "com.unity.rendering.hybrid": "0.4.0-preview.7",
so I guess 0.4.0 is v2?
we are now at 0.3.5-preview.15
that 0.4.0-preview.7 isn't yet available on public registry (had to check)
(meaning, we can't test this ourselves yet)
is there a list anywhere yet of whats changing? the two things i care about are 1) manipulating shader properties without hacks or replacing the material and 2) being able to build for IOS without it showing psychedelic textures. been working on other things while i wait for these.
there isn't any list yet but once the hybrid renderer v2 releases, we get at least the changelog
this is what Joachim wrote at the start of Feb: ```Changes are part of 20.1.
It is both massive perf improvements, keeping all instance state persistent on the GPU.
As well as supporting the full feature set of HDRP during conversion. ```
(about the upcoming hybrid renderer upgrade)
I'm bit reserved on "full feature set" part there, but it's probably going to be more compatible at least
also from what I can tell, the changes are for both HDRP and URP on the SRP repo, no idea if they still keep focusing on HDRP more (like they said earlier)
@mint iron I did dig the psychedelic visuals tho π
but yeah, better URP support would probably help you there
sounds promising, so very glad they're working on it.
uuuh, burst new package ? π
Should hopefully fix some of the crashes
yeah probably, since previous release was yesterday
I'm confused how comes I can convert a LocaltoWorld straight into a Matrix4x4 componentdata but I cant convert Localtoworld into a Matrix4x4 in a local variable?
Should be able to. Again, make sure you're using localToWorld.value so you're actually using the float4x4 and not the localToWorld
## [1.3.0-preview.6] - 2020-03-12
### Added
- Experimental support for `Prefetch`, allowing users to request from the memory subsystem pointer addresses they intend to hit next. This functionality is guarded by the `UNITY_BURST_EXPERIMENTAL_PREFETCH_INTRINSIC` preprocessor define.
### Fixed
- Fix SSE `maxps` intrinsic would emit `maxss`
yep am doing that I think
Matrix4x4 bezzymatrix = new Matrix4x4();
bezzymatrix = BezzyTransform.Value;```
that's what I'm doing the Bezzytransform is the localtoworld and it does not work
BezzyTransform.Value is a float4x4?
no its a LocaltoWorld
you need it to be a float4x4
since float4x4 can be implicitly converted back to Matrix4x4
otherwise you can do a Matrix4x4.TRS(position, rotation, scale)
do I have to manually do that with each specific variable? I thought I could auto assign it
uh what does auto assign mean?
i just mean assign all the variables at once like an array to an array
it works when I assign a transform to a localtoworld in the conversion for instance
I can even assign a transform straight to a matrix4x4 by using transform.localtoworldmatrix
LocalToWorld.value is a float4x4 isn't it?
it is
localtoworldmatrix is matrix4x4π
Oh you stored a LocalToWorld component
I wanted one that I knew and was easily accesible
yeah that's something I'm a bit confused about. Is there a reason the Matrix4x4 stores a Float4x4? The same with quaternion
Why not store the individual variables inside the structs?
I couldn't just get it off the entity because I'd have to create a tag for entity and couldn't be bothered
individual variables in the struct?
what do you mean by individual variables?π€
Like we can do Quaternion.x, but we have to do quaternion.Value.x in ECS
Well I'm not sure what you're trying to do, but the point you need to understand is that you're not converting a LocalToWorld to a Matrix4x4, you're converting a float4x4 to a matrix4x4
4x4 matrix is default thing for transformation, isn't it?
no wonder I was confused
@bright sentinel I think you are confusing Rotation (a component) and Rotation.Value (quaternion)
oh
... I might
never used direct values
I was right
yup - float3.x but quaternion.value.x is a bit annoying
the could build getters into the default components make it look nicer i guess.
All components are just wrappers around the underlying data. You shouldn't think of a component itself as your data, it's just the wrapper
oh, sorry, the quaternion has a float4 in it
it just happens to be a constant exception with my codegen as it's different from other types but it's not a big deal
I guess it might help remind some people it's not euler?
i think I remember i had a blob that was value.value.value once
haha - yes that was me once π€
Yeah I also don't like the naming of the built-in components Value
Too nondescript imo
considering the UnityEngine.Quaternion docs have a "please don't touch this" next to each of x,y,z,w storing an opaque "value" may be a good idea tho π
yeah, why do you need this valuesπ
that's it I'm not using value anymore π
@warped trail For netcode syncing
I mean, it's not like a big thing to write .Value, but it caught me off guard for a while
what about an extension with aggressive in-lining, you'd have to check but it might collapse it.
can't have extension properties, so it'd be quat.x(), which is even more confusing
I've written an extension for localToWorld to get the scale as an xyz float3 and my friend looked at me funny asking 'What's ltw.Scale()?"
## [Burst 1.3.0-preview.6] - 2020-03-12
### Added
- Experimental support for `Prefetch`, allowing users to request from the memory subsystem pointer addresses they intend to hit next. This functionality is guarded by the `UNITY_BURST_EXPERIMENTAL_PREFETCH_INTRINSIC` preprocessor define.
### Fixed
- Fix SSE `maxps` intrinsic would emit `maxss`
I thought prefetching didn't give any gains on modern cpu's
it does bring gains
i measured gains from it on an ECS implementation i did
but it was very convoluted to actually work well, so it was annoying to use
i saw a gain of 10%
ah, I just remember some older discussion on this topic where that was the stance for not supporting it
but it was having to look-ahead 5 steps
Isn't that the whole issue of performance optimizations? π
in the algorithm
Hard to achieve
btw, unity updated physics samples now: https://github.com/Unity-Technologies/EntityComponentSystemSamples
## [Samples Project for 0.3.0-preview] - 2020-03-12
### Changes
* Joint samples now correctly add new joint entity to prefab's linked entity group so they will be instantiated along with the rest of a prefab.
* Renamed `StiffSpringJoint` to `LimitedDistanceJoint` to reflect changes in API.
* Updated the following packages
* Input System from `0.9.6-preview` to `1.0.0-preview.5`
* Lightweight RP from `7.1.6` to `7.1.7`
* DOTS Editor from `0.2.0-preview` to `0.3.0-preview`
* Hybrid Renderer from `0.3.3-preview.11` to `0.3.4-preview.24`
* Character controller improvements
* `CharacterControllerUtilities.CheckSupport()` now uses a collider cast
* Character now doesn't collide with triggers, but raises trigger events instead
* Fixed the support check issue with slopes equal to MaxSlope
* Fixed the returned support state when there are no supporting planes```
if i had less or more, the 10% would quickly evaporate
sub 3 steps it was actually slightly slower
basically forming a bell curve
so yeah it was definitely not a good idea to try using it in that algo, at all. Too volatile and only works at specific step size that depends on cpu
interesting - that's good to know
there are some fun cases where you can get gains. THere is a cppcon talk about it
a very fun one
where they abuse coroutines to execute a bunch of binary-searches at once, interlieaving the search state with prefetching
o - do you have the link to the video?
http://CppCon.org
βMemory Latency Troubles You? Nano-coroutines to the Rescue! (Using Coroutines TS, of Course)β
β
Presentation Slides, PDFs, Source Code and other presenter materials are available at: https://github.com/CppCon/CppCon2018
β
Are you doing memory lookups in a hu...
i did some tinkerin with the coroutines on cpp20
they already work on visual studio
they are pretty fun. Better and more flexible than the C# ones
main detail of the cpp coroutines is that their overhead is very low. They are stackless, and its basically an abstraction over a state machine
anyone know how you use ConvertAndInject with child gameobjects ? I want the builtin conversion system to run on the child gameobjects though, not just manually adding components
the compiler creates a "context" struct with a state machine inside, and thats what the coroutines converts to. It does 1 allocation
in that talk above, they do the whole state machine business by creating a custom coroutine type, and then also create a custom allocator, because coroutines in C++ will forcibly allocate once
@safe lintel havent found a public API yet - but I did manage to do it with reflections to match ConvertAndDestroy but left out the destroy part
Cpp coroutines are closer to the auto-generated IEnumerators (with yield etc) from C# than the Go variant, right?
somewhat, yes
but a lot more controllable
kinda... too much
its absolute madness
in fact, the compiler doesnt implement coroutines
its up to the library writers to use the syntax sugar state machine thingy
and create their own coroutine types
in the cppcoro lib, there are a few you can use, thats what ive been tinkering with
there is a generator<T> coroutine that is sync (cant await), and its handle can be used as iterator
generator<int> numbers(int max){
for(int i = 0; i < max; i++){
co_yield i;
}
}
for(int n : numbers(3)){
cout << n;
}
this will print 0,1,2
also a recursive_generator where you can yield a generator inside another generator, but from the outside, everything will still be a 1d linear iterator
yeah, really wish the C# enumerators had less overhead. They are really convenient (vs. an explicit state machine), and writing this kind of generators as structs that can be supported by foreach is so much pain
the library also has more "typical" coroutine stuff. THere is a Task<> type that is a over-time task (can be co_awaited), and in fact it doesnt get executed until you co_await it
but yeah, the point of the cpp coroutines is that they are spectacularly overengineered, so that library authors implement whatever they want with the "base" mechanism
I was trying to replicate the Rust-style iterators a few years ago, would be trivial to do with yields (or even LINQ) if you don't care about allocations, ended up with like 2k LoC of generic structs just for some basic iteration over lists/arrays/slices
rust final async-await thing is not iterators
its just the "state machine" base
its also truly zero overhead. rust awaits dont allocate
unlike cpp ones
honestly my first impression of generators is great
this is crazy good for ecs stuff
simplifies the code
i will try to make those sort of prefetched generators (like in the talk above) on my ECS lib
I meant the Iter trait (and friends) and how the Rust for works. Like expressing your int 0 -> max generator as a C# IEnumerable struct is a stupid amount of code
do you guys have tag components or do you have boolean components do do if(booleanComponent.true) in the systems?
say for example if a mesh needs to be recalculated, should I do it as a tag or boolean component?
mostly depends on if I want to query by that component
yeah I mean either you query or you do a if statement
sorry for the newb question: what's a tag component?
if the mesh recalculation is rare I'd probably use a tag
Generally better to mark (ie change boolean value) if it changes every frame
Tagging changes chunk archetype
@slim nebula tag components are components with no data in them (only used so that you can use them in queries)
yeah because of the copying when changing archetype I was asking
there won't be new meshes created every frame
only when the player changes something in the environment
I'd just tag in that case then. Fewer entities to iterate through
In that specific case (assuming that it matters for performance, which it probably doesn't), I'd actually spawn an entity with an UpdateMesh(Entity meshEntity) component or tell the mesh rebuilding system directly to update that mesh when it next updates
also what is the best practice for this: I want to create the meshes and store them somewhere and handout an id to some entities, so that I can control the deletion of the meshes from the entity.
Should I do this as a normal C# dictionary in a class that I pass around? what should I use as index? I could use the entities that "own" the mesh as index or just an increasing id.
If I would use an increasing id I could also just use an array of meshes instead of a dict and try to implement some "id reuse" mechanism
WithChangeFilter could be of use too
@worldly pulsar you are a genius thanks
that is the perfect solution
I'll check that out @fallow mason
"best practices" for Unity ECS are not exactly well established. I'd say do the simplest thing that works and see if there are any problems with it
ok I mean dict with entity index is obviously the simplest thing
and there won't be so many meshes to draw that the iteration over the dict will completly kill me I guess
ok and if they somehow make the whole mesh thing fully ECS at some point then I will probably have to change that either way
ok trying to find anything that explains what WithChangeFilter does π
I know we just said there is no "best practices" for ECS, but if there a good way to make a global entity archtype? I've only been able to find examples of them being setup and used immediately. Where as I would rather set them up all in one location then create them on need, but i can;t think of a good way to go about it.
you can store it in componentπ€
static class?
Static class was what I was thinking, Which usually means there's a much better way to go about it.
MyGameArchetypes.PlayerArchetype tempting though, huh? π
I know I'm gonna end up with like 20 singletons at this rate XD
doesn't need to be a singleton, just a static class (in case that's what you were thinking)
Well it seems like I need to go do some reading as I apparently have been handling static classes wrong in unity
yeah static classes can be abused (singletons even more so), but it might be the right tool for this particular job
Be careful with static classes if you are using the fast scene/assembly reload options
It causes issues i take it?
static fields are not reset to null when you enter/exit play mode (that's like 90% of the speed boost you get from the fast assembly reload afaik). So be sure you explicitly initialize them in system OnCreate or w/e
Anybody know what's happening with the Hybrid dots renderer V2 that was added to the roadmap?
what's new with v2, why is it better etc.
@glossy summit There's some discussion further up about it, although not sure how much π Generally, it has full feature set for SRP, URP and HDRP, and a lot better optimization than V1
Ah yeah, found it thanks!
@warped trail thanks
guys, i can't get the dots physics debug display to work. all you need to do is create an empty game object and add PhysicsDebugDisplayAuthoring (and ConvertToEntity of course) to it, right? i'm just seeing no debug display at all when running, and there are objects colliding...
@zenith wyvern Late response because I had to sleep but that gives me ArgumentException: Slice may not be used on a restricted range array
the one downside of ECS I seem to be finding is loading assets at runtime. Which is a shame as I really liked the "old" way of doing it.
@loud matrix I think this is mostly a hybrid/transition issue. I'm sure there will be some better ways once there is some ECS integration with the editor
But you're right, it's an issue currently
I keep wanting to make a quick prefab out of cubes but its quicker to just make it in blender and export a propper mesh
Is this a good channel for DOTS networking? Thoughts on using ghosts for NPCs/synced AI?
just a quick question, if I use z as height instead of y, will I regret that later?
I mean will I often have to work against the system in some sense?
@sour ravine As with any network question... It depends on your needs. Generally, if your NPCs change their path a lot/is not on a predictable path, then you probably want to use ghosts. If they are predictable though, you might want to just sync whenever a change in their path happens.
it makes life especially easier for me since I often have 2d coordinates on my map and then at some points add the height,
newpos = int3(pos, height)
is much nicer than
newpos = int3(pox., height, pos.y)
@sour ravine I recommend starting with the ghosts (as you are probably already familiar with it), and if you need to cut down on bandwidth, then you can start looking into how to optimize the data like I described above
You may even be able to completely simulate the NPCs on clients with deterministic behavior, depending on your needs
But generally NetCode is made with server authority in mind at the moment
system is pretty innately event-driven FWIW but we would like to support direct player nav control and player possession of NPCs
(RPG)
not a bad case for the transport layer but it seems like I might be fighting the fundamental assumptions of ghosts and data architecture
Hmm, I'm not too familiar with NetCode yet to say what your best option for that is then. I'm fairly certain it should be possible to change the entities to be Client Predicted.
But yes, right now NetCode is built for FPS games, with generally 1 character tied to 1 player
But if you can figure out if you can change an entity from interpolated to predicted, then it's definitely possible to do what you want
You can of course also forego any prediction, and sync the player commands to the server, which are then executed, and then synced back to the player
the actual codegen stuff is actually a neat idea
low-level net replication is usually a brain melter
I'm not a fan of it
Surely it should be possible to have that code somewhere without devs having to press buttons to generate it
Also forces recompile
Maybe there are some pros to it, but I'm not smart enough to know those atm
alright, the Tiny Spaceship demo has the Camera converted in a subscene, yet doesn't render the scene unless unconverted. Is this how it's supposed to be?
currently having this problem ArgumentException: Unity.Collections.NativeArray`1[UnityEngine.Color32] used in native collection is not blittable, not primitive, or contains a type tagged as NativeContainer
I can confirm it converts to an entity with a Camera component on it, but no rendering
oh hah ok
basically you have to open the subscene for editing for it to work or something?
right
but then its MB camera which will not work in tiny build if I'm not mistaken
yeah, that is weird
I'd give it more time
it's first 2D version on this iteration π
like 3rd version so far but still
yeah cant expect much yet. just didnt see the forum post. though you'd think camera rendering would be one of the first things you'd address π€·ββοΈ
anyone?
you cannot use native containers in other native containers ^
they might have had it running internally but then disabled it before release if there were remaining issues
@odd cipher is Color32 blittable? might need to be float4?
I mean, I could totally see something like that happening
color32 should be. Unity.Collections.NativeArray`1[UnityEngine.Color32] used in native collection is referencing a native collection
I'm not sure.. I'm very new to the Job system and its very confusing to me :p
You also cant put native collections into struct component datas
You might want to check NativeMultiHashMap
Yeah I tried that aswell, but gives the same problem with Color32
might be an actual issue with the way Color32 is declared then
NativeMultiHashMap<int, Color32>
I mean, isn't Color32 a reference type?
it's a struct
probably not
did you use NativeMultiHashMap?
Tried both
I'm not sure if you tried NativeMultiHashMap with an array (which you should not do) or just with the Color32
OK, so it might be a problem with Color32 then
One penguin to another, imo try to take it easy if you are new to ecs/jobs, there are some tutorials on the internet that you can check out
this is the full line NativeMultiHashMap<int, NativeArray<Color32>> colors = new NativeMultiHashMap<int, NativeArray<Color32>>(tiles1D.Length, Allocator.Persistent);
you can use something like a uint4 from the mathematics package
still native container inside another. cant do
but that means it can only have one color no?
it's NativeMultiHashMap, not NativeHashMap
you can put the values in a temp array if you really really need them in that form
in your job, etc.
A NativeMultiHashMap is sort of like a 2D array, which you seem to want
You ujst need to handle it a bit differently
alright, well, next error :P UpdateTerrainJob.colors is not declared [ReadOnly] in a IJobParallelFor job. The container does not support parallel writing. Please use a more suitable container type.
the error message is, uh, pretty much correct there π¦
what are you trying to do here
are you only reading the colors array ? if so put [ReadOnly] attribute
it might actually make more sense to Dare to be Stupidβ’ and brute force with parallelism
I'm trying to make a IJobParallelFor that generates the colors for every "tile" in my 40,000 tile array
to make the process faster but im not really getting anywhere on that front
yeah, just make a big NativeArray<Color32> and Be Stupidβ’
IJobParallelFor uses an index into that array and does its thing
what is the key for
the Tile index
what's the meaning of/utility of that
sparse sets?
that's a parallelism antipattern
whatever that means
π
OK, fair π
so for lots of normal CS education, you're taught about skipping work and tricks for that
so that looks like you have a hash map set up where you only keep colors for certain tiles (probably hashing the X and Y?)
so parallel work (and jobs) have a different set of assumptions-- you want to do lots of the same work, even though the CS academics will scream at you about big-O efficiency and other brain rot
As I understood it, weren't you trying to create a large array where each 8x8 in that array was the same color?
no, each pixel in that 8x8 area is supposed to be different colors
Ah, right
because its terrain so
Yeah makes sense
yeah it makes more sense to think in terms of individual pixels in your terrain
a lot like shader invocations, in fact
I'm just wondering why isn't this just on a texture actually?
it is on a texture
You can do [NativeDisableParallelStuff] attribute on the array, and calculate 'local' index from the index you get from execute function, but not sure if that can help your situation
Hmm, it kind of sounds like you're trying to do work on a CPU that should really be done through a shader, no?
Hm, that's true.
I guess I just really am missing some context on what the end goal is here
it might make sense, but that ties into the 'what are you actually doing` question
Hehe π
I did not but I 'm quite literally trying everything
sort of- but it didn't work very well
Alright well, the problem was this Slice may not be used on a restricted range array
That was the recommended solution no?
Dare to be Stupidβ’
Yeah my system does work without a Job but I wanted to try and optimize it
π
I think I'm also a bit confused, but this is maybe just a general confusion about DOTS here and how to architect the jobs. How would you split up the jobs? Would you create a job for each single pixel, or each single tile here?
that's actually an interesting question in of itself
Right, but these can be split into either tiles or individual pixels.
That's what I'm wondering
the reason it works without a Job is because it knows what tile it is on; in this case it doesnt
I would actually say tile makes sense on account of cache line sharing
non-overlapping 8x8 pixels represent tiles yes
So in this case you would split the jobs into each tile?
lol
No each job should be a tile, right?
theres only one Job, which is a IJobParallel
Yeha okay, but each parallel version of the job should loop over a tlie
@bright sentinel scan lines are another popular choice in image processing
You just need to use a bit of math to derive your pixel position for the Execute index
@zenith wyvern I have to note, not really that good at math :)
you can actually tune the break threshold too
yeah, the number of iterations is the number of tiles
Googling will help you, there are plenty of resources on collapsing 2d arrays. It's a bit more complicated in your case since it's essentially a 2d array in a 2d array
I'm talking about how to structure the execute function of the parallel job. Should I do
setPixel[index] = someColorValue
Or should I
for (i = 0; i < 8*8; i++)
{
setPixel[index + i] = someColorValueDependantOni
}
You need to get which tile you're in from the index then derive which pixel of that tile you're in
Because I feel like the parallel could be run as both here
@bright sentinel option B very possibly
index + i doesnt work
it sure do
well
Why would it not work?
assuming index is an int and all that
It was more to get thoughts on how to architect jobs
I dont know why it wouldnt work
it just
this is what happens when I do that IndexOutOfRangeException: Index 13308 is out of restricted IJobParallelFor range [13296...13307] in ReadWriteBuffer.
Definitely
[NativeDisableParallelForRestriction] is necessary for that in any case, I think
otherwise the bounds checker will think (not without merit) you're stomping over other values
might be misunderstanding the attribute tho
If we go with option B of my pseudo code, then it is needed, as the index will only be as many as you have tiles, while the size of the NativeArray will need to be 256 times the amount of tiles
yeah, done that
In at work so I cant write an example for you right now. You need to do more research on collapsing 2d arrays so you really understand what that means and how it works
@digital scarab What are your thoughts on my different options? The first option will generally need to have the data more accessible beforehand, as each job would need some way to access the individual pixels value, while the second will to know which tile it is currently operating on.
It feels like the first option is more the DOTS way, but the second option improves "developability" a lot
At least it's easier to conceptualize
You would probably even need another job to lay out the data properly
Right sure, but that seems like it would require a lot of work done before entering the job, and sort of breaking the whole purpose for a job
Since it would need to know which pixels to operate on, and then you've already done the heavy work outside the job
Sorry if I'm not understanding exactly what you're saying or making myself clear, but the whole DOTS way of thinking is quite new to me
Sparse writes?
read start offset from a NativeArray<> and use the same 8x8 block logic
jobIndex turns into Offsets[jobIndex] where Offsets is your array of tiles in need of updating
could also be scan lines or any other non-pixel level of granularity
I kind of thought that the whole point of jobs was to split each execution into atoms, i.e. the smallest part available
But this kind of breaks that way of thinking, no?
the point of jobs is to be parallel, just on its own terms
sometimes being parallel beats being smart
sometimes being smart and then parallel beats being parallel
is he just trying to iterate all the blocks in a 2d grid?
p much
that's an embarassingly parallel problem anyway, but you're assuming every pixel needs touching
which may (but is not necessarily) be true
I'm just considering to go back to before all this Jobs stuff
I'm just confused as to when you should stop granularity within jobs. Because before all this it made sense to split jobs into its smallest part as possible, kind of like a fragment shader. But now I'm just confused
i dunno unless you're careful and really need to go parallel it can easily be slower.
smallest part possible is not a good idea either because you've got the scheduling overheads
SIMD parallelism likes small dumb scalar jobs but I don't know what Burst is capable of there
which I will class as extremely advanced topic
speaking of GPUs
Hm, DoD is confusing. That's all I've learned from all of this π
all software is terrible
Well, objects are easy to conceptualize
especially Javascript
yeah, nightmare material
Ghost stories told around a campfire: [] + {} != {} + []
Anyways, it's still been educational
Although I still can't quite wrap my mind around DoD
Feels like I'm missing a few puzzle pieces
(Sorry for hijacking the chat)
@bright sentinel DoD throws plenty of professional engineers for a loop-- case in point Mike Acton's GDC presentations on the topic.
Always liked John Carmack code, he's another really smart dude that figured this out before it had much of a name
So what's your argument for using a double for loop within the "execution" loop? Couldn't that also be in their own jobs?
@sour ravine That's good to know π
that's not quite what he's asking I think
it's a 'why this granularity' question
not indexing logic
I'll take a stab at it, then
there's a minimum amount of work involved with setting up a job
Even then you could put that into a job, by supplying it with which pixel coordinates to draw on and the color
like, at a very mechanical level you need to spend some cycles putting things in the worker thread queue
I think that's what the question is really about-- nested parallelism and what makes sense as a unit of work
jobs creating other jobs/mesh dependencies are actually kind of a parallelism holy grail
Amdahl's, etc.
Of course that's possible. I can set up a job that takes the pixel coordinates which will be unique, and input the color it should be set to as well
No I'm not :d
π
But what's stopping me from saying that each execution should read data about which pixel coordinates it should set and what color that should be?
But wouldn't that mean something like a blur algorithm isn't thread safe?
write hazards are always a thing
I'm not understanding what you're asking, then
topher is correct
That was a bit of a tangent though to the original problem. Let's park that for now.
What I'm not seeing in the problem though is how each pixel is dependent on each other
But in the example code you posted, the pixels didn't seem to be dependent on each other
Right, and I understand that part
yeah, so maybe I was kind of right. That's the embarassingly parallel bit. There's no actual interdependence between pixels, so scheduling it in the smallest, most parallel terms (i.e. most number of elements) makes sense
if you use a different algorithm that can impose ordering limits
What I'm trying to understand is why I wouldn't execute for each of those inner loops if I need to do some calculation on each pixel, compared to executing for each block. (And I mean
and that's what topher lumps into scheduling overhead
Yeah I think you get what I mean @sour ravine π
which is a broad and rather technical topic
yep, p sure they get it
Why would you do a block over individual pixels?
so I was right, this is a question about parallelism and scheduling overhead
why would you choose to use blocks over pixels when it can actually be done in more parallel fashion
that's the question being asked, I think
π This is getting comical haha
I'm sorry if I have been unclear here, but it's really cleared up a lot about parallelism at least. I get that overhead can be a problem if what you're doing in a job is very little work. This at least seems like a problem for the profiler. But it makes sense that you would try to atomize the job if you can
Just trying to explain my thinking helped me make it more clear
π€ crossing my fingers so hard right now that smelly is actually Acton trolling everyone π
if that's correct, just consider that there are some cycles involved with configuring the job scheduler. If you spend a certain number of them setting up a job, and processing an individual element takes about the same amount of cycles, there will come a point where you will have spent more cycles on job creation over actual element processing
this is disguised Amdahl's Law
Mike I love you if you're actually reading this
Big Mike
@bright sentinel https://en.wikipedia.org/wiki/Amdahl's_law
I have no idea what is going on in here anymore
I'm sorry for hijacking your problem π
I feel like I started something here
@sour ravine Yeah, I guess that helps explain it. I guess this helps to learn a bit more about DoD, but adds a lot of complexity when actually writing your jobs haha
just remember that any scheduling is going to happen on the main thread by design
it's not a wrong decision, but it is going to contribute to that parallel vs. serial factor
you can spend more time scheduling jobs then you get back running them on different processor cores at the same time
in other news; I did manage to render the world but some weird things are happening with the colors
not getting the correct color that is
I'm making a tool to make it easy to communicate between game objects and systems. I was wondering if the syntax is awkward or not.
Here are examples of what's in there yet:
_entityProxy = new EntityProxy("Name of entity used for communication");
_monologue = _entityProxy.CreateMessage<IMessageComponentData>()
.WithoutResponse<IMessageComponentData>();
_monologueState = _monologue.HasBeenRead();
_monologue.Send(_messageComponentData);
_dialogue = _entityProxy.CreateMessage<IMessageComponentData>()
.ExpectingResponse<IMessageComponentData>();
_dialogueState = _dialogue.HasBeenResponded();
_response = _dialogue.GetResponse();
_dialogue.Send(_messageComponentData);
@opaque ledge I know you've used an Hybrid method. What do you think?
And I was thinking to make a game object with Unity events, but I'm not really sure it's a good idea.
@digital scarab Thanks for that link, it helped make this; though a problem is with getting the correct color to be set at the end cs for (int _x = 0; _x < instance.terrainSpriteSize; _x++) { for (int _y = 0; _y < instance.terrainSpriteSize; _y++) { int pixelX = (x * instance.terrainSpriteSize) + _x; int pixelY = (y * instance.terrainSpriteSize) + _y; int j = pixelX * instance.terrainTextureHeight + pixelY; colors[j] = c[_x * _y]; } }
"c" is the locally created color array, that I show the process of since its pretty big
also ill probably have to fix that instance thing because it doesnt work with Burst. instance is a reference to a script that has a couple variables and functions so
x and y is the location of the tile
your global array indexing looks off no, hold on. There are some index issues
If I'm understanding correctly, colors is supposed to be the output?
yes
Then you should not write it in as knowing x and y, output something to colors[j], rather you should write it as knowing j, calculate x and y in reverse, output something to colors[j]
In that case j would be the index your job execute passes in.
The job walks through your colors array once and that's it, there's no x and y loops.
no.. index is the tile
yeah this is basically a blit routine
your strides are off, just thinking through the loops here
blit?
old-school image copy stuff
well, I cant really think of any way to do it better, because I want the index to be the current tile its rendering
no, c is the color array that it picked above that code
which isnt really related to this
I just need to change the end bit to get the correct pixel from the c array, because it currently doesnt
its not supposed to look like that
That's the disconnect between your way of thinking vs job's way of thinking.
probably going to need some more info then. Like I mentioned, this looks like a tile copy like what you'd actually see in an actual old-school 2D console game
Job walks through your output array once, with the index of that array, you operate on it.
The index is the index of the array not the index of the tile.
yeah and the array is the array of tiles
so
could calling functions that arent in the struct be the cause?
that would generally cause a compiler error, so it would not be my first guess
right, like it should be along the lines of c[_x * instance.terrainSpriteSize + _y] though you mentioned c might not be the same size as the tile is
assuming it is, in fact, a blit
c is filled with some colors that you copy to the final location
that is probably your issue then
Say if you have 10 tiles, are you trying to output an array of 10x8x8?
You're accessing x*y index from your source. What is that index supposed to be
^
Again, you need to actually understand how to convert a 2d index to a 1d index
Copy pasting is not going to help you
x and y is the position of the tile and the tile is from the 1D tile array with the index index
Because from my brief reading, it seems that you are trying to do
Let job go from 1 to 10, then I operate on each tile to generate the 8x8 for that tile
The right way should be
Let job go from 1 to 10x8x8, then I operate on each pixel
"X*Y" is a meaningless index.
that's why I've been asking for more information about the length of c and what it actually contains
might not be, but I'm assuming it's a sort of 'source sprite'
Yeah that's what I was saying, you are still thinking of tile index, when you should be thinking of the output array index.
you can even simplify that loop further if you're generating the tile in a local array first, just go one scan line at a time
hoist some terms up
so.. pretty much just make the loop for each pixel instead of tile? thats gonna mean a lot of changes to my existing code
Optimization will often lead to code rewriting
I mean, yeah true
not exactly, just moving int pixelX = (x * instance.terrainSpriteSize) + _x; out of the loop on y, that kind of thing.
You can do two nested loops, one over the tiles the inner one over the pixels. Which is exactly what topher posted
compiler is probably already doing it
It's the most sensible way, you just need to understand how to convert the 2d to 1d tile index then the 2d to 1d pixel index.
If your source tiles are the same size you may not even need to "2d loop" on the pixels. Just copy then 1-1 in a normal loop.
well, in other news: I did get it fully working but yeah, you guys know plenty of optimizations to it that I do want to do https://gyazo.com/719565962d813f656c752db20be38a66
was it colors[j] = c[_x * instance.terrainSpriteSize + _y];
no but close colors[j] = c[_x + instance.terrainSpriteSize * _y];
plus the change on this int j = pixelX + instance.terrainTextureHeight * pixelY;
excuse, that's right. I was striding on the wrong loop counter. So you actually did write a blit
well.. I mean it is faster than the version without Jobs
time for SIMD
SIMD?
reinterpret as uint4 and... probably run at the same speed since lol out of order execution
Same Instruction, Multiple Data
if you've heard of NEON or SSE, that kind of thing
no still not sure what you mean
Burst will insert appropriate stuff if you use vector types. Whether or not it's clever enough to manipulate the loop and autovectorize is a good question, but LLVM is damn clever
you do need to worry about memory bandwidth vs. instruction dispatch
currently my Job isnt really working with burst because of this main issue Burst error BC1042: The managed class type `WorldController` is not supported. Loading from a non-readonly static field `WorldController.instance` is not supported
so, id have to pass those variables I use through the Job correct?
pretty much. Just copying the ints you need is a good start
Yes, and that is very much preferred actually, instead of getting them from a static class
the Mike Acton way is to drop the class π
Just doing that to enable burst will give you massive performance increases
alright, what about functions I call? do I just.. copy paste them into the struct or what
consider caching the result if it's the same over the entire run of the Job
just whack it in a field
alright, one additional problem.. one of the variables I use is a Dictionary, is it possible to pass those?
Whos mike π
Invalids personal jesus
just dont use a dictionary, use a NativeHashMap instead
@sour ravine Why are you so obsessed with MActon? π€
Any specific video or response he gave that got you hooked?
internet comedy purposes, but real talk I do think the DOD stuff is a good way to approach compsci
well this is gonna be a problem
Blobs can still work
I dont know what those are so
BlobString for equality, that kind of thing
invalid has a big boner for mike
ask me about Chris Avellone π
blobs is for ECS right?
I'm going to regret this, but who is Chris Avellone?
Yep, for things that don't change a lot but have varying sizes and shapes
@bright sentinel a Very Important Writer
@odd cipher there are also NativeStringXX types which should be easier to work with
you can make them pretty easily from regular strings
alright thatll work then, but what about the Color32[] array thats in there
multimap per earlier discussion
oh I totally forgot
you can actually turn that into a NativeArray in the tile job if you want, but that's obviously going to take CPU time
iterate through values
park in the array
alright, so which NativeString do I use and how do you convert a string to it
how big are your string(s)
the number at the end is how many characters you can store in it
big strings are usually not a good idea for performance, though, so if you can use another way to identify tile groups consider cheating your way out of the problem that way
enums, the like
(I think they're value types in C# but I'm second-guessing myself)
yeah
enough to test things out and see what happens. Again, if you want to go DOTS then go DOTS. Figure out how to avoid the strings altogether
i think fixedstring is the replacement for nativestring now but ya it sounds like ideally you don't want to use strings here
yes, there are even format methods in there so you can generate them directly
TryGetFirstValue() -> TryGetNextValue()
you can probably even make an extension method if you're feeling fancy
put those in an array at the beginning of the job
you're doing repeat work at that point, but at least it's amortized per tile
I'm not sure if array slices would pose a problem but another strategy is to make a big list of all the Color32s for all the tile groups and then put region markers in the hash map
start index and length, that kind of thing
Trying to figure out how to set value array now
keep adding the elements to the map using the same key
you need to clear the key out first if you reset-- this is sounding like a good use case for BlobAssets but you know more about what you need the game to do
Alright, also noticed I need to pass the array of tiles as well.. and a tile is a non monobehaviour script so its gonna throw an error
as always, consider whether or not you need to actually touch that data
if there's just a property you need, gather it all up in reference-friendly C# land then kick off to the job system
pretty much what BlobAssetReference was built to do
"Next release will also have blob assets which is made for sharable immutable resource data. In that case, you could convert a ScriptableObject into a BlobAsset and then share it from multiple instances in the same scene.
BlobAssets are made for zero cost deserialization for large amounts of data. AnimationClip, CollisionMesh, CurveData is a good example of what we think belongs into a BlobAsset." Old forum post
So essentially a ScriptableObject in ECS land
bingo
I'm guessing we would also use this to store textures then?
you can recreate it as appropriate, but that construction can be expensive
Burst error BC1042: The managed class type `Tile[]` is not supported. Loading from a non-readonly static field `WorldController.tiles1D` is not supported
NativeArray would also work if you just need a big flat run of constant size stuff
@bright sentinel see BlobAssetBuilder
The problem is the docs are very sparse on BlobAssets unfortunately
yes, they're mostly just a way to bundle up some structs in a way that doesn't run afoul of Burst
ya and they are a pain to create
dont really know what to do to fix this since Burst doesnt like my Tiles
I've built a simple database out of them, it's actually not as bad as you'd expect
@odd cipher it's complaining about the managed array
if possible, move the data you need from each Tile into a struct and return a NativeArray<> holding that
alternately use a NativeList<> with the Tiles directly if that can be done
but again, this is really, really sounding like all the hard work of using a BlobAssetBuilder and going that route
I dont think any of that is possible
I check the tiles neighbor etc
such as if (t.NeighbourCheck(x, y + 1)) neighbourName += "N";
that's a tremendously inefficient design if you're working on strings
..maybe.. buttt I want to get this working first
You cant create strings in burst. What are you using the strings for? And why do you need a dictionary?
well the dictionary contains all the sprites for the terrain
with the string name of the sprite as the key
Change the key from a string to an enum
You should never use strings for anything except debug and ui
cant use an enum because that sprite list "changes"
in the sense you have variants?
not a problem for enums, FWIW. You can create values with the modifiers
adding new modifiers is a pain, but doesn't happen too often
the game is moddable
What do you mean it changes? The source data for your sprites changes?
the value can't sure, but depending on gameplay mechanics it might not actually change
consider minecraft-- while there are mods, there are very few that fundamentally change the kinds of blocks or ways they interact
hence it might still be possible to make the block type an enum
or just an int
or even bit flags
Well regardless of how you represent it outside the job, if you're passing it in to a bursted job so you need to restructure it to fit that requirement.
I would probably use ints as keys, use a static class of constants named appropriately to represent the keys
so theres no way to do this in the meantime without changing pretty much everything?
No shortcuts to performance
You should never use strings for anything except debug and ui
@zenith wyvern
i 100% agree but this reminded me of this other issue i was having
before DOTS i'd just dospriteAnimator.SetAnimation("fart");
i don't like using strings here but they are a lot friendlier to use than to check the animation id from the scriptableobject and doSetAnimation(5)
so was thinking of either
a) codegen an enum whenever the anim scriptableobjects change
b) do something like SetAnimation(animDict["fart"]) that returns an int
what would you do? am open to other ideas as well
zoidberg why not enums
i could start changing the strings to int but the problem itself will still exist; that I cant use Tiles in the loop
I have some old style unity cubes in my scene mixed with ECS converted cubes, both have the same material, yet they are lit differently. why? I changed the lighting settings but only the old cubes are taking my new light settings, the ECS cubes render as if they use the default unity lighting settings
@sour ravine @zenith wyvern ^^
you can, you just need a way to turn it into a flat NativeArray of structs
so, whats the point of changing the strings to ints currently? I mean sure I can do it later but I don't NEED to do it right now
Ah crap
Kids and their fart animations grumble grumble
aandd I just accidentally erased all the stuff I did for it to semi work with Burst.. I dont think I'll be able to make it work either way because of the Tiles :<
Quick question on timing when dealing with component systems. In monobehaviour when dealing with animations, we could simply have actions get triggered when a certain frame hits, in terms of ECS (specially in multiplayer NetCode server authoritative), is there a best practice for that? Should the server have a ability system that tracks time? Or should the server inject the GameObjects and trigger the same animations and wait for feedback from the monobehaviour script?
@sour ravine So, I took your advice and changed it from using the name of the terrain type to id of the terrain type and using enums instead of strings. all this made the world generation take 100MS instead of 200MS :DD
yeah I dont remember what that is
jobs
oh right
yeah I am still using the semi working Job code :P
I do really want to use Burst though
since it can give huge performance boosts
What is it about your tiles that prevents you from using it in Burst?
due to this Burst error BC1042: The managed class type `Tile[]` is not supported. Loading from a non-readonly static field `WorldController.tiles1D` is not supported
So change your tile array to a NativeArray<Tile>
thats it? oh.. well then, ill try
Your tile also needs to be blittable, so it has to be a struct with only blittable types
well that wont really be possible with my current system
Then you need to decide what you're using your tiles for in your job and extract that out into a NativeArray that you pass into the job
With burst it's all about how to get your data into that nice linear nativearray
@zenith wyvern
i 100% agree but this reminded me of this other issue i was having
before DOTS i'd just dospriteAnimator.SetAnimation("fart");
i don't like using strings here but they are a lot friendlier to use than to check the animation id from the scriptableobject and doSetAnimation(5)so was thinking of either
a) codegen an enum whenever the anim scriptableobjects change
b) do something likeSetAnimation(animDict["fart"])that returns an int
what would you do? am open to other ideas as well
@hollow sorrel
It's been a while since I've used Unity's animation system. If I remember right Unity uses strings too, but they hash them to ids anywhere they don't need to be human readable
oh no i'm not using unity's anim system
oh i didn't read the second part
yea i like the hashing, is simpler than codegen
I would avoid codegen like the plague unless you're having some kind of serious performance issue
would be nice to have enums tho since you can just autocomplete and see what anims there are without leaving ide
why would you avoid it
The results are fine, it's the work to get it there and what it does to your code readability that's the problem
how would you generate a random number between 0 and 2 in a Job? nothing really seems to work for me
ah, fair
i agree and i'll go for hashing then, ty
how would you generate a random number between 0 and 2 in a Job? nothing really seems to work for me
@odd cipher
Google "Unity rng in jobs" there are plenty of resources to learn from
The short answer is use the "Random" struct from Unity.Mathematics
But it's a complicated subject that requires research to get right
Yeah I tried that but it seems off.. ```cs
var random = new Unity.Mathematics.Random((uint)index + 1);
if (random.NextInt(0, 2) == 1) { }
it doesnt look "random"
@covert raven I would've liked to help, but I don't know and am curious myself. You might want to ask on the Unity forums
RNGs are not meant to be dropped in place like that. They're persistent objects. You get random from repeatedly calling NextInt on the same Random
But since Random is value type you can't just pass it into the job and get it back out, you need to put it in a native array, or pre-generate your random numbers
Like I said, a complicated subject
The quick and dirty solution is to create your Random on the main thread, seed it with UnityEngine.Random.Range() then pass it into the job and use it throughout.
You could probably put a random in a scriptable object, make it addressable and retrieve the addressable in your job.
Just kidding, but if you are working in Hybrid ECS, you can use a MonoBehaviour to randomize your numbers and send it to the ECS world as an entity component.
When the component is read, a tag component is added to the entity that holds the component, which is found by the MonoBehaviour, who then sends a new value and removes the "used" tag.
Or you could just use my messaging tool when it's ready π π
Talking about messaging tool, I posted that earlier, but didn't get any response yet:
I'm making a tool to make it easy to communicate between game objects and systems. I was wondering if the syntax is awkward or not.
Here are examples of what's in there yet:// Creates a proxy using an entity. _entityProxy = new EntityProxy("Name of entity used for communication"); // Creates a message that doesn't expect a response from any system. _monologue = _entityProxy.CreateMessage<IMessageComponentData>() .WithoutResponse<IMessageComponentData>(); // Checks if the message has been read. _monologueState = _monologue.HasBeenRead(); // Sends the message. _monologue.Send(_messageComponentData); // Creates a message that expects a response from a system. _dialogue = _entityProxy.CreateMessage<IMessageComponentData>() .ExpectingResponse<IMessageComponentData>(); // Checks if the message has been responded. _dialogueState = _dialogue.HasBeenResponded(); // Gets the response from a system. _response = _dialogue.GetResponse(); // Sends the message. _dialogue.Send(_messageComponentData);
That's only the code MonoBehaviours can use. I'm not finished with the part where a system can retrieve the message.
Is the syntax easily understandable, or is it awkward?
how are you making dialogue get a response before its sent?
also maybe you could omit the withoutresponse part if its only with or without
if expectingresponse is not included then without is implied right
It's just a list of all the things you can do right now, not necessarly in order.
ah i see
And usually in an update I would check if a response was received at the beginning of the update. And if I also send a message in the same update function, I would put it after, so it doesn't override the last message before it is received.
I don't know if I should've made it so it would work fine if there was two of the same message, but that would mean I would have to either create and delete entities, or make a pool or something. Which I thought would get messy for no reason.
But if you think it would be better that way, I might change my mind on that.
@hollow sorrel And for the WithoutResponse thing, if I omitted it, that would mean I could do .ExpectingResponse on the monologue whenever I want, and I thought that would be awkward.
But maybe it wouldn't be that awkward.
I mean we can basically do Entities.WithBurst().ForEach().WithBurst().WithoutBurst().Run() and it still works, but I find that weird myself.
Or I could do _proxy.Message().ExpectingResponse().Create() and _proxy.Message().Create(), but I don't know if that's also weird or not.
@zenith wyvern Hey, hope I'm not annoying you but I'd like to thank you for helping me a lot with this even though I had no idea what I was doing. I would ping InvalidPointer aswell but they are offline so I don't really wanna ping them.
No worries, happy to help
@plush portal i never tried to implement hybrid event system π i know Tertle did some ECS events using native containers you might want to check them out
as for your syntax, it seems cool
you missed initing the random is why its not random
var r = new Unity.Mathematics.Random();
r.InitState();```
So are there any cases where you don't want it initialized?
hello! I have a question, somebody told me a while ago that allocating and writing to a NativeArray more than 12mb in size in a burstjob would crash the stack or something, but it appears it doesn't (in fact my array I'm testing it with is 500mb+), why is this the case?
ahh so is it because I use Allocator.Persistent which allocates to the heap?
i think in their documentation it says its basically malloc function if you use persistent
yes it appears so, thank you!
oh so I know what the misunderstanding was, I said that I was going to use the array as a temporary one, but it actually gets re-used millions of times
i am getting this weird error:
Burst error BC0101: Unexpected error while processing function `TradeFleetAvoidEnemeyAITagRequest.<>c__DisplayClass_TradeFleetAvoidEnemyRequest.OriginalLambdaBody(TradeFleetAvoidEnemeyAITagRequest.<>c__DisplayClass_TradeFleetAvoidEnemyRequest* this, Unity.Entities.Entity entity, ref Unity.Entities.DynamicBuffer`1<AITagRequestBuffer> requests, ref TradeFleetAvoidEnemeyStateData state, ref Unity.Entities.DynamicBuffer`1<RaycastResult> results)`: System.ArgumentNullException: Value cannot be null.
Parameter name: key
at System.Collections.Generic.Dictionary`2[TKey,TValue].FindEntry (TKey key) [0x00008] in <437ba245d8404784b9fbab9b439ac908>:0
at System.Collections.Generic.Dictionary`2[TKey,TValue].TryGetValue (TKey key, TValue& value) [0x00000] in <437ba245d8404784b9fbab9b439ac908>:0
at Burst.Compiler.IL.Syntax.ILBuilder.CreateBasicBlockForInstruction (Mono.Cecil.Cil.Instruction inst, System.Boolean isEntry) [0x00000] in <d44e69c6e62c40a48187c633e22e4133>:0
at Burst.Compiler.IL.Syntax.ILBuilder.PrepareFunction () [0x001fc] in <d44e69c6e62c40a48187c633e22e4133>:0
at Burst.Compiler.IL.Syntax.ILBuilder.ProcessFunctionBody () [0x00084] in <d44e69c6e62c40a48187c633e22e4133>:0
at Burst.Compiler.IL.Syntax.ILBuilder.CreateFunctionFromRef (Burst.Compiler.IL.Syntax.ILFunctionReference funcRef) [0x000fa] in <d44e69c6e62c40a48187c633e22e4133>:0
at Burst.Compiler.IL.Syntax.ILBuilder.VisitPendingFunctionReferences () [0x000b2] in <d44e69c6e62c40a48187c633e22e4133>:0
And this error repeating:
nvalidProgramException: Invalid IL code in TradeFleetAvoidEnemeyAITagRequest/<>c__DisplayClass_TradeFleetAvoidEnemyRequest:OriginalLambdaBody (Unity.Entities.Entity,Unity.Entities.DynamicBuffer`1<AITagRequestBuffer>&,TradeFleetAvoidEnemeyStateData&,Unity.Entities.DynamicBuffer`1<RaycastResult>&): IL_004b: br.s IL_ffffffdf
This error happens at this
var relation = Shared_Cooperations.GetRelation(ownerData, targetOwnerData);
function is:
public static CooperationRelationValue GetRelation(Guid host, Guid target)
{
if (host == target) return CooperationRelationValue.Ally;
var relation = relations.Data;
var enumarator = relation.GetValuesForKey(host);
while (enumarator.MoveNext())
{
var data = enumarator.Current;
if (data.Equals(target)) return data.relation;
}
return CooperationRelationValue.None;
}
relations.Data is shared static
The thing is i am using this function at 2 seperate places, one for pirate AI one for trade fleet AI, pirate one doesnt give me any errors and works properly, trade fleet one doesnt
it works if i do WithoutBurst
oh this only started to happen after i upgraded to 2020.1b
it works now π€·
@loud matrix This is what I was referring to when I recommended statics: ```using Unity.Entities;
using Unity.Transforms;
public static class MyGameArchetypes
{
public static EntityArchetype PlayerArchetype => World.DefaultGameObjectInjectionWorld.EntityManager.CreateArchetype(new ComponentType[] {
typeof(Translation),
typeof(Rotation),
typeof(Player)
});
public static EntityArchetype EnemyArchetype => World.DefaultGameObjectInjectionWorld.EntityManager.CreateArchetype(new ComponentType[] {
typeof(Translation),
typeof(Rotation),
typeof(Enemy)
});
}```
public class EntitySpawnerSystem : SystemBase
{
protected override void OnCreate()
{
World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(MyGameArchetypes.PlayerArchetype);
for(int i = 0; i < 50; i++)
{
World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(MyGameArchetypes.EnemyArchetype);
}
}
protected override void OnUpdate() { }
}```
I don't think you'd run into the issues with fast reloading/assembly that Rett described because the statics are stateless and don't need to be initialized. Wouldn't be the same case if you needed to create the archetype definition at runtime however.
@worldly pulsar correct me if I'm wrong
Entities 0.8 is up π
## [0.8.0] - 2020-03-13
### Added
* Added missing dynamic component version API: `ArchetypeChunk.GetComponentVersion(ArchetypeChunkComponentTypeDynamic)`
* Added missing dynamic component has API: `ArchetypeChunk.Has(ArchetypeChunkComponentTypeDynamic)`
* `EntityArchetype` didn't expose whether it was Prefab or not. Added bool `EntityArchetype.Prefab`. This is needed for meta entity queries, because meta entity queries don't avoid Prefabs.
* Added Build Configurations and Build Pipelines for Linux
* LiveLink now gives an error if a LiveLink player attempts to connect to the wrong Editor, and advises the user on how to correct this.
### Changed
* Optimized `ArchetypeChunkComponentTypeDynamic` memory layout. 48->40 bytes.
* LiveLink: Editor no longer freezes when sending LiveLink assets to a LiveLinked player.
* LiveLink: No longer includes every Asset from builtin_extra to depend on a single Asset, and sends only what is used. This massively speeds up the first-time LiveLink to a Player.
* Upgraded Burst to fix multiple issues and introduced native debugging feature.
### Deprecated
* Types that implement `IJobForEach` interfaces have been deprecated. Use `IJobChunk` and `Entities.ForEach` for these jobs.
### Fixed
* Fixed LiveLinking with SubScene Sections indices that were not contiguous (0, 1, 2..). Now works with whatever index you use.
* Fixed warning when live converting disabled GameObjects.
* Allow usage of `Entities.WithReadOnly`, `Entities.WithDeallocateOnJobCompletion`, `Entities.WithNativeDisableContainerSafetyRestriction`, and `Entities.WithNativeDisableParallelForRestriction` on types that contain valid NativeContainers.
no idea π
probably something related to IJobChunk
also F for IJobForEach, he will be remembered π«
Wait hold up ijobforeach deprecated? π©
@safe lintel welcome to the club buddy
hmm I guess it's not too difficult to switch IJobForEach to the fluent API - well I should go change that sometime soon in my stuff
I love it, it hides so much boilerplate
I quite liked the format of ijobforeach, switching is a hassle but not my main source of contention
im lazy - so I didnt wanna change it when I had the chance lool
Well with IJobForeach you can follow solid, now your Update method is a bunch of logic
With anonymous methods in it
Even lets say if you do not inline it - you pay method call cost because Mono never inlines aggressively
For collections 0.7
# [0.7.0] - 2020-03-13
### Added
* Added ability to dispose NativeKeyValueArrays from job (DisposeJob).
* Added `NativeQueue<T>.ToArray` to copy a native queue to an array efficiently
### Changed
* Upgraded Burst to fix multiple issues and introduced a native debugging feature.
### Deprecated
* Deprecated `Length` property from `NativeHashMap`, `UnsafeHashMap`, `NativeMultiHashMap`,
`UnsafeMultiHashMap`, `NativeQueue`, and replaced it with `Count()` to reflect that there
is computation being done.
### Removed
* Removed expired API `CollectionHelper.CeilPow2()`
* Removed expired API `CollectionHelper.lzcnt()`
* Removed expired API `struct ResizableArray64Byte<T>`
### Fixed
* Removed code that made `NativeStream` incompatible with Burst.
Hybrid Renderer 0.4
### Added (All Versions)
* HeapAllocator: Offset allocator for sub-allocating resources such as NativeArrays or ComputeBuffers.
### Added (Hybrid V2)
Hybrid Renderer V2 is a new experimental renderer. It has a significantly higher performance and better feature set compared to the existing hybrid renderer. However, it is not yet confirmed to work on all platforms. To enable Hybrid Renderer V2, use the `ENABLE_HYBRID_RENDERER_V2` define in the Project Settings.
* HybridHDRPSamples Project for sample Scenes, unit tests and graphics tests.
* HybridURPSamples Project for sample Scenes, unit tests and graphics tests.
* MaterialOverride component: User friendly way to configure material overrides for shader properties.
* MaterialOverrideAsset: MaterialOverride asset for configuring general material overrides tied to a shader.
* SparseUploader: Delta update ECS data on GPU ComputeBuffer.
* Support for Unity built-in material properties: See BuiltinMaterialProperties directory for all IComponentData structs.
* Support for HDRP material properties: See HDRPMaterialProperties directory for all IComponentData structs.
* Support for URP material properties: See URPMaterialProperties directory for all IComponentData structs.
* New API (2020.1) to directly write to ComputeBuffer from parallel Burst jobs.
* New API (2020.1) to render Hybrid V2 batches though optimized SRP Batcher backend.
### Changes (Hybrid V2)
* Full rewrite of RenderMeshSystemV2 and InstancedRenderMeshBatchGroup. New code is located at `HybridV2RenderSystem.cs`.
* Partial rewrite of culling. Now all culling code is located at `HybridV2Culling.cs`.
* Hybrid Renderer and culling no longer use hash maps or IJobNativeMultiHashMapVisitKeyMutableValue jobs. Chunk components and chunk/forEach jobs are used instead.
* Batch setup and update now runs in parallel Burst jobs. Huge performance benefit.
* GPU persistent data model. ComputeBuffer to store persistent data on GPU side. Use `chunk.DidChange<T>` to delta update only changed data. Huge performance benefit.
* Per-instance shader constants are no longer setup to constant buffers for each viewport. This makes HDRP script main thread cost significantly smaller and saves significant amount of CPU time in render thread.
### Fixed
* Fixed culling issues (disappearing entities) 8000+ meters away from origin.
* Fixes to solve chunk fragmentation issues with ChunkWorldRenderBounds and other chunk components. Some changes were already included in 0.3.4 package, but not documented.
* Removed unnecessary reference to Unity.RenderPipelines.HighDefinition.Runtime from asmdef.
* Fixed uninitialized data issues causing flickering on some graphics backends (2020.1).
### Misc
* Highlighting `RenderBounds` component change introduced in `0.3.4-preview.24` which was not part of the previous changelogs, see below.
π€© hybrid v2 soothes the pain of losing ijobforeach
Is there a reason IJobForEach is removed?
Is it just a case of always wanting to do IJobChunk instead?
Well basically ForEach lambda and IJobForEach is doing the same, they decided to support the lambda way
But isn't the lambda generating a job?
wait, ComputeBuffer write from job
as a graphics programmer YEEEEES
the dark ascent begins
that's yuuuge
Techincally yeah Bmandk
enjoy InvalidPointer π i simply dont know whats going on with hybrid renderer
is it beneficitial to us peasants ?
some coders are put off by boilerplate code, so it makes sense to not promote IJobForEach, especially after all the work they have done to optimize ForEach. However I find the pattern still awkward when you need to iterate over entitites of separate archetypes
i see many "huge performance increase" so there must be π
@opaque ledge you can do whole categories of efficient algorithm now
clustered shading on CPU, etc.
GPUs can choke on cluster construction a bit while CPUs have no issue
I'm just wondering if there was ever a reason to use IJobForEach over IJobChunk
ForEach eats components, Chunk eats ArchetypeChunks
yeah basically π
but i am on the same boat, i never used IJobForChunk, i always used ForEach
Hyped for Hybrid V2, this looks very promising!
this is cool since you can do really bonkers instanced rendering, etc.
probably where the perf benefits come from
IJobChunk is less intuitive for sure
But I mean, deprecation of IJobForEach means that it's going away at some point. That would mean that Entities.ForEach() would be invalidated (which is obviously not happening)
ijobchunk is crazy useful tho
being able to move some branching to the per-chunk level is great
So I'm just wondering if the lambda uses an IJobChunk or IJobForEach underneath
@bright sentinel the struct approach is being deprecated in favor of the lambda
Yes, IJobChunk is used when you do ForEach
I wouldn't be surprised if the code is similar to what IJobChunk does
Thanks @opaque ledge
IJobChunk can be more efficient because you can access the chunk data as how its laid out in memory, at least thats what their doc says
Yeah, so I doubt the lambda uses IJobForEach, considering the lambda will always work on chunks of archetypes
any ForEach can be written from/in a Chunk
it isn't a functionality thing, just boilerplate removal
common use case, etc.
dont forget to add "ENABLE_HYBRID_RENDERER_V2" in project settings for new hybrid renderer btw
@opaque ledge I have used them only once but with ArchetypeChunk is easier to iterate over archetypes of different entities
speaking of the lambda foreach...anyone have an idea of how to pass a ptr to the foreach statement?
less intuitive, but less awkward too
you dont pass anything to ForEach lambda, you have to capture it locally
yeah was gonna say p sure it's a regular capture
cool - I think I had an error statement saying I had to use a function like CaptureVariable... or something like that before - but I'll check again - haven't touched it in a week
you might need to assign it to a local as a hack or something
ah - alright I'll give it a try later today
@fallow mason Thanks, I got it working but that's a cleaner way of doing it, I was using a constructor class to make a single instance of the Entity Manager
wooo hrv2!
How should i disable gravity on spesific rigidbodies?
Anyone up for a code review? I've created a Job to remove all the enemies that enter a trigger but not sure if I'm doing it 'right' https://github.com/yaustar/PlanetaryDefenceDots/commit/247fa350c15c08976bb85e3588b867224acab63b#diff-346d25ea5525897cb358cf0ccf7f03deR13
hmm, when i activate v2 it gives me an error in package, i guess i have to stick with old one π¦
Also not sure why planetEntities need to be read only (error given by the runtime) Is it because it is the entity that has the trigger component?
@opaque ledge Are you on 2020.1?
yeah
Β―_(γ)_/Β―
π
how stable is 2020.1 for people now? I've been holding off on upgrading to it, and will probably wait a little longer yet
@opaque pilot i think you need to do trigger events BEFORE EndFramePhysics and AFTER StepPhysics
i keep getting some weird ass error in 2020.1 π
so wait a while π
go for f:s x)
meanwhile I just downloaded it and abt to jump to it π
I'm getting a tonne of errors in 2019.3.4 so I'm trying out 2020
Installed physics package and everything decided it wasn't compatible, and it refuses to let me downgrade
@opaque pilot you also probably want to readonly both components and pass "true" to GetComponentDataFromEntity
@opaque ledge Ah, really? I'm partially copying the Unity example
Is there a reason for it need to be read only:?
lol a new entities package again
