Repository navigation
feat(video): decode the AV1 movies with dav1d on Windows (#2316) - #2515
Merged
Merged
Conversation
The CPM cache's FFmpeg has no AV1 decoder, so on Windows the movies went to Media Foundation, whose synchronous AV1 decoder is the AV1 Video Extension's software one on every GPU (and a Store download where it is missing). Movies stutter and fall behind their sound there (NotRexed#2316). - host/dav1d_av1.c: dav1d behind the same interface as mf_av1.c, NV12 out, one dav1d_get_picture per submission so its frame threads stay busy. - host/nn_video.c: on Windows, dav1d first (when built in), then Media Foundation; one log line per movie with its decoder, decode time per picture against the time between pictures, and pictures skipped. - scripts/windows/dav1d.sh: dav1d 1.5.4 (the same pins as Linux) built with llvm-mingw into the CPM layout, reproducibly. nx2/build.py links it on the llvm-mingw route when the CPM cache has it; without it nothing changes. - CI: a Linux job builds it and runs the wrapper's test on it under Wine, uploading the CPM folder; the Linux test job installs libdav1d-dev. Co-authored-by: Cursor <cursoragent@cursor.com>
Joey-Magnus
force-pushed
the
feat/windows-dav1d-movies
branch
from
November 11, 2026 21:39
8f78e2e to
53a5046
Compare
|
wouldn't it be better to make use of hardware acceleration instead of relying on software decoding? just curious |
The package job takes the dav1d job's CPM folder and, when the latest release's cpm part lacks dav1d/1.5.4, unpacks that part into tools/cpm with dav1d added (release_parts.py package --cpm-add). make_setup_parts.py then makes a new cpm part, which the release job uploads to builder-parts like any other new part. Once a release carries it, later runs reuse it. Co-authored-by: Cursor <cursoragent@cursor.com>
sheepbun
added a commit
to sheepbun/Bunny-Hood
that referenced
this pull request
Nov 12, 2026
Upstream's new cpm_add (NotRexed#2515) took named_part's result as the part alone and its files under BunnyHoodBuilder/. Here named_part gives the part and the folder its files sit under (this fork's part, or upstream's), so the Windows builder's package job stopped in zipfile with a tuple for a path. cpm_add takes both and reads the part under its own folder. tests/test_release_cpm_add.py stubs the fork's lookup, and adds a part with upstream's folder. Area: scripts, tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Movies stutter and fall behind their sound on Windows (#2316, now tracked in #2503; on an RTX 4060; a GTX 1050 in Discord). The Windows FFmpeg has no AV1 decoder, so the 1080p AV1 movies go to the AV1 Video Extension's software decoder on every GPU. This PR decodes them with dav1d instead, the same decoder Linux uses.
Why it was Media Foundation's software decoder everywhere
--disable-everythingplus h264, vp8, vp9 and opus. It has no AV1 decoder of any kind, and not even the AV1 parser.host/nn_video.ctherefore hands AV1 tohost/mf_av1.c. That file asks Media Foundation for its synchronous decoder first, which is the extension's software one; the hardware decoder is used only when no synchronous one exists. So an RTX 4060 decodes the movies in software too, and a PC without the extension gets the Store dialog (#380).What changed
host/dav1d_av1.cwraps dav1d behind the same open, decode, flush and close interface asmf_av1.c, handing back NV12, whichnn_video.ckeeps as it is. It fetches one picture per submission: a seconddav1d_get_picturewith no data sent in between makes dav1d drain, which waits for every frame in flight and leaves its frame threads decoding one picture at a time. At the end of the stream it drains everything.HOS_DAV1D_THREADS=<n>sets the thread count (default: dav1d's, one per logical core).HOS_NO_DAV1D=1goes back to Media Foundation.host/nn_video.cturns "our own AV1 decoder" into a small table of functions: dav1d first when it is built in, then Media Foundation. Nothing changes when FFmpeg has its own AV1 decoder (Linux).video: a movie of <n> pictures on <decoder>: <mean> ms to decode each (worst <ms>), one shown every <ms> ms; <n> skipped to keep up with its sound. A slower-than-real-time decoder is flagged "(slower than it plays)". The next IN GAME MOVIES STUTTER OR OUT OF SYNCH AUDIO #2316 log will show whether decoding is what falls behind.scripts/windows/dav1d.shbuilds dav1d with llvm-mingw from the same release and source-tree hash thatscripts/linux/deps.shpins (1.5.4; it reads them from there). It installs into the CPM layout<cpm>/dav1d/1.5.4/{include,lib/libdav1d.a,COPYING}. The output is reproducible: two builds give the samelibdav1d.a(SHA-256e6f1adec…7e98with llvm-mingw 20261104). That took clang's-mno-incremental-linker-compatibleand nasm's--reproducible; without them the COFF objects carry timestamps.nx2/build.pycompilesdav1d_av1.cand linkslibdav1d.awhen the CPM cache has dav1d, on the llvm-mingw route only (it is a MinGW library). Without it, or on the Visual Studio route, nothing changes and Media Foundation decodes the movies as before.windows-builder.ymlbuilds dav1d with the pinned Linux-hosted llvm-mingw and runs the wrapper's test on the Windows build under Wine. It uploads the CPM folder as thedav1d-windowsartifact. The Linux test job installslibdav1d-devfor the new test.How dav1d reaches players
Players build the game on their own PC, so dav1d has to reach them in the Builder's cpm part. The cpm part is normally reused from the latest release. This PR makes the release workflow add dav1d to it: the package job takes the
dav1d-windowsartifact and runsrelease_parts.py package --cpm-add. If the latest release's cpm part lacksdav1d/1.5.4, the job unpacks that part intotools/cpmwith dav1d added.make_setup_parts.pythen makes a new cpm part, and the release job uploads it tobuilder-partslike any other new part. Once a release carries it, later runs reuse it. No manual step is needed.I checked this against the real v2.1.0 setup list and the dav1d built here. The new part is
HookshotBuilder-cpm-73d3c9f5a31d.zip(19.6 MB, up from 18.6 MB). It holds the 451 files ofHookshotBuilder-cpm-c30c77e4cf00.zipbyte-for-byte, plus dav1d's 8 files. Two runs gave the same SHA-256, because the zip andlibdav1d.aare both reproducible.Testing
tests/test_dav1d_av1.py(new) uses a 3.8 KB synthetic clip intests/dav1d_av1/: 12 pictures of a formula pattern, encoded losslessly with libaom, so no game data. Every picture must come out in order, with its pts, as the exact NV12 bytes of the pattern. That includes the pictures dav1d holds back until the end, and a second pass after a flush. It runs at 1 and 4 threads, and checks thatHOS_NO_DAV1D=1leaves the decoder closed. It passes on Linux with clang, clang++, gcc and g++ against the system dav1d. It also passes for the Windows build (llvm-mingw, the script'slibdav1d.a) under Wine at 1 thread, 4 threads and the default. Breaking the end-of-stream drain makes it fail.tests/test_cpm_discovery.py: dav1d is used only on the llvm-mingw route, never on the Visual Studio route or Linux, and only with both its header and library present.tests/test_release_cpm_add.py:--cpm-addadds dav1d to a cpm part that lacks it and keeps every file the part had. It leaves a part that already has dav1d to be reused as it is, and adds a new version beside an old one.tests/test_vcore_video_acquire.pyandtests/test_run_all_declarations.pywere updated for the new type and tool.python -I -B tests/run_all.py: 420 passed, 0 failed on Linux, rebased on currentmain.host/nn_video.ccompiles for Windows with llvm-mingw and the cpm part's own FFmpeg headers, with and withoutHOOKSHOT_DAV1D, and for Linux.Not tested
No screenshots: nothing new shows on screen. The movies should just play in sync, and the extension's Store dialog no longer appears once dav1d is in the Builder.