Quick answer: GameMaker complaining about surface stack mismatch? Some Step or Draw event throws an exception after calling surface_set_target but before surface_reset_target — the stack stays unbalanced.
An error inside a render-to-surface block leaves the surface target stack imbalanced; subsequent frames render to the wrong target.
Always Reset
surface_set_target(surf);
try {
draw_things();
} finally {
surface_reset_target();
}GML has try/catch/finally. Use it around set/reset pairs so a thrown error still pops the target.
Track Depth Manually
Increment a global on each set, decrement on reset. If non-zero at end of frame, log a warning — you have a leak. Debug build-only.
Verify Surface Validity
A destroyed surface can’t be set as target; the call errors out. Check surface_exists before surface_set_target.
Avoid Nested set_target
GameMaker supports nested targets, but they make balancing harder. Flatten to single-level if possible — clearer error handling.
Verifying
Surface stack stays balanced across frames. No mismatch warnings; render targets match expectations.
“Pair every set_target with a reset_target in a finally. Stack imbalance leaks frames.”
Build a tiny wrapper with_target(surf, draw_fn) — takes a script reference, handles set/reset. No more leaks.