|
1 | 1 | # Spool |
2 | 2 |
|
| 3 | +**Disposable experiment branch:** `experiment/trickplay-yuv-benchmark` launches |
| 4 | +the onscreen RGB/YUV benchmark automatically. It is not intended for merging. |
| 5 | +Use `--regular-app` to enter the retained normal application. |
| 6 | + |
3 | 7 | - libmpv: We've forked this and made it compatible in the directory above. keep libmpv behind a thin PlayerController / PlaybackController facade and do not let Jellyfin/network/UI code know about mpv internals. |
4 | 8 | - Qt6.11 |
5 | 9 | HTTP / REST: QNetworkAccessManager as the base, QRestAccessManager on top, plus QNetworkRequestFactory for shared base URL / headers / auth boilerplate. Use Qt’s JSON types (QJsonDocument, QJsonObject, QJsonArray) end to end. In Qt 6.11, QRestAccessManager is the REST-focused wrapper over QNetworkAccessManager, and Qt OpenAPI generates Qt HTTP clients using Qt Network APIs such as QRestAccessManager. |
@@ -169,6 +173,70 @@ the player; it does not close Spool. `stop` also returns to the application. |
169 | 173 | Bindings which manipulate volume, mute or speed update the corresponding Spool |
170 | 174 | controls. Closing Spool continues to use its normal application shutdown path. |
171 | 175 |
|
| 176 | +## Disposable trickplay RGB/YUV benchmark |
| 177 | + |
| 178 | +The bundled private fixture is the nearest-rank 95th percentile by compressed |
| 179 | +JPEG size among 15,603 sheets inventoried from Jellyfin's trickplay directory: |
| 180 | +1,548,147 bytes, 3200×1800, 10×10 thumbnails, 8-bit 4:2:0. The exact SHA-256, |
| 181 | +source-relative path, population and selection rule are in |
| 182 | +`tests/fixtures/trickplay-p95.json`. This is not a claim of 95th-percentile |
| 183 | +decode latency. Do not publish the fixture with this temporary branch. |
| 184 | + |
| 185 | +The three cases are the current production Qt RGB32 decoder, direct libjpeg |
| 186 | +BGRA decoding, and direct libjpeg planar YUV420 decoding. All use the same |
| 187 | +single-quad Qt Quick renderer, thumbnail geometry and fixed tile trace. |
| 188 | +Direct RGB batches scanlines; YUV writes directly into MCU-padded planes and |
| 189 | +uses actual one-byte GPU textures. GPU textures are reused for replacement |
| 190 | +sheets. Crop-only frames never re-upload pixels. Both paths are opaque, |
| 191 | +single-pass draws into the SDR window; no intermediate RGB framebuffer exists. |
| 192 | + |
| 193 | +Each case runs three complete warm-ups, then three measured runs in a shuffled, |
| 194 | +logged order. Every run contains 12 decode/upload replacements and 120 |
| 195 | +crop-only frames. The seed is logged. Default launch leaves the final result |
| 196 | +onscreen; `--bench-auto-exit` makes desktop runs finite: |
| 197 | + |
| 198 | +```sh |
| 199 | +nix develop .#native -c cmake --build build/linux-release/app --target spool |
| 200 | +nix develop .#native -c env SPOOL_RENDER_API=vulkan \ |
| 201 | + build/linux-release/app/spool --bench-auto-exit --bench-seed 20261005 \ |
| 202 | + --bench-log local-docs/trickplay-yuv-benchmark/optimized-vulkan.jsonl |
| 203 | +nix develop .#native -c env SPOOL_RENDER_API=opengl \ |
| 204 | + build/linux-release/app/spool --bench-auto-exit --bench-seed 20261005 \ |
| 205 | + --bench-log local-docs/trickplay-yuv-benchmark/optimized-opengl.jsonl |
| 206 | +``` |
| 207 | + |
| 208 | +`--bench-fresh-textures` restores the initial fresh-resource experiment. |
| 209 | +`--bench-screenshot-dir PATH` chooses the diagnostic image directory. |
| 210 | +Without an explicit log path, JSONL goes into the platform's persistent data |
| 211 | +directory; the path is printed and shown onscreen. Every raw record also goes |
| 212 | +to stdout with a `BENCH` prefix. |
| 213 | + |
| 214 | +Logs retain warm-ups and measured records, wall/thread-CPU decode stages, |
| 215 | +allocation sizes, queue/handoff times, synchronization, material updates, |
| 216 | +per-plane upload enqueue, command recording and selection-to-Qt-swap latency. |
| 217 | +Qt does not expose a truthful allocation/codec stage split, so those Qt fields |
| 218 | +are null. GPU upload enqueue is not GPU execution time. Vulkan GPU events are |
| 219 | +attributed to their originating request using Qt's frame-slot timestamp |
| 220 | +lifecycle and include the whole Qt frame, uploads and drawing. Other backends |
| 221 | +retain explicitly unattributed delayed GPU diagnostics; isolated GPU |
| 222 | +upload/draw timings are always null. |
| 223 | + |
| 224 | +Process RSS/anonymous memory has both a fixed pre-fixture baseline and |
| 225 | +per-run baselines. An independent requested-1 ms sampler records approximate |
| 226 | +run peaks; cumulative VmHWM remains separately labeled. Replacement decoding |
| 227 | +can overlap the old and new CPU buffers. Decoded bytes, logical texture bytes |
| 228 | +and Vulkan/D3D12 allocator counters are separate: none are a physical VRAM |
| 229 | +measurement, and they must not be added blindly to RSS. Allocator caches and |
| 230 | +driver/code pages can remain resident across runs. |
| 231 | + |
| 232 | +After timing, the app reads back two thumbnails per case, including a |
| 233 | +same-pointer crop change with `uploaded=false`, and verifies real rendered |
| 234 | +pixels against CPU-decoded fixture crops. Screenshots and comparisons are |
| 235 | +excluded from every performance metric. Treat results as valid only when the |
| 236 | +final `complete` record reports both independent visual checks successful. |
| 237 | +The early `fresh-vulkan.jsonl` run predates that check and is invalid because |
| 238 | +its missing batchable shader variant produced blank previews. |
| 239 | + |
172 | 240 | ## Performance benchmarks |
173 | 241 |
|
174 | 242 | GitHub Actions reports performance regressions as warnings, not build failures: |
|
0 commit comments