mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 06:00:33 +02:00
Mac in the headset: final benchmark numbers; relay delay is a delay line
Review round 8 (GPT-6 Astra xhigh): --delay paused upstream reads, so busy streams saw 30-60 ms instead of 30. Verified locally: 60-62 ms round trip with 30 ms each way under load. No saved result used --delay. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
9e4dcdd147
commit
00b45bafc5
3 files changed
+1211
-5
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -196,6 +196,21 @@ Baseline on 2026-09-28 (**verified**, home Wi-Fi, Tailscale, Balanced,
|
|||||||
| scroll (1920×1290) | 14.7 | – | 50.4 | encode 6.7, decode 6.4 (software), 9.4 Mbit/s, 16% late |
|
| scroll (1920×1290) | 14.7 | – | 50.4 | encode 6.7, decode 6.4 (software), 9.4 Mbit/s, 16% late |
|
||||||
| type (1920×1290) | 16.7 | 51.2 / 73.2 | – | most of the input time is the Mac app reacting |
|
| type (1920×1290) | 16.7 | 51.2 / 73.2 | – | most of the input time is the Mac app reacting |
|
||||||
|
|
||||||
|
After this work, with the controller on (**verified**, same setup,
|
||||||
|
`bench/results/2026-09-28-9e4dcdd-final.json`; ms p50/p95). Content is
|
||||||
|
now measured from the earlier of display time and delivery, which adds
|
||||||
|
about 5 ms to scroll compared with the baseline's way of measuring:
|
||||||
|
|
||||||
|
| Scenario | Content | Input to drawn | fps drawn | Grades |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| test | 10.5 / 16.7 | 29.6 / 36.3 | 60 | all within target |
|
||||||
|
| scroll | 19.8 / 27.4 | – | 55.9 | fps, late frames (4.8%) and worst gap (222 ms) only "acceptable": Wi-Fi stalls and ScreenCaptureKit's 46–53 fps from virtual displays |
|
||||||
|
| type | 15.0 / 21.4 | 43.0 / 60.5 | – | all within target |
|
||||||
|
|
||||||
|
Of the targets, click to photon is met without the Frame's compositor (the
|
||||||
|
headset has to be worn to measure its share), and so is content latency.
|
||||||
|
The frame-rate and no-stall targets aren't yet met while scrolling.
|
||||||
|
|
||||||
What was learned (all **verified**, unless marked):
|
What was learned (all **verified**, unless marked):
|
||||||
|
|
||||||
- The biggest costs are encoding (4–7 ms), network (4–6 ms), and decoding
|
- The biggest costs are encoding (4–7 ms), network (4–6 ms), and decoding
|
||||||
|
|||||||
@@ -153,12 +153,30 @@ class Relay:
|
|||||||
w.close()
|
w.close()
|
||||||
|
|
||||||
async def _plain(self, reader, writer): # Frame -> agent: delay only
|
async def _plain(self, reader, writer): # Frame -> agent: delay only
|
||||||
|
if not self.delay:
|
||||||
|
while data := await reader.read(65536):
|
||||||
|
writer.write(data)
|
||||||
|
await writer.drain()
|
||||||
|
writer.close()
|
||||||
|
return
|
||||||
|
# Each chunk leaves `delay` after it arrived, while reading goes on:
|
||||||
|
# a delay line, not a pause (which would add up behind a busy stream).
|
||||||
|
queue = asyncio.Queue()
|
||||||
|
|
||||||
|
async def send():
|
||||||
|
while (item := await queue.get())[1] is not None:
|
||||||
|
wait = item[0] - self.loop.time()
|
||||||
|
if wait > 0:
|
||||||
|
await asyncio.sleep(wait)
|
||||||
|
writer.write(item[1])
|
||||||
|
await writer.drain()
|
||||||
|
writer.close()
|
||||||
|
|
||||||
|
sender = asyncio.ensure_future(send())
|
||||||
while data := await reader.read(65536):
|
while data := await reader.read(65536):
|
||||||
if self.delay:
|
await queue.put((self.loop.time() + self.delay, data))
|
||||||
await asyncio.sleep(self.delay)
|
await queue.put((0, None))
|
||||||
writer.write(data)
|
await sender
|
||||||
await writer.drain()
|
|
||||||
writer.close()
|
|
||||||
|
|
||||||
async def _shaped(self, reader, writer): # agent -> Frame
|
async def _shaped(self, reader, writer): # agent -> Frame
|
||||||
queue = asyncio.Queue()
|
queue = asyncio.Queue()
|
||||||
|
|||||||
Reference in new issue
Block a user