Repository navigation
Conversation
…'s timestamp, outside the decoder lock One frame at a time (max_frame_delay 1, 2 threads) took 9-20 ms a packet of Ocarina of Time's 1080p60 title movie on an M1 Max, single packets up to 134 ms; more threads on one frame did not help. 8 threads and 8 frames in flight: at most 26 ms. A picture then comes some packets after its own: mav1_decode_ts carries the packet's timestamp through dav1d (Dav1dData.m), the wrapper queues pictures (mav1_next) and gives the frames still decoding at the end of the stream (mav1_drain), asking dav1d once per input, since a second ask drains. cv_dec_submit decodes AV1 under a decode lock of its own, so Release and Acquire do not wait on dav1d; a packet may add up to CV_BURST pictures past CV_HOLD, where packets are still refused. t_codec_video: a lone frame's picture comes at the end of the stream; 20 packets give 20 pictures in order with their timestamps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lel and pictures follow the movie's clock
) replaces the pull request heads Upstream landed Ocarina of Time in main (chain adec7d81d970: "holds 60 fps in Kokiri Forest, 4 GB less") and closed the pull requests it folded in, with reworded and partly different code. Mixing a pull request head with its landed version hunk by hunk silently broke things (two GlobalToStorage tables, a duplicate AF2ip, two mtk_pso definitions), so the rule here is one version per feature: - Pull requests upstream closed (seedds#1299, seedds#1300, seedds#1312, seedds#1343, seedds#1345, seedds#1353, seedds#1354, seedds#1359, seedds#1360, seedds#1373, seedds#1428, seedds#1430, seedds#1434): their hunks are taken out of our side before the three-way merge, so upstream's landed version stands alone. Gone with them: bindless pool table patching (reg_bump), the ghost table's per-submission hold, the growing raw pool table and its write-log sizing, seedds#1428's pool_buf spares (upstream's pool_table_take is the same idea), the ixc-based indirect mesh draw (upstream uses mdx), and their tests (t_pool_table, t_mesh_indirect, t_gmem_raw, finalized_bindless_pool, test_bnsh_nvn2, test_ndve). - Open pull requests that upstream's landing supersedes: seedds#1394 (upstream's mtl_pool_addressed) and seedds#1393 pool_skip_retired (upstream's budgeted pool walk; the profile does not use it) are dropped with their tests and docs. seedds#1368's mesh pipeline prewarm is dropped too: upstream kept the background build in nvn_geometry.inc, and the prewarm's mtk_build / mtk_claim / g_warm_mps went with it. - Kept, as upstream does not have them: NXForge's own features (GPU watchdog, present filters and anti-aliasing, aniso floor, quick settings, pipeline archive compaction, upload breakdown, memory report lines) and the still open pull requests seedds#1304, seedds#1305, seedds#1308, seedds#1320, seedds#1331, seedds#1332, seedds#1355, seedds#1356, seedds#1369, seedds#1376, seedds#1377, seedds#1379, seedds#1382, seedds#1390, seedds#1391, seedds#1392, seedds#1414, seedds#1420, seedds#1436, seedds#1439, seedds#1446, seedds#1453. - Combined where both sides changed the same lines: GW_N 1 << 16 with seedds#1320's wide-write intervals (GW_PAGES = SBW_WPAGES, 128 slots), the CPU upload log with upstream's linear upload route, arch_open_end before the compaction limit, cbn_prog in geo_gs_one (seedds#1382). - Translator: upstream's sources and binaries, plus seedds#1414's InstEmitAmpereMem diff and the LDSM SubgroupBarrier in InstEmitAmpereTensor.cs. The binaries and dmsh_chain.lock are upstream's until the Mac rebuilds them (dotnet publish, tool_lock --update-lock, dmsh_chain --header); dmsh_chain.h is regenerated for now. - RED_ONCE keys: upstream took 153 and 154, so the present anti-aliasing lines move to 156 and 157 and seedds#1420's packaged-entry line to 158 (array 159). - Host: hle_net.c and t_net_eventfd.c are upstream's (its eventfd poll waits out the whole timeout, which our 5d7920c fixed another way). - Kept ours: dm/tools/romfs_image.py and its test, so the 26 GB RomFS image is not packed again. dm/tools tests on Linux: same failures as on 875449e (Linux-only quirks), plus test_nlg4 and test_dmsh_pool_arrays, which fail on upstream main too. Not yet built on the Mac. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qteK29T18U8p1c9LsVY8g
…d on 4 threads with 3 frames at once, so movies no longer stutter at a steady 60 fps (PR #2240): a submission queues its packet and returns, as the SDK's does (its decoder is another process). Decoded inside Submit…InputBuffer, a packet held the client's thread for the whole decode; Ocarina of Time's movie reader, which also gives the shown pictures back, was held up to 120 ms, and its title movie (AV1, 1080p at 60 fps, dav1d) showed a picture again in 546 and 569 of about 2,900 frames on an M2 Pro at a steady 60 fps - hle_codec_video.c: cv_worker decodes the packets cv_dec_submit queues. The submission-ready event and the refusal count the pictures waiting for a released buffer, the packets queued and the one in decode together (cv_pending), so a client sees the same event whatever the decoder's speed. The decoder's thread waits for room instead of dropping a picture, and signals the sync point events itself. Stop and Flush drop the queued packets and number the stream anew, so the pictures of a packet still in decode are dropped; Finalize joins the thread. Guest calls take the decoder's lock as a host lock. One log line when a run of 600 packets left more released buffers waiting than the decoder has (`the decoder is behind the movie`), and the totals at Finalize - hle_movie_av1.c: dav1d on 4 threads, 3 frames at once (15.4 -> 7.1 ms a packet over the title movie's heaviest five seconds on the M2 Pro). Pictures come out later than their units, so each unit's time rides with its picture (mav1_picture.ts) and mav1_next gives the pictures after a unit, draining when no packet waits behind it or the stream ends. A drain that has given a picture goes to its end: dav1d 1.5.4's drain, left halfway by new data, leaves a live frame's task never run and dav1d_submit_frame waits for ever (a workaround of the library's bug, in the code comment and the reference entry) - t_codec_video.c: the event and the refusal with the packets decoded or not, which thread signals what, flushes and a Finalize with undecoded packets, AV1 key frames through the protocol with their times in order, a mid-stream drain and 600 rounds of drains at random places. The AV1 backend loop checks mav1_backend() rather than its pass: on a machine that reports hardware AV1 but cannot make the session (CI's, -12906) the VideoToolbox pass decodes with dav1d and gave its picture only on the drain. The four Initialize calls pass empty parameter sets: ccd834e added those arguments to both overloads and the test no longer compiled - docs/reference/movies.md: `nn::codec` packets decoded inside the submission; dav1d decoded one frame at a time; the entries they change. The Ocarina of Time note, Kirby Air Riders' busy line; symptom index regenerated PR #1377 (MetaIX) found the frame-threading cause first and carried each packet's time through Dav1dData.m the same way; it decoded on the client's thread under a lock of its own, and is superseded by this. Checked on an M4 Pro (48 GB), macOS 27.2: t_codec_video 3 runs, and 5 runs under ThreadSanitizer with no report; the rest of the host tests' run list (t_hid does not link on main: hle_hid.c's dm_gfx_addr_note, dm_gfx_bufsnap, probe_atrace) and tsan-threads; test_docs; lint_c.py tidy on both changed files, 0 findings. Games, headless, A/B with hosts from the tree at 918e9f3^ (chain cb2e91c138e3, libraries on disk) without and with this host diff: Ocarina of Time's title for 54 s with DM_AV1_DAV1D=1 and DM_PROBE on the player's advance (main+0x3b61ed8 failed, main+0x3b61f80 good): 361 failed of 3,041 before, 0 of 3,057 after, the same frame times and red lines. Mario Kart World's cup opening movie (VP9 2560x1440, libvpx): all 1,423 pictures on both, frame p95 18.8-19.3 ms in the movie on both, 3.24 ms a packet and 13.4 ms the longest, no new red line. The PR's own runs: Ocarina's title on an M2 Pro, ten runs, 0 or 1 failed advance; DecodeAsync 0.10 ms at the median and 1.1 ms at the 99th percentile, from 1.9 and 120. Not run: Fire Emblem Fortune's Weave (SDK 22's native images; its shader split on disk is older than the map), Donkey Kong Bananza, Mario Tennis Fever, Kirby Air Riders, an iPhone or iPad decoding AV1 with dav1d, regress.py (games chosen by hand: the impact map has no usage data for the codec games) Co-authored-by: SailorSnoW <49660929+SailorSnoW@users.noreply.github.com>
|
Thank you for this. You found the cause first: dav1d scales by frames in parallel, not by threads on one frame, and you carried each packet's time through The difference is where the decode runs. #2240 gives each decoder a thread of its own, since the SDK's #1379 is built on this PR's interface and on decoding in the game's thread. If you'd like it considered, please rebase it onto main: the late-picture skip would sit where the decoder thread hands pictures back. It will get its own design review then. |
What and why
Symptom: on a Mac without hardware AV1 (before M3), dav1d decodes a heavy 60 fps movie too slowly for the game to show it at 60. Ocarina of Time's 1080p60 title movie (
0400486024DEC000v131072, profile in #1299) took 9–20 ms a packet on average in-game on an M1 Max, with single packets up to 134 ms, against the 16.7 ms a frame allows. For reference, ffmpeg's libdav1d needs 9.3 ms of CPU a frame for that movie on one thread.Cause:
max_frame_delay1, 2 threads). More threads on one frame barely help (6 measured the same as 2): dav1d scales by decoding frames in parallel.Release…andAcquire…calls waited behind it.Change:
mav1_decode_tspasses the packet's timestamp through dav1d (Dav1dData.m) to its picture (a newtsfield inmav1_picture). The wrapper queues pictures;mav1_nextgives the others andmav1_draingives those still decoding at the end of the stream.cv_dec_submitdecodes AV1 under a decode lock of its own (lock order: decode lock, then the decoder lock) and takes the decoder lock only to hold the pictures. Reset and Finalize take the decode lock first.CV_BURST(8) pictures pastCV_HOLD; packets are still refused atCV_HOLD.Checked
make -C dm/host/tests GAME=0100000000010000 runpasses (Ocarina's profile is not on main; this test does not depend on the game).t_codec_videochanges in two ways:mav1_drain);Docs
A follow-up PR, stacked on this one, hands pictures out by the movie's clock when the game shows fewer a second than the movie has (Ocarina's 60 fps movies at 28 fps ran at 0.44×).
🤖 Generated with Claude Code