Skip to content
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

What is the conceptual difference between Executor and Builder? #152

Closed
travis-leith opened this issue Jun 17, 2020 · 9 comments
Closed

Comments

@travis-leith
Copy link

travis-leith commented Jun 17, 2020

A Builder can be used to compose a bunch of Systems which are all Schedulable.
It seems that (I may be wrong on this one because it is not yet explicitly documented as far as I can tell) that Systems can be grouped between flush calls and that Systems within such a group are executed in parallel (by default) and the groups themselves are executed sequentially.

Have I got that right?

An Executor is something that "Executes a sequence of systems, potentially in parallel, and then commits their command buffers."

These seem to fulfill the same purpose to me. Is one being deprecated or do they have different use cases?

Also, I am happy to take a stab at expanding the readme.md and/or the hello_world example with something that clarifies this, once I understand it.

@Rua
Copy link
Contributor

Rua commented Jun 17, 2020

From what I can see, Schedule contains a series of steps, where a step is defined as:

/// A step in a schedule.
pub enum Step {
    /// A batch of systems.
    Systems(Executor),
    /// Flush system command buffers.
    FlushCmdBuffers,
    /// A thread local function.
    ThreadLocalFn(Box<dyn FnMut(&mut World, &mut Resources)>),
    /// A thread local system
    ThreadLocalSystem(Box<dyn Runnable>),
}

Note that one of the step types is an Executor. So it is used as part of a Schedule, the part that runs systems specifically. However, Schedule does not appear to call Executor::execute, instead it calls only run_systems and keeps a list of all executors it has run, then flushes their command buffers separately in the FlushCmdBuffers step for efficiency.

@travis-leith
Copy link
Author

@Rua thanks this is helpful.
Can you tell me if what I said about grouping Systems between flush calls is correct?

@Rua
Copy link
Contributor

Rua commented Jun 18, 2020

Looking at how Builder works:

  • All the systems you add with add_system are accumulated in a list.
  • When you call flush, it groups all the systems accumulated up to that point into a single Executor and then adds a Systems step for it. Then it adds a FlushCmdBuffers step after.
  • Calling add_thread_local_fn has a similar effect, but adds a ThreadLocalFn step after the Systems step instead.
  • add_thread_local (not fn) doesn't do this, it simply adds a ThreadLocalSystem step but leaves the list of accumulated systems untouched. So that system gets executed before the regular ones you already added.
  • Calling build when there are still accumulated systems in the list will simply discard them, they don't get added to the Schedule. So your last call before build must be either flush or add_thread_local_fn.

Those last two seem somewhat odd, so I wonder if this is a bug @TomGillen ?

@travis-leith
Copy link
Author

@Rua thanks again. I've got what I wanted from this issue but I will leave it open as there is an outstanding question for @TomGillen. Feel free to close it once this has been answered.

@TomGillen
Copy link
Collaborator

add_thread_local (not fn) doesn't do this, it simply adds a ThreadLocalSystem step but leaves the list of accumulated systems untouched. So that system gets executed before the regular ones you already added.

This is probably a bug. It should either flush for both or neither.

Calling build when there are still accumulated systems in the list will simply discard them, they don't get added to the Schedule. So your last call before build must be either flush or add_thread_local_fn.

The builder is converted into a schedule via .into(). The schedule's From impl flushes the builder before it converts it.

@Rua
Copy link
Contributor

Rua commented Jun 18, 2020

Ah yes, I missed that bit. Disregard the last point then.

@Rua
Copy link
Contributor

Rua commented Jun 18, 2020

@TomGillen I see in 42a0573 that you fixed the bug and added documentation, but the documentation text seems a bit misleading to me. It doesn't actually flush the systems' command buffers when inserting these steps, it merely commits them into an Executor. Users might think that their command buffers are being run before the thread-local system, when they aren't. I don't know whether the intended/desired behaviour is a flush or not.

Another oddity I noticed in Schedule is that the order in which command buffers are committed is different from the order in which steps are run, if regular and thread-local systems are mixed. For example, in this case:

Schedule::builder()
    .add_thread_local(A)
    .add_system(B)
    .add_thread_local(C)
    .flush()
    .build()

A user might expect the command buffers to be executed in the order A B C. But Scheduler will always execute the buffers of regular systems first and then those of thread-local systems, so the order is actually B A C.

@TomGillen
Copy link
Collaborator

Ah, yes, the documentation is wrong. It shouldn't say that it flushes the command buffers.

But Scheduler will always execute the buffers of regular systems first and then those of thread-local systems, so the order is actually B A C.

Are you sure about that? I can't reproduce it.

@TomGillen
Copy link
Collaborator

Oh wait, command buffers are flushed in the wrong order, not the system executed? Yea, it'll do that, and it probably shouldn't...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants