I have run into a strange issue and I’m not sure why this is happening. Basically I have just finished implementing ECS A* pathfinding into my project and the screen freezes during the frame a path is requested in.
From looking at the Profiler it seems to me as the IJobChunk pathfinding system is running on both the main thread and worker thread 0 (correct me if I’m wrong, I’m fairly new to ECS) which is quiet confusing for me. Any Help would be greatly appreciated guys.
the main thread is waiting for the job to complete. it means that you either are calling .Complete() immediately (don’t do it) or some other system needs the data. maybe you are accessing that data with GetComponentArray() or similar on the main thread after scheduling?
Don’t call complete when you schedule the job because that will make the main thread wait for the job to complete.
Are you returning the handle for your job in the scheduling code? From what I understand this will lead to your job being completed in the same frame.
I have a system that loads data asynchronously using the Addressables package. I store the async operation handle in a lookup table in the system and wait for the async operation to complete (by polling each async operation handle on update checking its status).
A similar approach could be used in your pathfinding system (assuming it doesn’t need access to data that might change accross frames).
All that happens really is that when units are selected and I right click on the terrain a ComponentSystem sets the path request component for all entities that are selected. Then the PathfindingSystem does the calculations and once Its complete it sets the way points in a IElementBufferData for each of the units that was selected. So Nothing is really depends on the PathfindingSystem other than the system that makes the entities walk along the way points in the buffer which is populated by the PathfindingSystem.
Generally yeah sure maybe there is some optimization that can be done for how the path is calculated which i will look into at some point but I feel like your indicating that there is a way to make the main thread not wait for the job to finish(It really doesn’t need to be done in 1 frame) by calling Complete() in a certain way. If that is the case would you mind sharing how to do it with me as I cant find anything online?
Unless something changed, as long as the job is independent (not scheduled with any dependency) it will run over frames and you can check the status with .IsComplete (or something) and if that’s true you call .complete() and schedule new job
If you remove the line jobHandle.Complete() your job will still complete in the same frame because you are returning it as a dependency.
In order to get the job to run across multiple frames you should return inputDeps and save your job handle in a lookup table in your system.
Here is a description of how I handle aync operations.
In my system that uses the Addressables system to load data asynchronously I use a series of queries to accomplish this. I have the following:
A query that finds entities that need data to be loaded.
A query that finds waiting entities.
A query that cleans up requests from entities that were destroyed while waiting.
With the first query I schedule the async operation to retrieve the data using Addressables. I store the async operation handle in a lookup table with a unique key. I add a component to each entity found by the query that stores the unique key and marks them as waiting.
With the second query I check to see if any async operation has completed. If it has I apply the data retrieved to any entity found by the query with the matching key and remove the waiting component.
My system is a component system (doesn’t run jobs, but async operations instead) because I make calls to addressables on the main thread, but the concepts between using an async operation and an async job are very similar.
If I do it like this iscomplete never returns true. my screen still freezes during the frame the path request comes in and I can also no longer see anything about the pathfinding job in the profiler. the system works pretty much just like it did before.
You did not understand my post, it’s essentially the same as desert ghost (he explained it better)
if I recall correctly (did this long ago and I think there is a post somewhere, where Joachim replied) this has the caveat that you should not go wide with long running jobs as they might block all cores. Unfortunately ijob chunk has no schedule single, unless this changed…
Any job that touches Entity data in chunks has to be completed within a frame-span (it is allowed to wrap around). So any IJobForEach or IJobChunk is out of the question if you want the job to be long-running. If you want that data in a long-running job, you have to copy it out into NativeArrays. (An exception to this rule exists if you are using multiple worlds.) So I guess the real question here is: Do you want this to be a long-running single-threaded job, or do you want to try to optimize this to make it finish in a frame going wide? The latter is definitely doable if you aren’t using Burst but are willing to try it.
No. If system haven’t any dependencies to other system (which call complete, etc.), then this system call own dependency chain complete before update in next frame, this is how it works. In current frame after update it will change m_LastSystemVersion, ScheduleBatchedJobs and adds dependency to chain. And only right before next onupdate call it will call stuff for incrementing version, setting changed queries
// We need to wait on all previous frame dependencies, otherwise it is possible that we create infinitely long dependency chains
// without anyone ever waiting on it
m_PreviousFrameDependency.Complete();
Alright so long story short allowing the system to run for more than 1 frame is not possible if I want to stick to the ECS approach. Optimization it is!! haha
This poses a real challenge as I’m trying to achieve something similar to Stronghold 2 where you can build your own castle and you have entitles going from building to building going about their business, collecting resources etc which would be automatic and also the ability to create soldiers control them and fight as well. As you can imagine that’s a lot of path requests. I have tried IJobForEachWithEntity for the pathfinding system but buffers don’t allow parallel writing so it has to be a IJobChunk.
I have a few ideas on what I can do to optimize it.
Sending only 1 path request when more than 1 entity is selected and making the other units position themselves accordingly to the leader entity.
Splitting the grid into smaller grids and calculating the path bit by bit(No idea on how to go about doing something like this but hopefully after a good long think ill come up with something).
cache paths for the civilian entities and only updating them when a building is moved or new one is placed.(unless a new building is placed or moved their path from lets say the stock house to the lumber mill shouldn’t need to change )
I’ve got a imitation limit set up which is currently set to 100,000 which essentially for now caps how long the path can be. I’m thinking that perhaps I could lower the limit and every time it is reached I can make the entity go to the position the path finding system has managed to get to within that limit and then calculate the path again and again until the final destination is reached.(again finding it very hard to visualise how im going to do something like this with IJobChunk).
This is an RTS game and the map will be rather large so the pathfinding is probably the biggest challenge that I will face.
Has anyone here done anything similar before?
Thanks for all your help guys, keep up the good work!
If yes, read my thread from page 2, where I explain a bit how we doing path finding (with link to white papers) https://discussions.unity.com/t/716170 page-2
You may want to check out the experimental NavMeshQuery API. It can be used inside of jobs, but it is experimental and could be removed in a future version of Unity.