re: the fast probe stalls -- its own dense negatives are withdrawn

Cross-checked the instrument built last iteration against an independent
grabber while both watched the same screen, and it fails.

A single long-lived ffmpeg x11grab stream degrades and then freezes:

  862 frames in 540.1 s = 1.60 fps        (it starts at 3.98)
  t=450/480/510/540 s: surface mean 5.21, identical every time

At that same moment `import` read surface mean 125.65, and a freshly
started ffmpeg stream read 122.43 -- agreeing with import to 3%. So the
acquisition was broken, not the analysis: the stream replayed a stale
frame while the screen was 24x brighter.

That withdraws last iteration's headline. "2391 frames over 600 s from
t=0, max glyph 0" cannot distinguish "the title never appeared" from "the
stream froze early and repeated one frame 2391 times". Its 3.98 fps was
measured over the first 20 s, before the degradation. Sample count is not
coverage unless the samples are known independent.

Fixed: the stream is now torn down and restarted every 30 s. Startup is
~0.3 s, cheap against the title's window, and it guarantees live frames.

Separately, the cache hypothesis was tested and is SUPPORTED. cache,
cache0, cache1, cache_host moved aside (to /tmp/xenia-cache-aside, not
deleted) and the surface renders again: import reads mean 54.8 and 68.6
with 100% non-black warm content, against 0.07 and 0.08% non-black in the
black run; 773 of 862 probe frames had >2% non-black. One run each side
and many kill -9s before the black one, so it is supported, not proven --
the old caches are kept for reproduction.

Still no title, but that number now comes from a stalling probe and
establishes nothing either way.

METHOD: validating a probe on static images tests its analysis, not its
acquisition -- cross-check against an independent grabber during a run.
This commit is contained in:
Sylpheed RE agent
2026-08-29 02:01:47 +00:00
parent a3946e5005
commit 46006406a4
4 changed files with 95 additions and 6 deletions

View File

@@ -1,4 +1,16 @@
"""Fast title probe: one long-lived x11grab stream, ~3.5 fps, glyph count per frame.
"""Fast title probe: a PERIODICALLY RESTARTED x11grab stream + glyph count.
🔴 DO NOT run one x11grab stream for the whole session. Measured 2026-08-29: a
single long-lived stream degrades from 3.98 fps to 1.60 fps and then FREEZES,
reporting a stale frame indefinitely. Cross-checked at one moment against an
independent `import` grab: the stream said surface mean 5.21 while `import` said
125.65, and the stream repeated that same value for four consecutive 30 s marks.
Any negative result from a stalled stream is worthless -- an earlier "2391
frames, zero hits" claim came from exactly that failure.
So the stream is torn down and restarted every RESTART_S seconds. Startup costs
~0.3 s, which is cheap against the title's few-second window and buys a
guarantee that the frames are live.
Replaces the two-`screenshot` polling loop, which costs ~4-11 s per grab while
xenia runs and so samples every ~41 s -- slower than the title screen lasts.
@@ -10,13 +22,19 @@ import numpy as np
W,H = 1280,720
LIMIT = float(sys.argv[1]) if len(sys.argv)>1 else 120
NEED = int(sys.argv[2]) if len(sys.argv)>2 else 400
p = subprocess.Popen(["ffmpeg","-loglevel","error","-f","x11grab","-draw_mouse","0",
"-video_size",f"{W}x{H}","-i",":98","-r","4","-f","rawvideo","-pix_fmt","rgb24","-"],
stdout=subprocess.PIPE, bufsize=W*H*3*2)
n=W*H*3; t0=time.time(); frames=0; best=0; hits=0
RESTART_S = 30 # tear down and restart the stream this often
def _open():
return subprocess.Popen(["ffmpeg","-loglevel","error","-f","x11grab","-draw_mouse","0",
"-video_size",f"{W}x{H}","-i",":98","-r","4","-f","rawvideo","-pix_fmt","rgb24","-"],
stdout=subprocess.PIPE, bufsize=W*H*3*2)
p=_open()
n=W*H*3; t0=time.time(); frames=0; best=0; hits=0; seg=time.time()
while time.time()-t0 < LIMIT:
if time.time()-seg > RESTART_S: # a stalled stream reports stale frames forever
p.kill(); p=_open(); seg=time.time()
buf=p.stdout.read(n)
if len(buf)<n: break
if len(buf)<n:
p.kill(); p=_open(); seg=time.time(); continue
a=np.frombuffer(buf,np.uint8).reshape(H,W,3).astype(int)
r,g,b=a[:,:,0],a[:,:,1],a[:,:,2]
c=int(((g>130)&(g-r>45)&(g-b>45)).sum())