It sounds like you’re describing a state machine for the pizzas. A pizza (presumably) can’t simultaneously be “baking” and “burnt” at the same time, it will progress from one state to the next at certain transition points. So instead of multiple enableable components, it probably makes sense to have a single component called something like PizzaState.
The next question is what to do with that PizzaState component. Our internal tests show that except in unusual edge-cases, the fastest way to process multiple entities with state machines represented in this way is just a single job with a switch statement inside it:
switch(pizzaState)
{
case PizzaState.Baking:
// Do baking logic
break;
case PizzaState.Baked:
// Do baked logic
break;
case PizzaState.Burnt:
// Do burnt logic
break;
}
You said that you need different jobs for the different states, but didn’t really explain why. It’s probably worth examining that requirement, because if they really do have to be different jobs you’d want to implement them as IJobChunk and have your system launch a job for each state with an EntityQuery matching all of the pizzas, and then have each job check each pizza to see if it’s the required state for processing. This might be fine, but launching multiple jobs over the same set of entities seems kind of redundant in terms of job scheduling overhead and managing dependencies to guarantee job safety. To mitigate that, you would ideally combine states into as few jobs as possible, so perhaps you absolutely need BurntPizzaJob to be its own thing, but perhaps you can combine BakingOrBakedPizzaJob into one job that can process both states, and only launch 2 jobs rather than 3.
As you point out, Burst vectorization failing due to branches is a potential issue in all of the approaches that avoid structural changes. Whether it’s actually a problem will depend quite a lot on the nature of the work done in the jobs, whether it’s heavy number-crunching or just incrementing timers, etc… In situations where the profiler tells you that the lack of vectorization is hurting performance, perhaps it would be possible to refactor the jobs such that the logic happens inside a branchless job that applies to all of the pizzas regardless of what state they’re in. Whether or not the savings from removing branches and enabling vectorization (and/or explicitly making use of SIMD intrinsics) outweigh the redundant work that will be performed for pizzas in states that don’t need the data will be very specific to your use-case, and something that really only a profiler can tell you.
Finding a good solution to these sorts of problems is always going to be a fundamentally profiler-driven process. You make a best guess at a first approach, profile, and refine if the performance is not what you were hoping. That’s why the Key Principles say you should embrace iteration. If I were building the first attempt at this, I’d probably use a PizzaState, and the smallest number of jobs required to process all of the states - ideally just one job, with the switch statement. I wouldn’t worry too much about vectorization to begin with, unless the profiler showed that the initial approach needed improvement.