mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
test(embedded-mpv): Windows frame-copy measurements close the iGPU gate
The probe now runs on Windows too (.exe helper name; setImmediate polling because setTimeout quantizes to the ~15.6 ms system timer, which would dominate ageMs). RESULTS.md gains the Windows section measured on the same i7-1165G7/Iris Xe laptop as the Linux rows (dual boot): 1080p60 sustained over a 60 s run, viewport-price claim reproduced (4K HEVC in a 720p viewport = 60 fps at 1.3 ms copies), d3d11va hwdec active (5.5x helper CPU drop vs sw decode), zero torn reads everywhere. PORTING.md marks the Windows port done and records the machine gotchas (Smart App Control, Basic Display Adapter binding). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fd1bac011f
commit
9fefd70dd1
3 files changed
+100
-22
No files matched your search
@@ -19,12 +19,28 @@
|
||||
(Wayland session) with the xtream mock portal. Dev-build-only on Linux:
|
||||
the helper links system libmpv and `electron-after-pack.cjs` strips it
|
||||
from packages until milestone 4 (Linux bundled-libmpv runtime) — remove
|
||||
that strip when milestone 4 lands. The Windows implementation lives in the
|
||||
follow-up PR #1175; until that PR lands, `master` still has no WGL/named-shm
|
||||
helper and the frame-copy flag falls back to the native engine on Windows.
|
||||
that strip when milestone 4 lands.
|
||||
- **The Windows port below landed through PR #1175** — WGL GlContext twin,
|
||||
QPC clock + `Local\` named-file-mapping
|
||||
shm twins (the protocol keeps POSIX-style `/impv-*` names; the native
|
||||
sides derive the mapping name), reader compiled as C++ on `_WIN32`
|
||||
(MSVC has no C11 `<stdatomic.h>`), helper `.exe` binding.gyp target
|
||||
linking the vendored import lib + opengl32 (mpv DLL resolved from the
|
||||
exe's own directory), TS gates + `.exe` helper discovery, i18n,
|
||||
packaging/CI guards (win32 packages must ship helper + reader; MSVC
|
||||
intermediates and import libs excluded from dist). **The open iGPU perf
|
||||
gate is CLOSED**: RESULTS.md rows on the same i7-1165G7/Iris Xe laptop
|
||||
as the Linux section (dual boot) — 1080p60 sustained (clean 60 s run),
|
||||
the viewport-price claim reproduces, d3d11va hwdec active (5.5× CPU
|
||||
drop), torn=0 everywhere. Machine gotchas for future sessions: Windows
|
||||
11 Smart App Control must be OFF to run locally-built unsigned helpers,
|
||||
and a fresh Windows install can leave the iGPU on the Basic Display
|
||||
Adapter — bind the real Intel driver (`pnputil /remove-device` +
|
||||
`/scan-devices` once the driver is in the store) or WGL has no 3.2 core
|
||||
context and no d3d11va.
|
||||
- PR #1169 credits larsemig's idea (#1154 comment 4932807350). Shared-player
|
||||
controls are reviewed as a separate integration after this platform stack;
|
||||
do not conflate that UI layer with the frame-copy transport ports.
|
||||
controls remain a separate integration after this platform stack; do not
|
||||
conflate that UI layer with the frame-copy transport ports.
|
||||
|
||||
## What "porting" means
|
||||
|
||||
@@ -33,22 +49,24 @@ The stdio protocol, shm layout, TS adapter, main-process service, preload
|
||||
pump, and Angular UI are shared and already shipped.
|
||||
|
||||
```
|
||||
apps/electron-backend/native/helper/ # state after the Linux port:
|
||||
apps/electron-backend/native/helper/ # state after all three ports:
|
||||
├── mpv_frame_helper.cpp # portable: protocol, mpv session, snapshots
|
||||
├── frame_helper_io.h # portable: TSV-in/JSON-out, percent-encoding
|
||||
├── frame_shm.h # portable layout + shared CLOCK_MONOTONIC clock
|
||||
│ # (POSIX shm calls still need a Windows twin)
|
||||
├── frame_helper_render.h # portable: FBO/PBO readback + shm publish
|
||||
└── frame_helper_gl.h # PLATFORM SEAM: GlContext — CGL (macOS) and
|
||||
# EGL (Linux); Windows adds its WGL twin HERE
|
||||
├── frame_shm.h # portable layout + shared clock (POSIX
|
||||
│ # CLOCK_MONOTONIC / Windows QPC) + the Windows
|
||||
│ # Local\ mapping-name derivation
|
||||
├── frame_helper_render.h # portable render + ShmRing (POSIX shm_open /
|
||||
│ # Windows CreateFileMapping twins)
|
||||
└── frame_helper_gl.h # PLATFORM SEAM: GlContext — CGL (macOS),
|
||||
# EGL (Linux) and WGL (Windows)
|
||||
apps/electron-backend/native/src/embedded_mpv_frame_reader.c
|
||||
# real impl on __APPLE__ + __linux__, stub
|
||||
# elsewhere (Windows needs shm-open/clock twins)
|
||||
# real impl on __APPLE__ + __linux__ + _WIN32
|
||||
# (compiled as C++ there), stub elsewhere
|
||||
```
|
||||
|
||||
Porting Windows = give `frame_helper_gl.h` a WGL GlContext twin, give the
|
||||
shm create/open (+ `frame_shm_now_ns`) Windows twins, flip the TS gate in
|
||||
`embedded-mpv-frame-copy-platform.util.ts`, extend packaging.
|
||||
All three ports have landed; new platforms follow the same seams: a
|
||||
GlContext twin in `frame_helper_gl.h`, shm create/open + `frame_shm_now_ns`
|
||||
twins, the TS gate in `embedded-mpv-frame-copy-platform.util.ts`, packaging.
|
||||
|
||||
## Branching & merge strategy
|
||||
|
||||
@@ -95,7 +113,11 @@ shm create/open (+ `frame_shm_now_ns`) Windows twins, flip the TS gate in
|
||||
the Linux support matrix in `docs/architecture/embedded-mpv-native.md`
|
||||
when this lands.
|
||||
|
||||
### Windows
|
||||
### Windows (DONE 2026-07-12 — see the update in "State" above)
|
||||
|
||||
One deviation from the checklist below: the WGL context uses a hidden
|
||||
regular window, not a message-only one — SetPixelFormat needs a
|
||||
display-capable DC.
|
||||
|
||||
1. **Render backend**: WGL headless — create a hidden message-only window +
|
||||
dummy pixel format, `wglCreateContextAttribsARB` 3.2 core, then the same
|
||||
|
||||
@@ -143,6 +143,50 @@ Readings:
|
||||
- EGL display tier used: Mesa surfaceless platform (first tier; no display
|
||||
server needed).
|
||||
|
||||
## Windows mid-range laptop (iGPU) — PENDING
|
||||
## Windows mid-range laptop (iGPU) — Windows 11 Home 26200, i7-1165G7 / Iris Xe, x64 — 2026-07-12
|
||||
|
||||
Blocked on the Windows helper port (WGL or D3D11 readback path).
|
||||
Source: Windows port branch (WGL `frame_helper_gl.h` backend), vendored
|
||||
libmpv from zhongfly/mpv-winbuild 2026-06-14 (`git-7d245fd100`,
|
||||
`libmpv-2.dll`), Intel driver 30.0.101.1340. **Same physical laptop as the
|
||||
Linux section above** (TUXEDO Book XP14 Gen12, dual boot), so the two
|
||||
sections compare OS/driver stacks on identical hardware. Measured with the
|
||||
production helper + `embedded_mpv_frame_reader.node` through
|
||||
`linux-frame-probe.mjs` (now cross-platform; on Windows it polls via
|
||||
setImmediate because setTimeout quantizes to the ~15.6 ms system timer,
|
||||
which would dominate *age*), so *age* is produce→reader-copy and excludes
|
||||
the renderer texture upload — same semantics as the Linux rows. Unlike the
|
||||
Linux rows, hwdec IS available here: mpv's `hwdec=auto` engages d3d11va
|
||||
(verified by helper CPU: 0.51 core-s/s vs 2.82 core-s/s with `--hwdec no`
|
||||
on the 4K row — a 5.5× CPU drop). Test clip generated with `hevc_qsv`
|
||||
(the Intel encoder), so decode complexity is not byte-identical to the
|
||||
videotoolbox/x265 clips of the other sections.
|
||||
|
||||
| Scenario | New fps | copy ms avg/p95 | age ms avg/p95 | torn |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1080p60 testsrc2, sw | 60.0 | 1.80 / 2.10 | 1.83 / 2.14 | 0 |
|
||||
| 4K60 testsrc2, sw | 56.0 | 8.81 / 9.88 | 8.83 / 9.86 | 0 |
|
||||
| 4K60 HEVC 25 Mbit, hwdec=d3d11va | 41.1 | 8.57 / 10.4 | 8.71 / 10.4 | 0 |
|
||||
| 4K60 HEVC 25 Mbit, sw decode | 45.7 | 10.3 / 15.1 | 10.4 / 15.4 | 0 |
|
||||
| 4K60 HEVC 25 Mbit in a 1280×720 viewport, hwdec | 60.2 | 1.29 / 1.87 | 1.27 / 1.82 | 0 |
|
||||
|
||||
Readings:
|
||||
|
||||
- 1080p60 — the realistic viewport class for this laptop's 1920×1200
|
||||
screen — holds a clean 60 fps with ~1.8 ms copies. A 60-second sustained
|
||||
run kept 60.0 fps over 3601 frames (copy 1.57 / 1.84 ms, torn 0).
|
||||
- The 4K rows are stress rows, as on Linux: at a full-4K viewport the
|
||||
Iris Xe is saturated by mpv render + readback (56 fps ceiling with no
|
||||
decode at all), so d3d11va decode — which shares the same iGPU — buys
|
||||
CPU headroom (5.5×), not fps; sw decode trades ~2.3 cores for ~4 fps.
|
||||
- The viewport-size claim reproduces on Windows: the same 4K60 HEVC clip
|
||||
in a 720p viewport runs 60 fps with 1.3 ms copies and hardware decode
|
||||
active.
|
||||
- torn=0 across every run; the aspect-fit generation bump was verified
|
||||
(4:3 960×720 source in a 1280×720 viewport → `-g2` at 960×720).
|
||||
- WGL context: `gl renderer: Intel(R) Iris(R) Xe Graphics` (hardware,
|
||||
3.2 core via wglCreateContextAttribsARB). Machine prerequisite hit
|
||||
during bring-up: Windows 11 Smart App Control blocks locally-built
|
||||
unsigned executables (the helper) until turned off, and a fresh Windows
|
||||
install may run the iGPU on the Basic Display Adapter — the frame-copy
|
||||
engine needs the real Intel driver bound (WGL on the basic adapter has
|
||||
no 3.2 core context, and there is no d3d11va).
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Linux frame-copy probe: spawns iptvnator_mpv_helper, attaches the
|
||||
* Linux/Windows frame-copy probe: spawns iptvnator_mpv_helper, attaches the
|
||||
* embedded_mpv_frame_reader addon to the announced shm generation, and
|
||||
* reports producer fps, copy latency (ageMs), copy wall time, torn reads
|
||||
* and pixel spread. Usage:
|
||||
@@ -22,7 +22,12 @@ const hwdecIdx = rest.indexOf('--hwdec');
|
||||
const hwdec = hwdecIdx >= 0 ? rest[hwdecIdx + 1] : null;
|
||||
|
||||
const reader = require(path.join(releaseDir, 'embedded_mpv_frame_reader.node'));
|
||||
const helperPath = path.join(releaseDir, 'iptvnator_mpv_helper');
|
||||
const helperPath = path.join(
|
||||
releaseDir,
|
||||
process.platform === 'win32'
|
||||
? 'iptvnator_mpv_helper.exe'
|
||||
: 'iptvnator_mpv_helper'
|
||||
);
|
||||
const shmBase = `/impv-probe-${process.pid}`;
|
||||
|
||||
const args = ['--shm-base', shmBase, '--width', width, '--height', height];
|
||||
@@ -56,6 +61,13 @@ child.stdout.on('data', (chunk) => {
|
||||
});
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
/* Poll yield between latestSeq checks: Windows quantizes setTimeout to the
|
||||
* ~15.6 ms system timer, which would dominate ageMs; setImmediate keeps the
|
||||
* poll sub-ms there at the cost of one busy event loop. */
|
||||
const pollYield =
|
||||
process.platform === 'win32'
|
||||
? () => new Promise((r) => setImmediate(r))
|
||||
: () => sleep(2);
|
||||
|
||||
await sleep(300);
|
||||
child.stdin.write(`load\turl=${url.replace(/%/g, '%25')}\n`);
|
||||
@@ -102,7 +114,7 @@ while (Date.now() - start < durationMs) {
|
||||
if (result.torn) torn += 1;
|
||||
}
|
||||
}
|
||||
await sleep(2);
|
||||
await pollYield();
|
||||
}
|
||||
const elapsed = (Date.now() - start) / 1000;
|
||||
|
||||
|
||||
Reference in new issue
Block a user