perf(video): extend lazy pool RGBA conversion to planar YUV420P and add x86_64 SSE2 NV12 interleave #2114
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2114
Loading…
Reference in a new issue
No description provided.
Delete branch "video/sse2-nv12-lazy-rgba"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Commit
61cbb67introduced lazy NV12-to-RGBA conversion (pool_rgba()/o->rgba_stale), but only skipped eagersws_scale()whenf->format == AV_PIX_FMT_NV12(Media Foundation on Windows). On Linux (and any CPU FFmpeg decode outputtingAV_PIX_FMT_YUV420P/AV_PIX_FMT_YUVA420P),queue_frame()already constructsp.nv12, yet still ran full 1080p bilinearsws_scale(..., AV_PIX_FMT_RGBA)and allocated an unused 8 MiBp.rgbabuffer on every frame.This PR extends the lazy-RGBA optimization to planar 4:2:0 software decodes and accelerates UV packing:
YUV420P/YUVA420P): Leavesp.rgba = NULLand avoidssws_scale()inqueue_frame()wheneverp.nv12is populated, relying on upstream'spool_rgba(o)(o->rgba_stale) to convert on demand ifReadDecoderOutputImageRgbaor a non-API RGBA memory pool needs RGBA.HOS_VIDEO_RGBA=1forces eager conversion if ever needed for debugging.YUV420P/YUVA420P->NV12UV interleave (#if defined(__x86_64__) || defined(_M_X64)): Replaces scalar per-pixel UV packing with_mm_unpacklo_epi8/_mm_unpackhi_epi8(16 UV pairs per iteration), keeping scalar fallback for tail pixels and non-x86_64 builds.calloczero-fill: Allocatesp.nv12withmalloc(nv12_size)(only zeroing on odd dimensions or unrecognized pixel formats).thread_count(AV1: 4 -> 8, others: 2 -> 4) for Linux/software 1080p60 AV1 cutscenes. PreservesHOS_VIDEO_NO_CATCHUPdefault behavior (0, catch-up enabled).NVIDIA & Cross-Platform Compatibility
<emmintrin.h>gated cleanly by__x86_64__ || _M_X64).pool_rgba()/rgba_stalecleanly for full RGBA pool compatibility on non-NV12 games.Files Changed
host/nn_video.cTest Plan / Verification
git apply --checkagainstmain(ca082bf).host/nn_video.c(clang -fsyntax-only -std=gnu11 -Wall -Wextra -Wno-macro-redefined -Wno-unused-parameter): 0 errors, 0 warnings.Thanks, the core idea checks out. On main, a dav1d/FFmpeg decode on Linux comes out as YUV420P, so
queue_frame()still runs the full 1080psws_scaleto RGBA and allocates 8 MiB per frame, even though OoT only reads the NV12. Leavingp.rgbaNULL lets acquire setrgba_stale, andpool_rgba()converts later only if RGBA is asked for. It goes through the sameSWS_BILINEARsws with the same default matrix as the old eager path, so colours should not change. The SSE2 interleave is fine too: SSE2 is in the x86-64 baseline, so no runtime CPU check is needed. The loads stay inside the row (x + 16 <= w / 2), strides come fromlinesize, the scalar loop handles the tail, and AArch64 and other hosts just take the scalar loop. Since it's a plain byte interleave it can't drift from the scalar output. A small check comparing the two would still be cheap to add, but I'm not asking for it.What I'd like before this goes in:
HOS_VIDEO_LOG=1shows how far the picture falls behind) on a 4-core machine as well as your own? Note that #1929 on main already stops the movie thread holding a virtual core while dav1d decodes, which was most of the Linux stall.git apply --checkand-fsyntax-only. This changes what Linux movies go through, so please report one cutscene on Linux: it plays, the colours look the same, and it keeps up with the sound.Smaller points are inline (
env_onfor the new switch, and odd sizes). The branch is behind main (env_on, #1929'shos_vcore_awayinnn_video.c), but the hunks don't overlap, so it should merge cleanly. One wording note: the description says MSVC, but the Windows build is llvm-mingw. The_M_X64guard does no harm.This doubles the decoder threads (AV1 4→8, others 2→4) and isn't related to the lazy RGBA. dav1d's frame delay grows with the thread count, so this also means more memory and more pictures decoded ahead. That may be fine, but it needs a measurement, ideally on a 4-core machine too, or a separate PR. Otherwise I'd keep 4/2 here.
@ -359,3 +367,4 @@}if ((w | h) & 1) memset(p.nv12, 0, nv12_size);uint8_t *dst[4] = {p.rgba, NULL, NULL, NULL};int dst_stride[4] = {w * 4, 0, 0, 0};Main now has
env_on()fromhookshot_env.h(already included in this file on main), which is the one rule for these switches:0/false/off/nomean off. Its header also says to read a switch once into a static, while this callsgetenv+atoion every frame. Something likestatic int force = -1; if (force < 0) force = env_on("HOS_VIDEO_RGBA");, asvideo_log()does, would match. Nit, but cheap.@ -374,1 +390,4 @@_mm_storeu_si128((__m128i *)(uv_row + 2 * x), _mm_unpacklo_epi8(u16, v16));_mm_storeu_si128((__m128i *)(uv_row + 2 * x + 16), _mm_unpackhi_epi8(u16, v16));}#endifNit, low reach (OoT's movies are 1080p): with lazy RGBA, an odd-sized YUV420P picture now gets its RGBA from this NV12, not from the source planes. The last chroma column (odd w) is dropped, and the last chroma row (odd h) isn't stored, while
pool_rgbareads(h+1)/2chroma rows. Zero chroma also shows as green, not grey. Either keep the eager sws path for odd sizes (lazy_rgba = ... && !((w | h) & 1)), or fill the chroma with 128 and round the size up. MF's NV12 path already behaves this way on main, so this isn't a blocker.The main idea is sound: software decodes produce YUV420P, and every reader on main handles a picture with no RGBA (
pool_rgba(),nn_video.c:204-216and the other call sites). The SSE2 loop is correct for unaligned data, odd widths and non-x86. Three things:YUVA420Ploses its alpha through the NV12 path. Keep it on the eager path, or drop it fromlazy_rgba.nn_video.c:291) is unmeasured and can compete with the game on a 4-core Deck. Please drop it or measure it.HOS_VIDEO_RGBAis undocumented and read withgetenv/atoievery frame, against #2042'senv_onconvention.github-sync referenced this pull request2026-11-12 06:22:54 +00:00