Skip to content

AMD VAAPI(780M / radeonsi)极速、流畅档不遵守 maxrate:实测超目标 2–4 倍,且两档输出几乎相同 #33

Open

Description

@hiclaw

摘要:AMD Radeon 780M(radeonsi)上,UU_VIDEO_ENCODER=vaapi 的极速(1M)与流畅(2M)两档几乎不生效——实测码率分别是目标的 4.3 倍和 2.4 倍,而两档之间只差 0.06%。CPU(libx264)路径没有这个问题。目前参数是 -rc_mode VBR -qmin <cq>,看起来在这套驱动组合下是按质量走的,没有遵守 -maxrate。

环境

  • GPU:AMD Radeon 780M Graphics(Phoenix1,1002:15bf),amdgpu 驱动
  • Mesa 26.2.2-arch1.1,vainfo:Mesa Gallium driver 26.2.2-arch1.1 for AMD Radeon 780M Graphics (radeonsi, phoenix, ACO, DRM 3.64, 7.2.5-3-omarchy)
  • libva 2.24.1 / VA-API 1.24;VAProfileH264ConstrainedBaseline: VAEntrypointEncSlice 可用
  • 内核 7.2.5-3-omarchy,Arch Linux
  • 主机 v0.1.28,UU_VIDEO_ENCODER=vaapi,无回退(日志 encoder=h264_vaapi)

复现

用 v0.1.28 的固定输入探针消除桌面内容变量:

ffmpeg -f lavfi -i "testsrc2=size=2560x1600:rate=60:duration=1" \
       -c:v libx264 -qp 0 -pix_fmt yuv420p clip.mp4

UU_PROBE_INPUT=clip.mp4 UU_PROBE_LOOP=1 UU_PROBE_FRAMES=180 UU_PROBE_QUALITY=N \
  UU_VIDEO_ENCODER=vaapi uuremote-server desktop-video-probe out.h264

输出逐字节可重复(同档重复跑 sha256 相同,CPU 与 VAAPI 都是),所以下表是精确值而非统计结果。

数据

2560x1600@60,180 帧(= 3.000 s),输入 10.7 MB 无损 testsrc2。

档 cq 目标 VAAPI 实际 VAAPI 字节 CPU 实际 CPU 字节
1 极速 30 1M 4,346 kbit/s 1,807,446 828 310,763
2 流畅 26 2M 4,708 1,808,488 1,551 581,735
3 高清 21 8M 5,626 2,358,783 6,125 2,297,045
4 蓝光 17 20M 13,618 5,745,156 15,439 5,789,773
5 自动 18 — 14,573 6,076,902 31,629 11,860,877

目标确实传下去了:

Fixed input probe: source=2560x1600 output=2560x1600 fps=60 cq=30 maxrate=1000k encoder=h264_vaapi
Fixed input probe: source=2560x1600 output=2560x1600 fps=60 cq=26 maxrate=2000k encoder=h264_vaapi

要点

  1. 1 档与 2 档输出只差 1,042 字节(0.06%),关键帧亮度偏移逐位相同——两档在 VAAPI 下产生的是同一个编码结果,-qmin 从 30 变成 26 没有体现出来。
  2. 两档都远超目标(4.3× / 2.4×)。用户想靠降档省流量时,在 AMD VAAPI 上省不下来;CPU 路径同一输入是 828 / 1,551,贴着目标走。
  3. 高档位 VAAPI 反而比 CPU 小(4/5 档 14.6 vs 31.6 Mbit/s),方向和 gpu_encoder_requested() 只读环境变量、不探测显卡,AMD 机器上每次建流都白跑一次 NVENC #10 里「VAAPI 输出字节少约 44%」一致。所以不是整体偏高或偏低,而是低档位的码率控制没有生效。

期望

低档位能接近 -maxrate(即 CPU 路径的行为)。固定输入探针随时可以复测,改了参数之后欢迎让我们对比。


English summary: On AMD Radeon 780M (Mesa 26.2.2 radeonsi), UU_VIDEO_ENCODER=vaapi ignores the 1M/2M viewer presets: measured 4.3x / 2.4x the target bitrate, while tiers 1 and 2 produce a 0.06%-different output with identical IDR offsets. libx264 on the same fixed input hits 828 / 1,551 kbit/s. Reproducible byte-for-byte with the v0.1.28 UU_PROBE_INPUT probe.

Activity

  1. VallierDev commented on Oct 17, 2026

    @VallierDev
    Owner

    谢谢整理成独立 issue(#31、#32 是重复提交,已关闭)。

    1、2 档输出几乎逐字节相同,让我怀疑这不是码率控制没生效,而是两档都已经撞到 QP 上限 51:testsrc2 在 2560x1600@60 下细节和运动都极高,硬件编码器在最大 QP 时可能也压不到 1–2 Mbit/s,而 libx264 的压缩效率更高。可否帮忙跑一个对照(与生产相同的 profile/GOP,只把码率控制换成固定 QP 51):

    ffmpeg -vaapi_device /dev/dri/renderD128 -i clip.mp4 -frames:v 180 \
      -vf format=nv12,hwupload -c:v h264_vaapi -profile:v constrained_baseline -g 30 -bf 0 \
      -rc_mode CQP -qp 51 -f h264 qp51.h264
    ls -l qp51.h264
    • 如果 qp51.h264 也在 1.8 MB 左右:是硬件编码器在这种内容下的下限,降低码率只能靠降低帧率或分辨率(我们可以考虑低档位在 VAAPI 下自动降分辨率)。
    • 如果明显更小:确实是 VBR 路径没有遵守 maxrate,我们会改低档位的码率控制参数。

    另外,用普通桌面内容(终端/网页)跑 1、2 档的结果也很有参考价值,那才是实际使用中的码率。

    Thanks (closed #31/#32 as duplicates). Tiers 1/2 being nearly byte-identical suggests both hit the QP 51 ceiling rather than ignoring maxrate. Could you run the CQP 51 command above? ~1.8 MB means it's the hardware floor for this content (only fps/resolution can go lower); much smaller means VBR really ignores maxrate and we'll change the low-tier rate control. Numbers on ordinary desktop content would also help.

  2. ferstar commented on Oct 21, 2026

    @ferstar
    Contributor

    复测与排查记录(不是修复,记录已排除的方向)。

    环境:Ryzen 7 8845H,核显 Radeon 780M(PCI ID 1002:1900,HawkPoint1,与报告中的 1002:15bf Phoenix1 不同);本机 X11 会话,/dev/dri/renderD128 可用。版本为当前 pr80 分支。

    复现(固定输入探针,与报告一致):2560x1600,180 帧,进程路径 h264_vaapi,tier 1 为 1807446 字节、tier 2 为 1808488 字节,与报告完全相同。

    进程路径,去掉/改动参数(6 秒非循环素材,目标 750k,字节数):

    • 原参数(VBR + -qmin):约 4.1–5.5 Mbps
    • 去掉 -qmin:字节数不变
    • 加 -qmax 51:不变
    • -rc_mode CBR:与 VBR 完全相同(2945761)
    • -rc_mode CQP -global_quality 35:约 14 Mbps,更严重

    结论:进程路径上 -qmin/-qmax/CBR 都不改变低档位码率,FFmpeg 的 CBR 在这个驱动上似乎没有生效。

    in-process 路径(--features inproc-av,桌面 1920x1200,60 fps,300 帧,单次测量)

    配置 tier 1(目标 1M) tier 2(目标 2M) tier 3(目标 8M)
    CPU libx264(进程路径,基线) 0.92 Mbps — 6.7 Mbps
    VBR(原版) 2.4 Mbps 2.4 Mbps 2.6 Mbps
    CBR 优先(已确认 rc_mode=CBR 生效) 2.6 Mbps 2.5 Mbps 5.9 Mbps
    CBR + max_qp=51 2.6 Mbps(1.66 MB) — 5.9 Mbps(无变化)
    VBR + HRD = 目标/4 2.7 Mbps — 1.7 Mbps

    结论:

    1. 低档位(1M / 2M)在 in-process 的 VBR 与 CBR 下都约超标 2.4–2.6 倍。
    2. CBR 能让高档位接近目标,但低档位不变。
    3. max_qp 和 HRD buffer 都无法让低档位收敛。HRD 缩小会让高档位明显偏低。

    目前看来:问题不在 -qmin/-qmax/rc_mode/HRD 这些参数上。下一步需要驱动侧信息,例如 libva trace 中每帧实际下发的 RC 参数与驱动返回的 QP,或 Mesa radeonsi 对 VAEncMiscParameterRateControl 的处理。这部分还没做。

    测试未改动任何代码,相关实验均已撤销。

  3. VallierDev commented on Oct 21, 2026

    @VallierDev
    Owner

    感谢 @ferstar 这么细的排查。进程管线上 -qmin、-qmax 和 CBR 都不改变低档码率,这个结论很有用。

    #80 已合并(21c01d2)。PR 里说 sequence 参数提交顺序修正后,in-process H.264 1M 档降到了 0.97 Mbps,这和你这里表格中 VBR 2.4 Mbps 对不上。想确认一下这组数据用的是哪个提交:a4a58c4 之前还是之后?如果之后也超标,麻烦附上测试素材和命令,我们按素材对比。另外,FFmpeg 的 h264_vaapi 是不是也有同样的参数顺序问题,也值得查一下:可以用 LIBVA_TRACE 看它每帧实际下发的 RC 参数。

  4. ferstar commented on Oct 23, 2026

    @ferstar
    Contributor

    三件事都在另一块 AMD 集显(Cezanne / radeonsi renoir,Mesa 26.2.5-arch1.1,libva 2.24.0,ffmpeg n9.0.2,代码 = origin/main 21c01d2(#80 合并)加两个纯文档提交 19397dd,即含 a4a58c4)上补完了。

    1. 之前那组 2.4 Mbps 是 a4a58c4 之前测的

    时间线可以确认:a4a58c4 是 2026-10-21 21:23 UTC 推进 PR 分支的,我那条评论 21:45 UTC 发表,中间只隔 22 分钟,那套矩阵只可能基于我自己当晚的 push(最后一次是 50fd458)。所以 VBR 2.4 / CBR 2.6 那组数字是顺序修复之前的行为。

    2. 合并后代码在第二块 AMD 集显上复测

    固定输入(进程管线),与 #33 报告同素材同命令——Cezanne 几乎逐项复现 780M 的数字:

    档 目标 780M(#33) Cezanne(本次) Cezanne 字节
    1 1M 4,346 kbit/s 4,284 kbit/s 1,782,340
    2 2M 4,708 4,288 1,784,043
    3 8M 5,626 5,410 2,254,261

    tier 1/2 只差 0.096%,和 780M 的 0.06% 同一现象,与具体 GPU 无关。

    in-process(当前 main):Xvfb 1920x1200 + ffplay 1280x720 testsrc2 窗口(复现 PR 的动态场景),每档 1200 帧:

    档 目标 in-process 同场景进程管线
    1 1M 1.03 Mbps 1.31 Mbps
    2 2M 1.50 1.51
    3 8M 6.00 —

    in-process tier 1 命中目标,PR 的 0.97 在第二块集显上成立;修复前 tier1≈tier2≈2.4 的形态消失。进程管线在这个中等复杂度场景也只有 1.3×,与 PR 表里 HawkPoint1 的 1.48 一致——进程管线的病态 4.3× 超标只出现在 2560x1600 testsrc2 这种极限内容上(两块卡都是)。

    3. FFmpeg h264_vaapi 没有参数顺序问题;超标是 radeonsi VBR 的问题

    LIBVA_TRACE(tier 1、60 帧、GOP 30)显示 FFmpeg 在每个 IDR(2 次)按 sequence → RateControl → HRD → FrameRate → Picture → Slice 提交,非 IDR 帧不带 misc——与 a4a58c4 修复后 in-process 引擎的顺序一致,顺序没有问题。

    RC 内容也确实传到了驱动:tier 1 bits_per_second=1000000, target_percentage=75, window_size=125, min_qp=30, max_qp=0, initial_qp=0, mb_rate_control=2;tier 2 为 bits_per_second=2000000, min_qp=26,其余相同。两档 trace 不同、输出却几乎相同 → 参数已正确下发,是 radeonsi 无视了目标码率。

    同素材 CQP 阶梯(你上一条给的命令,此前没实际跑过)推翻了 QP 51 硬件下限假说:

    RC 码率(180 帧)
    VBR 1M(tier 1 实际产出) 4,284 kbit/s
    CQP 44 2,251 kbit/s
    CQP 47 1,820 kbit/s
    CQP 51 1,583 kbit/s

    VCN 在 QP 51 明明能压到 1.58 Mbps,VBR 却停在 4.28(≈ CQP 41–42 的水平):复杂内容下 radeonsi VBR 的有效 QP 上限远低于 51,-qmax 51/CBR 都救不了(前一条已验证)。这一半值得报给 Mesa/radeonsi。

    复现

    # 素材(同 #33)
    ffmpeg -f lavfi -i "testsrc2=size=2560x1600:rate=60:duration=1" -c:v libx264 -qp 0 -pix_fmt yuv420p clip.mp4
    
    # 进程管线(无 inproc-av 构建)
    UU_PROBE_INPUT=clip.mp4 UU_PROBE_LOOP=1 UU_PROBE_FRAMES=180 UU_PROBE_QUALITY=1 \
      UU_VIDEO_ENCODER=vaapi uuremote-server desktop-video-probe out.h264
    
    # trace RC 参数提交
    LIBVA_TRACE=/tmp/va.log <上面这条>
    
    # in-process 动态场景(注意:无 WM 的 Xvfb 上 SDL 要强制 x11 驱动,否则 ffplay 冻结首帧,
    # 场景看似在放实际是静止图——静止桌面走 keepalive 节奏还会导致 1200 帧探针超时)
    Xvfb :99 -screen 0 1920x1200x24 &
    DISPLAY=:99 SDL_VIDEODRIVER=x11 ffplay -an -left 320 -top 240 -x 1280 -y 720 \
      -f lavfi "testsrc2=size=1280x720:rate=60" &
    DISPLAY=:99 UU_DESKTOP_BACKEND=x11 UU_PROBE_FRAMES=1200 UU_PROBE_QUALITY=1 \
      UU_VIDEO_ENCODER=vaapi uuremote-server desktop-video-probe out.h264   # inproc-av 构建

    XShm 上传路径(Xvfb 无 DRI3);DRI3 零拷贝在 #80 验收时已单独验证过。低档位进程管线的缓解(复杂内容自动降分辨率,或引导 AMD 用户用 inproc-av)数据都在上面,你们定方向。

  5. ferstar commented on Oct 23, 2026

    @ferstar
    Contributor

    补充一个数据点,把上面的归因闭环:极限内容下 in-process 同样躲不过 VBR 天花板。

    把 in-process 引擎直接喂同款内容(Xvfb 2560x1600 + 全屏 ffplay testsrc2@60,live 抓屏走 XShm 上传,日志确认 pipeline=in-process,tier 1):

    路径(同一极限内容) 码率
    进程管线,固定输入探针 4,284 kbit/s
    in-process,live 抓屏 4,158 kbit/s
    CQP 44 对照 2,251 kbit/s

    也就是说 radeonsi VBR 的有效 QP 上限(≈41–42)对任何 VAAPI 客户端一视同仁,与引擎、参数顺序都无关——码率控制的责任完全在驱动侧。inproc-av + a4a58c4 解决的是「正常内容下目标码率被整体忽略」;极限内容下的低档超标两条路径都会有,缓解只能靠低档自动降分辨率(QP 44 都能到 2.25M,降分辨率后 1M 可达)或 CPU 编码。

    复现(in-process 极限场景):

    Xvfb :100 -screen 0 2560x1600x24 &
    DISPLAY=:100 SDL_VIDEODRIVER=x11 ffplay -an -left 0 -top 0 -x 2560 -y 1600 \
      -f lavfi "testsrc2=size=2560x1600:rate=60" &
    DISPLAY=:100 UU_DESKTOP_BACKEND=x11 UU_PROBE_FRAMES=600 UU_PROBE_QUALITY=1 \
      UU_VIDEO_ENCODER=vaapi uuremote-server desktop-video-probe out.h264   # inproc-av 构建

    (此分辨率下 Xvfb+XShm 采集约 17 fps,600 帧约 35 s;1200 帧会撞探针 55 s 超时窗口,属采集节奏,不是引擎问题。)

  6. ferstar commented on Oct 23, 2026

    @ferstar
    Contributor

    勘误 + 结论反转:VBR 没有无视目标;tier 1/2 是钉在 QP 51 的硬件下限上。前两条评论里的 CQP 对照全部作废。

    错误原因:我的 CQP 对照命令没加 -stream_loop -1,60 帧素材只编出 60 帧(ffprobe 核实每个对照输出都是 60 帧),我却按 180 帧 3 秒折算码率,数值全部恰好低了 3 倍。第一楼给的对照命令在 60 帧素材上有同样的坑(-frames:v 180 实际只输出 60 帧,593 KB 与探针的 1.8 MB 完全不可比)。对照必须加循环:

    ffmpeg -vaapi_device /dev/dri/renderD128 -stream_loop -1 -i clip.mp4 -frames:v 180 \
      -vf format=nv12,hwupload -c:v h264_vaapi -profile:v constrained_baseline -g 30 -bf 0 \
      -rc_mode CQP -qp 51 -f h264 qp51.h264

    修正后的数据(180 帧,Cezanne):

    RC kbit/s 字节
    VBR tier 1(目标 1M,探针) 4,753 1,782,340
    VBR tier 2(目标 2M) 4,757 1,784,043
    VBR tier 3(目标 8M) 6,010 2,254,261
    CQP 51 4,747 1,780,137
    CQP 47 5,459 2,047,011
    CQP 44 6,751 2,531,619

    VBR tier 1 输出与 CQP 51 只差 0.12%:tier 1/2 都顶在 QP 51 的硬件下限,testsrc2@2560x1600@60 在 VCN 上就是压不进 ~4.75M。第一楼的「QP 51 下限」判断是对的,我此前的「推翻」收回。radeonsi 的 VBR 在这里没有问题——tier 3(6.0M < 8M 目标)在正常贴目标走,LIBVA_TRACE 里那些参数(1M/2M 目标、顺序)也都如实生效,只是目标低于硬件下限时输出就停在下限。x264 同内容能到 828k 是软件编码器的效率优势。之前评论里「报 Mesa」的建议也相应撤回。

    不受帧数错误影响、仍然成立的部分(帧数均为探针自报):

    • a4a58c4 顺序修复:正常 live 内容 tier 1 从修复前 ~2.4 到修复后 1.03 Mbps,修复真实有效;
    • live 动态场景(1920x1200):in-process 1.03 / 1.50 / 6.00 Mbps,进程管线 1.31 / 1.51(都贴目标);
    • 极限内容(live 2560x1600 testsrc2):in-process 引擎的 max_qp = 44 会挡住低档到达硬件下限——只把 max_qp 改成 51 实测 4.16 → 2.60 Mbps。这个值来源于旧代码的硬编码,建议改成 51,我可以提个一行的 PR;
    • 低档在极限内容上省流量的现实路径仍是降分辨率(2560x1600 的 QP51 下限就有 4.75M;降到 1920x1200 同类内容 in-process 实测 tier 1 可到 1.03M)。

    抱歉前两条的对照数据误导,一条勘误留言比我私下更正有用。

  7. VallierDev commented on Oct 23, 2026

    @VallierDev
    Owner

    谢谢补测和勘误,结论很清楚:

    同意改成 51,欢迎提这个一行的 PR。PR 描述里请附上你实测的 4.16 → 2.60 Mbps(live 2560x1600,tier 1),以及正常内容下 tier 1/2/3 改动前后的对照,确认高档不受影响。min_qp、initial_qp 保持不变。

    低档在复杂内容下自动降分辨率是另一个方向,我们会单独评估,不放进这个 PR。

    🤖 Generated with Claude Code

  8. VallierDev commented on Oct 23, 2026

    @VallierDev
    Owner

    @ferstar 的 max_qp 44 → 51(#102)已随 v0.2.30 发布。复杂画面下,in-process VAAPI 低档现在能压到 VCN QP 51 下限对应的码率;中高档不受影响。

    低档在复杂画面下自动降分辨率仍单独评估。

    🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions