Summary
With approximately 30–40 user variables defined on a Movie Render Graph, rendering develops a significant per-output-frame stall. With around 40 variables, the stall is approximately 0.8 seconds per output frame.
The stall appears to increase as additional user variables are added, and the variables do not need to be connected to or used by the graph. Removing all user variables eliminates the stall.
As I am using tooling to configure render settings, I was able to work around the issue by:
- Removing the MRG user variables.
- Making a duplicate transient Movie Render Graph before each render.
- Setting the appropriate graph node properties directly in code instead of using user-variable overrides.
The performance improvement comes from removing the graph user variables.
With the same production graph and equivalent settings applied directly to the duplicated nodes, the per-frame stall disappears and rendering returns to a normal, consistent cadence.
What Type of Bug are you experiencing?
Foundation (C++ Tools, Profiling, & Pipeline)
Steps to Reproduce
- Create a blank level and blank Level Sequence.
- Create a simple Movie Render Graph using otherwise default render settings.
- Render the sequence through the standard Movie Render Queue and observe the render performance.
- Add approximately 30–40 user variables to the Movie Render Graph.
- Leave the variables unused/unconnected.
- Render the same sequence again.
- Observe a significant pause per output frame. The duration of the pause appears to increase as more user variables are added.
Expected Result
Defining or overriding Movie Render Graph user variables should not introduce a large per-output-frame performance penalty, particularly when those variables are unused.
Observed Result
With the user variables present, a large pause occurs approximately once per completed output frame.
In CSV Profiler captures, the pause is approximately 800 ms with around 40 variables.
During the pause:
- GameThread/FrameTime increases to approximately 800–850 ms.
- GPU time remains low.
- RenderThread spends much of the interval waiting.
- The behavior also occurs on a blank scene, so it does not appear related to scene complexity.
- Reducing resolution or changing PNG output to BMP does not significantly affect the pause.
Removing all MRG user variables causes rendering to return to normal speed.
The issue can be reproduced using variables that are completely unused by the evaluated graph, suggesting the overhead is associated with the presence/evaluation of MRG user variables rather than the settings they control.
Affects Versions
5.8
5.7
Platform(s)
Windows
Upload an image
RenderProfile_20260824_173212_Detailed_WithoutPause (7.47 MB)RenderProfile_20260817_132632_Detailed_WithPause (5.06 MB)
Additional Notes
The issue reproduces when using the standard Movie Render Queue, so it does not appear to be caused by my custom render executor/tool.
The primary stall occurs when beginning/evaluating a new output frame and reproduces with temporal sample count set to 1. Increasing temporal samples is therefore not required to reproduce the issue. Smaller additional overhead may also be visible during temporal-sample and warm-up processing.
Attached are Unreal CSV Profiler captures from an affected render and a render after removing the MRG user variables.
Source inspection suggests a possible hotspot, although I have not yet confirmed it with an Unreal Insights call stack.
UMovieGraphCoreTimeStep::TickProducingFrames() calls UMovieGraphConfig::CreateFlattenedGraph() on the first temporal sample of each output frame.
Near the end of CreateFlattenedGraph(), the engine iterates every user variable from every evaluated graph to populate UMovieGraphEvaluatedConfig::VariablesByName. This occurs even when a variable has no getter node and is not connected to the graph.
Each variable is resolved through:
UMovieGraphVariableNode::GetResolvedVariableValue()
→ ContextHasEnabledAssignmentForVariable()
→ shot/primary-shot/job GetOrCreateJobVariableAssignmentsForGraph()
In editor builds, existing assignment containers call UMovieJobVariableAssignmentContainer::UpdateGraphVariableOverrides(). That function repeatedly scans and sorts the graph variables and property-bag descriptors.
Because this sequence can occur for every variable during each first-temporal-sample graph evaluation, it may explain why unused variables still create a per-output-frame Game Thread stall and why the cost grows with variable count.
The relevant trace scope in UE 5.7 is MRQ_CreateFlattenedGraph.