-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Task Driver - Improve Dependency Resolution and Fulfillment in the Job #224
Comments
I'm not sure we want to do this. If we do, we would have to introduce the concept of an ID for each DataStream. Which could be user provided: StartDataStream = CreateDriverDataStream<FollowPathStart>(IDs.FOLLOW_PATH_START); Or generated and returned: //Could return id and out the stream instead or you just get it off the stream
StartDataStream = CreateDriverDataStream<FollowPathStart>(out uint id); But then we would have to be explicit on all the usages of that in job scheduling: actorMoveTaskDriver.ConfigureDriverJobTriggeredBy(
IDs.FOLLOW_PATH_START,
StartJob.Schedule,
BatchStrategy.MaximizeChunk)
.RequireEntityPersistentDataForRead(FollowPathBufferEntityPersistentData)
.RequireEntityPersistentDataForWrite(FollowPathStatusEntityPersistentData)
.RequireDataStreamForWrite(actorMoveTaskDriver.GetDataStreamByID(IDs.MOVE_START)); Which might be fine but we lose out on some direct referencing and type safety. (Although maybe we could make that nicer by having the ID be contained inside the DataStream so you still direct reference but then pass in an ID to get an inner sibling collection) But... the main concern is that Unity itself only keys on Type for all of ECS. If you need to have multiples of a type, then you wrap the struct in a different struct type which is what we can do as well for TaskDrivers. Which is annoying, I agree, but just raising the question of whether we should do this or not? |
I'm not sure about that last code block there. Why would we need the IDs? We have reference to the actual data stream instance. It's possible that the IDs are completely internal to TaskDriver so I can create two data streams of the same type and behind the scenes they resolve to their individual IDs. I suppose there would be a limitation on the job side when you try to fetch the instance through the jobdata instance. It could work so long as you only want one of the streams in your job. What if we provided the task driver instance in the job's constructor? Then you can fetch using the stream instance directly.
Just so I understand, is this a consistency with ECS concern or a limitation of the safety system and the way we manage job handles with Task Drivers? |
Hmmn, yeah that's interesting. I could see that working. I wonder if you even need the require/get flow then... The whole point before was that you needed to say what data you would need so we could make sure we got all the dependencies in order for you, then construct your job and give you an easy way to grab the data for your job and catch any issues where you hadn't requested access for it. And then we schedule. But if we give the typed TaskDriver instance to your job, then the constructor is still main thread land before scheduling.
Instead of this: private StartJob(DataStreamJobData<FollowPathStart> jobData)
{
m_PathBuffers = jobData.GetEntityPersistentDataReader<FollowPathBuffer>();
m_FollowPathStatusWriter = jobData.GetEntityPersistentDataWriter<FollowPathStatus>();
m_ActorMoveWriter = jobData.GetDataStreamWriter<ActorMove>();
m_VerboseLogger = new SXVerboseBurstableLogger<FixedString64Bytes>(typeof(StartJob).ToMessagePrefix());
} You'd get this: private StartJob(FollowPathTaskDriver taskDriver)
{
m_PathBuffers = taskDriver.RequireForRead(taskDriver.FollowPathBufferEntityPersistentData);
m_FollowPathStatusWriter = taskDriver.RequireForWrite(taskDriver.FollowPathStatusEntityPersistentData);
m_ActorMoveWriter = taskDriver.RequireForWrite(taskDriver.MoveDataStream);
m_VerboseLogger = new SXVerboseBurstableLogger<FixedString64Bytes>(typeof(StartJob).ToMessagePrefix());
} That would eliminate the need for the method chaining require blocks actorMoveTaskDriver.ConfigureDriverJobTriggeredBy(
StartDataStream,
StartJob.Schedule,
BatchStrategy.MaximizeChunk)
.RequireEntityPersistentDataForRead(FollowPathBufferEntityPersistentData)
.RequireEntityPersistentDataForWrite(FollowPathStatusEntityPersistentData)
.RequireDataStreamForWrite(actorMoveTaskDriver.MoveDataStream); Becomes: actorMoveTaskDriver.ConfigureDriverJobTriggeredBy(
StartDataStream,
StartJob.Schedule,
BatchStrategy.MaximizeChunk); We would probably want to make sure that API wise we've guarded against getting the end data without going through a special This would be an annoying bug to track down since it MIGHT still work and just be a race condition: private StartJob(FollowPathTaskDriver taskDriver)
{
m_PathBuffers = taskDriver.RequireForRead(taskDriver.FollowPathBufferEntityPersistentData);
m_FollowPathStatusWriter = taskDriver.RequireForWrite(taskDriver.FollowPathStatusEntityPersistentData);
//OOPSIE DOOPSIE
m_ActorMoveWriter = taskDriver.MoveDataStream.AcquireWriterAsync();
m_VerboseLogger = new SXVerboseBurstableLogger<FixedString64Bytes>(typeof(StartJob).ToMessagePrefix());
}
Yeah just a consistency with ECS and a "if they restrict that way, what do they know that we don't." However in seeing your ideas above and thinking more about it, I've switched sides and I'm for this idea now. |
Nice, yea that's interesting. Maybe a bit more refinement on how exactly you get references in the constructor but this is an interesting direction to pursue some day. Maybe we let people do the actual acquire in the constructor? So long as they get the job handles out the caller of the constructor we'd be fine right? Rather than "Require" it could be something like: taskDriver.AddDependency(MoveDataStream.AcquireWriterAsync(out m_ActorMoveWriter)); or even a required out param on the constructor that was some sort of dependency collection instance. |
Yeah I think we're saying the same thing: My example of: Would do what you said under the hood: Names of course all being subject to change for better clarity etc. The Taskdriver internally would already know about the Dependency collection for the job it was scheduling because it just instantiated you and is about to call schedule on it. So we don't need the developer to be explicit about it, we can handle it for them. The explicit |
oh I see. Yea good point about the auto handling of release. I was just trying to avoid all the annoying wrapper types to maintain. But I think that may be settled now anyway |
Changed the issue title to better reflect the work that needs to be done. Particularly after #245 gets closed with the workaround solution that will land soon. Lots of discussion has happened around what we'd like to support some day. Here are some high level thoughts:
|
Add the ability to require multiple cancel streams in a single job. Requiring is done as you would expect, just target the different streams on the JobConfig. Fulfilling the writer/instance is done by passing the stream instance into the `Fulfill` method. The access wrappers now represent access to the cancel type not a specific cancel stream instance. This works because all of the cancel streams share the same SharedWrite Pending collection so getting write access for one stream grants write access for all. The instance passed through `Fulfill` will get safety checked in the access wrapper and delivered back to be used for writer creation. Behaviour: - One cancel request stream required: `jobData.Fulfill` behaves as normal, no explicit instance required. - N cancel request stream required: `jobData.Fulfill` must be provided the instance for each stream to fulfill otherwise the safety system will throw an exception. Note: This is a workaround implementation. #224 will make this cleaner. This approach can be applied to all write wide (shard write) types. Support will be added in an upcoming commit.
NOTE: This task has changed purpose somewhat as of #224 (comment)
Currently, task drivers key their data streams on type alone. This is a good default behaviour but there are situations where multiple streams of the same type are required.
For example, a task driver that supports a cancellable and non-cancellable request stream.
Today, the workaround is to create separate types either through private subtypes or completely separate types.
Provide a way to register multiple data streams of the same type.
Example Workaround
The text was updated successfully, but these errors were encountered: