@@ -223,6 +223,19 @@ final class MetalTerminalRenderer: NSObject, MTKViewDelegate {
223223 private var preparedSnapshot : ( snapshot: TerminalSnapshot ,
224224 context: SnapshotRenderContext ) ?
225225
226+ /// Testing hook: block until the GPU finishes, so the drawable texture can
227+ /// be read back. Never set in production; a synchronous wait on the render
228+ /// path is exactly what this work is trying to remove.
229+ var waitForCompletionAfterCommit = false
230+
231+ /// Testing hook: retains the texture this renderer actually drew into.
232+ ///
233+ /// Necessary because asking the surface for a drawable a second time
234+ /// returns a *different* one from the pool, not the frame just rendered —
235+ /// comparing that instead silently compares uninitialised memory.
236+ var capturesRenderedTexture = false
237+ private( set) var lastRenderedTexture : MTLTexture ?
238+
226239 /// Asks the host to schedule a frame. A callback rather than a reference to
227240 /// the view's frame driver, so the renderer needs no view access
228241 /// (io-gaps.md G1, WO-F1c).
@@ -603,9 +616,18 @@ final class MetalTerminalRenderer: NSObject, MTKViewDelegate {
603616 os_signpost ( . begin, log: MetalTerminalRenderer . profileLog, name: " Metal.Commit " , signpostID: commitID)
604617 }
605618#endif
619+ if capturesRenderedTexture {
620+ lastRenderedTexture = drawable. texture
621+ }
606622 commandBuffer. present ( drawable)
607623 bufferPool. commit ( commandBuffer: commandBuffer)
608624 commandBuffer. commit ( )
625+ if waitForCompletionAfterCommit {
626+ // Testing only: makes the drawable texture readable right after
627+ // render() returns, so a test can compare what two surfaces
628+ // actually produced.
629+ commandBuffer. waitUntilCompleted ( )
630+ }
609631#if canImport(os)
610632 if MetalTerminalRenderer . profileEnabled {
611633 os_signpost ( . end, log: MetalTerminalRenderer . profileLog, name: " Metal.Commit " , signpostID: commitID)
0 commit comments