In UMovieSceneSignedObject::MarkAsChanged, if we’re currently cooking, there is an ensure and early return. The comment there explains that we don’t want to regenerate GUIDs at cook-time because it makes cooking nondeterministic, which I understand. But there’s another comment that “we also check that nobody is trying to modify data after we have compiled the sequence, as this would lead to a mismatch between the source data and the compiled data we use to run it. We therefore check with the compiled data manager whether our parent sequence has already been compiled or not.”
But… it doesn’t fail the cook (I think). The caller that was marking this object as changed has no way of knowing it failed, and an ensure lets the cook continue. We ran into this when we tried to remove entire bindings from sequences at cook time, since bindings themselves can’t really be marked Editor-only. From what we can tell, the sequences also work fine on a cooked target. The sequences are not volatile, so I don’t think they’re being recompiled at runtime.
So I’m wondering what it really means for the compiled data to be out of date. I looked into compilation years ago, but I only really remember the part about determinism fences, which is what I was looking at. Is the operation we’re doing here safe, and if so, is it only safe because we’re only removing bindings? I can imagine if we added some at cook time, maybe those bound actors would not work correctly at runtime. What other data will be stale, and does it matter in this case? Alternatively, if there is another way to mark entire bindings as Editor-only that we don’t know about, I’d be happy to learn. Thanks for any advice you can provide.
There may be places where modifying data that way could be safe, but it might not be the best way to go about it. Removing a binding with a binding lifetime track could have a time segment with a null-pointer for existence. That may not be an issue for you since you mention editor-only data, but it’s an example of how seemingly safe data could have knock-on effects later on.
We have an optimize for cook pass with a few extension points. We remove compiled/cached data for muted tracks and invalid bindings as an example, and you could implement a virtual method like GetCookOptimizationFlags() on a custom track as a point to hook into for a custom track. It depends on your use case.
Yes, technically that would be the case. In sequencer we use GUIDs and regenerate them often when things change to make sure that even though there are class structures are the same they are considered different in their signatures. That said, in the case I mentioned before where we remove muted tracks, we actually suppress the modification of the GUID with a scoped event. If you look at UMovieSceneSequence::OptimizeForCook() at the top of the function, we call UE::MovieScene::FScopedSignedObjectModifySuppress SignatureChangeSupression(true); So you could use that as a template of how to do that.
Your advice here proved very insightful. I was completely unaware of the UMovieSceneSequence::OptimizeForCook process and the ECookOptimizationFlags. We were doing our cook-time optimization in UMovieScene::Serialize when Ar.IsCooking(), but that was late enough in the process to fire the ensure in UMovieSceneSignedObject::MarkAsChanged(). However, OptimizeForCook happens in UMovieSceneSequence::PreSave when ObjectSaveContext.IsCooking(), right before the data is compiled, so the ensure is avoided. I was able to move our logic to hook into OptimizeForCook and do the equivalent of ECookOptimizationFlags::RemoveObject, so I’m very glad to have found an “approved” place to put this.
There’s one more thing I’m not sure I understand, though. The idea behind this is to not regenerate GUIDs at cook-time because it makes cooking nondeterministic. Doesn’t doing the removal here still regenerate GUIDs at cook time, just at a time we’re not ensuring about? I see that UMovieSceneCompiledDataManager::CanMarkSignedObjectAsChangedDuringCook has a comment
// No data ID has been created, so this sequence hasn’t been compiled yet.
// We’re OK to modify it.
that kind of explains it, but I guess I’m hoping to understand further. Perhaps the idea is that even if UMovieSceneSignedObjects have their GUIDs changed, the actual compiled data will generate the same ID every time if the objects have been modified in the same way as the last time the sequence was cooked. I’d love to know if that’s an accurate guess, or if there are other factors I’m unaware of. Please let me know if you can.
Ah, that is interesting. I had been looking at this in 5.5.1, which is before FScopedSignedObjectModifySuppress and GMovieSceneSuppressSignatureChangeInCook were added. In a way, it circles back to my original question with an interesting answer: when this happens, the GUIDs aren’t modified, but it’s still before compiled data is generated (which presumably makes it safe at runtime). Whereas our solution was less safe because it was after compilation. If the version I was looking at had had GMovieSceneSuppressSignatureChangeInCook and GScopedSignedObjectWarnModifyCompiledData, I would have looked further at when those are valid and found my way to UMovieSceneSequence::OptimizeForCook. But in any case, I’m much happier knowing there’s an approved place to do this kind of modification at cook time. Thanks a lot for your help!