77// byte-identical input and their profiles are directly comparable.
88//
99// Usage:
10- // RenderBench [--metal] [--seconds N] [--scenario dense|medium|scroll|arabic|arabic-line]
10+ // RenderBench [--metal] [--seconds N]
11+ // [--scenario dense|medium|scroll|arabic|arabic-line|stalled-frame]
1112//
1213// Scenarios:
1314// dense every cell gets its own truecolor foreground and background
1415// (vtebench dense_cells shape: many one-cell attribute runs)
1516// medium a color change every 8 cells (longer runs, fewer segments)
1617// scroll plain ASCII lines that scroll the screen
1718// arabic scrolling Arabic words (exercises the BiDi shaping path)
19+ // stalled-frame
20+ // hold the Metal frame permit before the first draw, then feed
21+ // terminal output. The pane stays black while the model changes.
1822//
1923// Profile it with Instruments:
2024// swift build -c release
@@ -40,15 +44,26 @@ while let argument = argIterator.next() {
4044 case " --scenario " :
4145 scenario = argIterator. next ( ) ?? scenario
4246 default :
43- print ( " usage: RenderBench [--metal] [--seconds N] [--scenario dense|medium|scroll|arabic|arabic-line] " )
47+ print ( " usage: RenderBench [--metal] [--seconds N] " +
48+ " [--scenario dense|medium|scroll|arabic|arabic-line|stalled-frame] " )
4449 exit ( 1 )
4550 }
4651}
47- guard [ " dense " , " medium " , " scroll " , " arabic " , " arabic-line " ] . contains ( scenario) else {
52+ guard [ " dense " , " medium " , " scroll " , " arabic " , " arabic-line " , " stalled-frame " ] . contains ( scenario) else {
4853 print ( " unknown scenario: \( scenario) " )
4954 exit ( 1 )
5055}
5156
57+ if scenario == " stalled-frame " {
58+ #if DEBUG
59+ useMetal = true
60+ setenv ( " SWIFTTERM_TEST_METAL_FRAME_PERMIT_HELD " , " 1 " , 1 )
61+ #else
62+ print ( " stalled-frame requires a debug build " )
63+ exit ( 1 )
64+ #endif
65+ }
66+
5267/// Deterministic generator so every run feeds identical bytes.
5368struct SplitMix64 {
5469 var state : UInt64
@@ -96,6 +111,8 @@ final class FrameSource {
96111 return scrollChunk ( cols: cols, rows: rows)
97112 case " arabic-line " :
98113 return arabicLine ( )
114+ case " stalled-frame " :
115+ return coloredFrame ( cols: cols, rows: rows, runLength: 1 )
99116 default :
100117 return arabicChunk ( rows: rows)
101118 }
@@ -183,6 +200,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
183200 window. title = " RenderBench: \( scenario) \( useMetal ? " (Metal) " : " (CG) " ) "
184201 terminalView = TerminalView ( frame: window. contentView!. bounds)
185202 terminalView. terminalDelegate = terminalViewDelegate
203+ window. contentView!. addSubview ( terminalView)
204+ window. makeKeyAndOrderFront ( nil )
205+ NSApp . activate ( ignoringOtherApps: true )
206+
186207 if useMetal {
187208 do {
188209 try terminalView. setUseMetal ( true )
@@ -191,13 +212,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
191212 exit ( 1 )
192213 }
193214 }
194- window. contentView!. addSubview ( terminalView)
195- window. makeKeyAndOrderFront ( nil )
196- NSApp . activate ( ignoringOtherApps: true )
197215
198216 let t = terminalView. getTerminal ( )
199217 print ( " renderer= \( useMetal ? " metal " : " cg " ) scenario= \( scenario) " +
200218 " cols= \( t. cols) rows= \( t. rows) seconds= \( seconds) " )
219+ if scenario == " stalled-frame " {
220+ print ( " FAULT ACTIVE: the Metal frame permit is held before the first draw. " )
221+ print ( " EXPECTED: the pane stays black while the terminal accepts synthetic output. " )
222+ }
201223 startTime = CFAbsoluteTimeGetCurrent ( )
202224 DispatchQueue . main. async { self . tick ( ) }
203225 }
@@ -220,6 +242,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
220242 }
221243 if elapsed >= seconds {
222244 report ( elapsed: elapsed, prefix: " TOTAL " )
245+ if scenario == " stalled-frame " {
246+ print ( " REPRODUCED: the terminal accepted \( frameCount) updates, " +
247+ " but every Metal draw was refused. " )
248+ }
223249 exit ( 0 )
224250 }
225251 DispatchQueue . main. async { self . tick ( ) }
0 commit comments