Skip to content

virgl: 2D_ARRAY lookups on samplers without a view read a 2D texture (motion-adaptive deinterlacing) - #615

Merged
gillesgoetsch merged 6 commits into
mainfrom
virgl-samp-array
Oct 29, 2026
Merged

gillesgoetsch merged 6 commits into
mainfrom
virgl-samp-array

Conversation

@gillesgoetsch

@gillesgoetsch gillesgoetsch commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

Lane v4l2-vavpp (re-review of #613), VM row by lane vavpp-row. Rebased on main c29fd80 (3.2.12). Not merged by the lane.

What was wrong. Mesa's motion-adaptive deinterlacer (vl_deint_filter.c, the same in Mesa 26.2.4 and 26.2.5) declares its samplers without views (DCL SAMP[0..3], no SVIEW) and reads the decoded pictures, plain 2D textures, with 2D_ARRAY lookups. virgl-shader-array-sampler-2d-view.patch maps a 2D_ARRAY sampler on a plain 2D view to sampler2D only where the shader declares the view 2D_ARRAY (Mesa's video compositor does). Here nothing was declared, so GL got a sampler2DArray on a 2D texture and read zeros (QEMU's log, once: GLD_TEXTURE_INDEX_2D_ARRAY is unloadable ... using zero texture). Every deinterlaced picture was Y=U=V=0: green in mpv's --vf=vavpp default and FFmpeg's deinterlace_vaapi (motion-adaptive), also with Mesa's VA driver alone. This is OmacVM's runtime, not Mesa.

Fix. In a shader that declares no sampler views, a 2D_ARRAY lookup counts as the 2D_ARRAY declaration of that sampler (samplers_2d_array_declared), so the shader info, the key bit and the 2D form follow as for declared views. The key bit is still set only for a plain 2D view, so real 2D arrays keep sampler2DArray. Not for samplers indexed indirectly (as before).

Test. Tests/virgl/test-const-ubo.c case 25 (Apple's software renderer, never the GPU): a SAMP-only 2D_ARRAY lookup on a 2D view, then the declared-view shader of case 23, then the SAMP-only one again, then a real two-layer 2D array in that slot read at layer 1.

renderer stripe 0 SAMP-only 1 declared view 2 SAMP-only 3 real array, layer 1
current patch black (0xff000000) green black blue
this branch green green green blue

run-regressions.py on this branch: all OK (case 25 in all three constant modes, 24 random seeds the same). Built from the 3.2.12 set's patched virglrenderer 1.3.0 with this patch swapped in; the patch applies in the chain without offset or fuzz and gives the same tree. SHA256SUMS updated.

VM row (2026-10-29, Mac mini M4, macOS 27). One hidden test VM (APFS clone of a kept test VM, 4 CPU / 2 GB, Update VM to 3.2.12 by this branch's app; Mesa 26.2.5, FFmpeg 9.0.2, mpv 0.41, Chromium 153, Chrome 156), started by the test-identity app: first the published 3.2.12 (relabeled, c29fd80), then a build of this branch (58908ea, runtime from source, build tests passed). Clips made in the VM with FFmpeg from testsrc2 at 50 fields/s woven by tinterlace=interleave_top into 25 fps, H.264 (x264): c1080 1920x1080 coded as frames, i1080 1920x1080 and i576 720x576 coded interlaced (MBAFF, tff). References: FFmpeg's CPU bwdif and yadif (send_frame, tff, every frame).

(1) FFmpeg deinterlace_vaapi (rate frame) on VA-API decoding, OmacVM's driver (the VM's default; Mesa's driver alone the same green before), read back as NV12; PSNR average over the frames, best of offsets 0-3:

c1080, OmacVM's driver frames green frames Y/U/V (middle frame) PSNR vs bwdif vs yadif
3.2.12, default 47 47 0 / 0 / 0 5.07 dB 5.07 dB
3.2.12, motion_adaptive 47 47 0 / 0 / 0 5.07 dB 5.07 dB
this branch, default 47 0 126.3 / 126.6 / 125.7 28.14 dB 28.07 dB
this branch, motion_adaptive 47 0 126.3 / 126.6 / 125.7 28.14 dB 28.07 dB
CPU bwdif (reference) 50 0 126.2 / 126.6 / 125.6 40.45 dB
the frames as decoded (no deinterlacing) 50 0 30.37 dB

28.14 dB compares different fields: the clip is flagged progressive, and FFmpeg's deinterlace_vaapi takes a frame without the top-field-first flag as bottom field first and, at rate frame, outputs the bottom field (the second, 20 ms later), while bwdif (parity tff, send_frame) outputs the top one. So it is a low mark, not the deinterlacer's quality.

(2) mpv --gpu-api=opengl --vo=gpu, full screen, paused at 1.0 s, mpv's screenshot-to-file (video and window): --hwdec=vaapi --vf=vavpp ("Selected deinterlacing algorithm: 3") and --hwdec=vaapi --deinterlace=yes give the same picture. c1080 on 3.2.12: all green (Y/U/V 55/106/100 in the PNG: Y=U=V=0 through BT.709), 9.9 dB against mpv's CPU deinterlacer (--hwdec=no --deinterlace=yes); on this branch: the right picture (28.7 dB against the CPU deinterlacer, which shows the other field), testsrc2's clock reads 1.020 / frame 51 without combing (the second field; the CPU deinterlacer shows the first, 1.000 / 50; without deinterlacing the digits are combed). Against testsrc2's own picture of the field each shows, in the colours mpv shows the clip in (its log: bt.709/bt.709/bt.1886/limited): this branch 33.0 dB (second field), mpv's CPU deinterlacer 34.2 dB (first field), 3.2.12 9.9 dB. Command: work/vavpp-row/kit/mac/psnr-truth.sh (Mac FFmpeg 9.0.1, testsrc2=size=1920x1080:rate=50, frame 50 or 51, format=yuv420p,scale=in_color_matrix=bt709:out_color_matrix=bt601,format=rgb24,format=yuv444p against the screenshot as yuv444p, psnr); output work/vavpp-row/out/psnr-truth.txt. An earlier version of this PR gave 26.2 / 26.4 / 9.4 dB: that is the same script's other reference, testsrc2 drawn as rgb24, whose colours are not mpv's (its lines are in the same file and match the first run to the digit, out/psnr-truth-first.txt).

(3) qemu.log: GLD_TEXTURE_INDEX_2D_ARRAY is unloadable once on 3.2.12, none on this branch. No new kind of line: the same kinds on both (342 vt-video: decode failed: -8969, 18 decode failed: 1, both capped, below; video: zero-copy failed, copying this frame with the CPU 1 and 3 times).

(4) Unchanged, row for row: video-geometry (mpv, mpv gpu-next, Chrome 156 on Wayland; H.264, VP9, HEVC at 1920x1080, 1280x720, 854x480, 640x360, 1000x562) 45/45 exact on both; VPP rows (scale_vaapi, converting vaGetImage on 8 clips, also OMACVM_VA_LOGICAL=0) 32/32 exact and texels 16/16 exact on both; glmark2 --validate 11 Success, 0 Failure, 16 scenes, score 2659 / 2636; Chromium WebGL1/2 60 s each 3594/3599 and 3604/3601 frames, no context lost; no host GPU fault, no core in the VM, tccd "Notifying" 0.

Not fixed here (found by these rows, the same on 3.2.12): H.264 coded interlaced (i1080, i576) is green in every VA-API path, with or without deinterlacing. Most frames: FFmpeg's bob (a plain copy in Mesa 26.2) has 30-36 of 50 frames green on both runtimes; motion-adaptive 47 of 47 on 3.2.12, 32-37 of 47 here; mpv's vavpp on i576 is still all green on this branch (mpv's frame at 1.0 s). VideoToolbox returns errors on these streams (vt-video: decode failed: -8969, its "bad data"): qemu.log has 20 decode failed lines per run of these two clips (19 -8969, 1 status 1; 18 runs per side: 12 FFmpeg, 6 mpv; none in the c1080 runs), but 20 is the runtime's log cap (virgl-videotoolbox-decode-async.patch logs the first 20 failures per stream), so the log does not say how many pictures failed. Cause not known when this was written; found later and fixed in #619 (the Mac's hardware decoder refuses interlaced H.264, the VA shim now refuses such pictures). At the 3.2.13 merge this PR's interlaced CHANGELOG sentence and docs item were replaced by #619's.

Raw rows: work/vavpp-row/out (summary.txt, before/, after/: deint.jsonl, mpv.jsonl + screenshots, vg.*, vpp.jsonl, glmark2.txt, chromium.txt, qemu.log). Kit: work/vavpp-row/kit.

Docs: docs/architecture/graphics.md row; docs/video-decode.md: the deinterlacing known issue says fixed since 3.2.13 with the rows above (GStreamer's vadeinterlace with method=adaptive named as the same Mesa path, not measured; no row ran GStreamer, and vapostproc is not listed), and a new known issue for H.264 coded interlaced; CHANGELOG 3.2.13 (unreleased).

Track: work/tracks/v4l2-vavpp-odd.md (VM row at its end).

gillesgoetsch added a commit that referenced this pull request Oct 29, 2026
Mesa's deinterlacer declares its samplers without views (SAMP only) and
reads the decoded pictures, plain 2D textures, with 2D-array lookups.
virgl-shader-array-sampler-2d-view.patch maps such lookups to 2D only for
samplers declared with a view, so the Mac's GL read zeros. A renderer
test shows it (test-const-ubo.c case 25: black on the current runtime);
the fix is #615, not yet measured in a VM. The bullet said the Mac's GL
did not sample the deinterlacer's own buffer: wrong.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gillesgoetsch gillesgoetsch reopened this Oct 29, 2026
gillesgoetsch and others added 4 commits October 29, 2026 16:06
test-const-ubo.c case 25: a fragment shader that declares only SAMP[0]
(no SVIEW) and looks up 2D_ARRAY, as Mesa's motion-adaptive
deinterlacer (vl_deint_filter.c) reads the decoded pictures, with a
plain 2D view bound; between two of its draws one with the declared
view of case 23; then a real 2D array in that slot, read at layer 1.

On the current runtime stripes 0 and 2 read black (0xff000000): the
2D-view patch only maps samplers declared with a view. The fix follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mesa's motion-adaptive deinterlacer (vl_deint_filter.c) declares its
samplers without views (SAMP[0..3], no SVIEW) and reads the decoded
pictures, plain 2D textures, with 2D_ARRAY lookups. The 2D-view patch
mapped such lookups to 2D only for samplers declared 2D_ARRAY with a
view, so here GL got a sampler2DArray on a 2D texture and read zeros
(QEMU's log: "GLD_TEXTURE_INDEX_2D_ARRAY is unloadable ... using zero
texture"): every deinterlaced picture was Y=U=V=0, green (mpv's vavpp
default, FFmpeg's deinterlace_vaapi).

In a shader that declares no sampler views, a 2D_ARRAY lookup now counts
as the 2D_ARRAY declaration of that sampler, so the key bit and the 2D
form follow as for declared views. Real 2D arrays are not touched (the
key bit is set only for a plain 2D view).

Tests: test-const-ubo.c case 25 (black before, right after; a real 2D
array still reads its layer), run-regressions.py all OK. Not yet
measured in a VM (motion-adaptive deinterlacing in mpv/FFmpeg).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ys green

The VM row of #615 (Mac mini M4, a hidden test VM, published 3.2.12
before, this branch after): FFmpeg's deinterlace_vaapi and mpv's vavpp
no longer give green pictures on interlaced video coded as frames, and
QEMU's log has no 'unloadable' line. H.264 coded interlaced (x264 MBAFF,
1080i and 576i) is green in every VA-API path on both runtimes:
VideoToolbox refuses most of its pictures (-8969). Listed as its own
known issue, with CPU decoding as the way round it.
… cause not known

GStreamer's vapostproc is out of the fixed list (no row ran GStreamer, and
the element that deinterlaces is vadeinterlace): vadeinterlace with
method=adaptive is named as the same Mesa path, not measured.

mpv against the test pattern: 33.0 dB (this branch, second field) and
34.2 dB (mpv's CPU deinterlacer, first field), 3.2.12 9.9 dB, from
work/vavpp-row/kit/mac/psnr-truth.sh with the colours as mpv shows the
clip (BT.709). The first numbers (26.2 / 26.4 / 9.4 dB) compared against
testsrc2 drawn as RGB, whose colours are not mpv's.

Interlaced H.264: "most" now rests on the green-frame counts (bob 30 to
36 of 50, was 30 to 35), the -8969 lines are capped at 20 per stream by
the runtime, and the cause is not known yet.
…ommand

Drops GStreamer's vapostproc (not measured), uses psnr-truth.sh's 33.0 and
34.2 dB (3.2.12 9.9 dB), and words interlaced H.264 by its green-frame
counts (30 to 36 of 50 with bob), cause not known yet.
@gillesgoetsch
gillesgoetsch merged commit a036aa9 into main Oct 29, 2026
3 checks passed
gillesgoetsch added a commit that referenced this pull request Oct 29, 2026
@gillesgoetsch
gillesgoetsch deleted the virgl-samp-array branch October 29, 2026 23:16
@gillesgoetsch gillesgoetsch mentioned this pull request Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant