0%

【Linux技术分享】dxva模块学习-初始化和渲染

dxva模块学习-初始化和渲染

dxva模块学习

通过这个文章可以知道vaapi是什么,wine下的dxva2->vaapi流程是如何工作的

dxva2概念

window下的dxva2

  • DXVA2(DirectX Video Acceleration 2.0,即DirectX视频加速2.0)是一个由微软制定的视频硬件加速接口规范。它的核心作用是将视频解码、处理等计算密集型任务从CPU卸载到GPU上执行

wine下的dxva2

  • Wine主要将DXVA2的调用翻译为VA-API(Video Acceleration API)。VA-API是Linux等系统上主流的视频硬件加速接口,被Intel、AMD和NVIDIA的开源驱动广泛支持
  • wine下编译dxva2模块时,需要添加VA-API的头文件和库文件,才能正常编译,apt install libva-dev

vaapi

  • VA-API(视频加速API)是一个开源的应用程序接口(API),主要目标是在Linux等类Unix系统上,让应用程序能够利用GPU进行视频的硬件加速编解码和处理,类似window下的dxva2
  • libva库会根据你的GPU型号,将任务转交给对应的硬件驱动(如Intel的intel-media-driver或AMD的mesa驱动),最终由GPU硬件完成实际的解码或编码工作
  • vaapi在系统中使用首先系统必须支持硬件驱动解码,然后安装vaapi的安装包,apt install va-driver-all vainfo
  • 安装后使用vainfo命令查看是否安装成功,其中libva info: 就是显卡视频驱动,vainfo中左侧VAProfile*就是支持的视频解码格式,右侧能对这类格式做什么操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    vainfo 
    Trying display: wayland
    Trying display: x11
    libva info: VA-API version 1.22.0
    libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/radeonsi_drv_video.so
    libva info: Found init function __vaDriverInit_1_22
    libva info: va_openDriver() returns 0
    vainfo: VA-API version: 1.22 (libva 2.22.0)
    vainfo: Driver version: Mesa Gallium driver 25.0.7-2 for AMD Radeon Graphics (radeonsi, phoenix, LLVM 19.1.7, DRM 3.61, 6.12.94+deb13-amd64)
    vainfo: Supported profile and entrypoints
    VAProfileH264ConstrainedBaseline: VAEntrypointVLD
    VAProfileH264ConstrainedBaseline: VAEntrypointEncSlice
    VAProfileH264Main : VAEntrypointVLD
    VAProfileH264Main : VAEntrypointEncSlice

视频编码

dxva2在wine下调用总体流程

mpv 调用 dxva2.dll 接口

├─[1] DXVA2CreateDirect3DDeviceManager9(device, &manager)
│ └─ main.c 中创建 device_manager

├─[2] IDirectXVideoDecoderService_CreateSurface(1920, 1088, NV12, POOL_DEFAULT)
│ └─ videoservices.c: DirectXVideoAccelerationService_CreateSurface() [L116]
│ └─ IDirect3DDevice9_CreateOffscreenPlainSurface() [L129]
│ └─ 创建 D3D9 offscreen plain surface

├─[3] vaapi_h264decoder_create() [vaapi-h264.c L1030+]
│ ├─ pvaCreateConfig() 创建 VA-API 解码配置
│ ├─ vaapi_create_surfaces() 创建 VA surface (VA-API 侧)
│ │ └─ 1080p: NV12, 1920x1088, 26 surfaces
│ │ └─ 4K: NV12, 3840x2160, 26 surfaces
│ └─ pvaCreateContext() 创建 VA-API 解码上下文

├─[4] 每帧解码循环:
│ ├─ WineVideoDecoderH264_BeginFrame(surfaceIndex) [L849]
│ │ └─ pvaBeginPicture() 开始解码一帧
│ │
│ ├─ WineVideoDecoderH264_ExecuteBuffer() [L818]
│ │ ├─ process_picture_parameters() → pvaCreateBuffer + pvaRenderPicture
│ │ ├─ process_quantization_matrix() → pvaCreateBuffer + pvaRenderPicture
│ │ ├─ process_slice_control_buffer() → pvaCreateBuffer + pvaRenderPicture
│ │ └─ process_data_buffer() [L602]
│ │ ├─ generate_sps_pps() → pvaCreateBuffer + pvaRenderPicture (SPS/PPS)
│ │ └─ pvaRenderPicture() → 渲染比特流数据
│ │
│ ├─ WineVideoDecoderH264_EndFrame() [L878]
│ │ └─ pvaEndPicture() 完成解码 → 硬件解码后的 NV12 数据在 VA surface 中
│ │
│ └─ WineVideoDecoderH264_LockImage() [L900]
│ ├─ pvaSyncSurface() 等待 GPU 解码完成
│ ├─ pvaGetImage() 从 VA surface 读取 NV12 数据到 VAImage
│ └─ pvaMapBuffer() 映射到 CPU 可访问的 buffer

└─ 解码完成,NV12 数据在 D3D9 offscreen surface 中

初始化

  • Direct3DCreate9: 创建 D3D9 对象,负责管理GPU资源、创建3D设备和处理图形渲染。它是DXVA2所有操作的基础
  • DXVA2CreateDirect3DDeviceManager9:函数的作用是创建一个 Direct3D 设备管理器(Direct3D Device Manager)的实例。
  • DXVA2CreateDirect3DDeviceManager9:创建 Direct3D 设备管理器,这是 DXVA2 的入口点。
  • IDirect3DDeviceManager9:ResetDevice将 D3D9 设备绑定到管理器。
  • IDirect3DDeviceManager9:OpenDeviceHandle打开设备句柄。
  • IDirect3DDeviceManager9:GetVideoService获取 IDirectXVideoDecoderService 接口,这是 DXVA2 解码服务的核心接口。
  • IDirectXVideoDecoderService:GetDecoderDeviceGuids查询显卡支持的解码格式 GUID 列表。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    0024:trace:dxva2:DllMain 0x7e440000,1,0x63fd24
    0024:trace:dxva2:DXVA2CreateDirect3DDeviceManager9 (0x63fe9c, 0x63fea8)
    0024:fixme:dxva2:Direct3DDeviceManager9_ResetDevice (0xbdb7c8)->(0xb4b7a0, 3735928559): semi-stub
    0024:fixme:dxva2:Direct3DDeviceManager9_OpenDeviceHandle (0xbdb7c8)->(0x63fea4): semi-stub
    0024:fixme:dxva2:Direct3DDeviceManager9_GetVideoService (0xbdb7c8)->(0xb4b7a0, 0x40b878, 0x63fea0): semi-stub
    0024:trace:dxva2:vaapi_videoservice_create Using VAAPI in X11 mode.
    libva info: VA-API version 1.22.0
    libva info: Trying to open /usr/lib/i386-linux-gnu/dri/radeonsi_drv_video.so
    libva info: Found init function __vaDriverInit_1_22
    libva info: va_openDriver() returns 0
    DXVA2 decoder service obtained.
    0024:fixme:dxva2:DirectXVideoDecoderService_GetDecoderDeviceGuids (0xbdb81c/0xbdb818)->(0x63fe94, 0x63fe98): semi-stub
    0024:trace:dxva2:WineVideoService_GetDecoderDeviceGuids (0xbdb7e8, 0x63fe94, 0x63fe98)
    Number of supported decoder GUIDs: 1
    [0] {1B81BE68-A0C7-11D3-B984-00C04F2E73C5}
    H.264 (DXVA2_ModeH264_E) is SUPPORTED.
    DXVA2 initialization test PASSED.
    0024:trace:dxva2:DirectXVideoAccelerationService_Release (0xbdb818)->() Release from 1
    0024:trace:dxva2:DirectXVideoAccelerationService_Release Destroying
    0024:trace:dxva2:WineVideoService_Release (0xbdb7e8)->() Release from 1
    0024:trace:dxva2:WineVideoService_Release Destroying
    0024:fixme:dxva2:Direct3DDeviceManager9_CloseDeviceHandle (0xbdb7c8)->(0xb4b7a0): stub
    0024:trace:dxva2:Direct3DDeviceManager9_Release (0xbdb7c8)->() Release from 1
    0024:trace:dxva2:Direct3DDeviceManager9_Release Destroying

    Press Enter to exit...00c8:fixme:console:default_ctrl_handler Terminating process 20 on event 0
    0024:trace:dxva2:DllMain 0x7e440000,0,0x1
    mecry@mecry:~/metho/dxva$ 007c:err:rpc:RpcAssoc_BindConnection receive failed with error 1726

    解码器创建

    0174:err:dxva2:vaapi_h264decoder_create VAConfigAttribMaxPictureWidth=4096 …
    0174:err:dxva2:vaapi_create_surfaces vaCreateSurfaces: skipping attrib path, using NULL attribs for 3840x2160
    0174:err:dxva2:vaapi_h264decoder_create surface[0] = 0x2 …
    0174:err:dxva2:vaapi_h264decoder_create vaCreateContext OK: width=3840 height=2160 numSurfaces=26 context=0x1c

    表面创建

  • 调用d3d9的d3d9_device_CreateTexture创建一个 纯 CPU 可读写的 offscreen 纹理 ,不能被 shader 采样,也不能作为渲染目标。DXVA2 解码器用它来存储解码后的 NV12 数据,mpv 再通过 StretchRect 把数据从它拷贝到可渲染的纹理上。
    016c:trace:dxva2:device_manager_processor_service_CreateSurface 018080B4, 1920, 1088, 25, 842094158, 0, 0, 0, 044A6480, 00000000.

    解码过程

  • BeginFrame 调用 VA-API 的 vaBeginPicture。
    0174:trace:dxva2:DirectXVideoDecoderGeneric_BeginFrame (018AC7B8)->(018D5E80, 00000000)
    0174:trace:dxva2:WineVideoDecoderH264_BeginFrame (0x5949f010, 25)
    0174:err:dxva2:WineVideoDecoderH264_BeginFrame vaBeginPicture OK: surfaceIndex=25 surfaceID=0x1b context=0x1d
  • 依次锁定不同类型缓冲区:PictureParams(图像参数)、QMatrix(量化矩阵)、Bitstream(码流数据)、SliceControl(片控制)。
    0174:trace:dxva2:DirectXVideoDecoderGeneric_GetBuffer (018AC7B8)->(0, 06F6FA88, 06F6FA8C)
    0174:err:dxva2:WineVideoDecoderH264_LockBuffer LockBuffer type=0 (PictureParams) size=1040
  • Execute 调用 vaRenderPicture 提交所有解码参数和码流。
    0174:trace:dxva2:DirectXVideoDecoderGeneric_Execute (018AC7B8)->(06F6FAE4)
    0174:err:dxva2:process_picture_parameters vaRenderPicture(VAPictureParameter) -> success
    0174:err:dxva2:process_quantization_matrix vaRenderPicture(VAIQMatrix) -> success
    0174:err:dxva2:process_slice_control_buffer vaRenderPicture(VASliceDecode) -> success
    0174:err:dxva2:process_data_buffer vaRenderPicture(VABitstreamData) -> success
  • EndFrame 调用 VA-API 的 vaEndPicture。
    0174:trace:dxva2:DirectXVideoDecoderGeneric_EndFrame (018AC7B8)->(00000000)
    0174:err:dxva2:WineVideoDecoderH264_EndFrame vaEndPicture OK: surface=25 surfaceID=0x1b
  • 解码完成后,通过 vaDeriveImage + vaMapBuffer 获取解码后的 NV12 帧数据,并写入表面。
    0174:err:dxva2:WineVideoDecoderH264_LockImage LockImage: currentSurface=25 surfaceID=0x1b vaImage.buf=0xffffffff
    0174:err:dxva2:WineVideoDecoderH264_LockImage vaSyncSurface OK …
    0174:err:dxva2:WineVideoDecoderH264_LockImage vaDeriveImage OK …
    0174:err:dxva2:WineVideoDecoderH264_LockImage vaMapBuffer OK …
    0174:err:dxva2:WineVideoDecoderH264_LockImage Dumped frame 0 to /tmp/dxva2_frame0_3840x2160.nv12

    渲染过程

  • 1080p 渲染过程
    mpv vo/gpu-next 渲染

    │ mpv 在自己的 D3D9 设备上创建了 BGRA render target 纹理
    │ (wined3d 支持 1080p BGRA 纹理创建,跟解码器是同一个 D3D9 设备)

    ├─ IDirect3DDevice9_StretchRect(src=NV12_surface, dst=BGRA_texture)
    │ └─ d3d9_device_StretchRect() [device.c L2004]
    │ ├─ 检查 src/dst 都在 D3DPOOL_DEFAULT ✓
    │ ├─ 检查 dst 是 RENDER_TARGET ✓
    │ └─ wined3d_device_context_blt() [L2080]
    │ └─ wined3d_device_context_blt 内部: 同设备
    │ ├─ 发现 src(d3d9_device) == dst(d3d9_device) → 同设备路径
    │ ├─ GPU shader 做 NV12→BGRA 格式转换
    │ └─ 直接 GPU 渲染到目标,无需 CPU 拷贝

    └─ mpv 直接显示 BGRA render target → 正常画面 ✓
  • 4k 渲染过程
    mpv vo/gpu-next 渲染

    │ mpv 创建了 D3D11 设备用于渲染 (dxva-dxgi 互操作)
    │ 解码器仍在 D3D9 设备上

    ├─ mpv 调用 IDXGIResource_GetSharedHandle(D3D11_texture)
    │ └─ dxgi_resource_GetSharedHandle() [resource.c L386]
    │ └─ 返回 wined3d_resource* 指针作为共享句柄

    ├─ mpv 调用 ID3D11Device_OpenSharedResource(handle)
    │ └─ d3d11_device_OpenSharedResource() [device.c L4592]
    │ └─ 从 handle 反推 wined3d_texture,创建 D3D11 纹理包装

    ├─ mpv 调用 IDirect3DDevice9_CreateTexture(shared_handle=handle)
    │ └─ d3d9_device_CreateTexture() 检测 shared_handle 非空
    │ └─ d3d9_texture_create_shared() [texture.c]
    │ └─ 创建 D3D9 纹理包装 D3D11 的 wined3d_texture
    │ ★ 但底层 wined3d_texture 属于 D3D11 设备!

    ├─ IDirect3DDevice9_StretchRect(src=NV12_surface, dst=shared_BGRA_texture)
    │ └─ d3d9_device_StretchRect() [device.c L2004]
    │ └─ wined3d_device_context_blt() [L2080]
    │ └─ wined3d_device_context_blt 内部: 跨设备
    │ ├─ src_texture->device (D3D9) ≠ dst_texture->device (D3D11) ★
    │ └─ 进入跨设备分支 [texture.c L3914]
    │ ├─ format_differs: NV12 ≠ BGRA → TRUE
    │ │
    │ ├─ 在源设备(D3D9)上创建临时 BGRA 纹理 [L3929]
    │ │ └─ wined3d_texture_create(src_device, BGRA, dst_w, dst_h)
    │ │
    │ ├─ 源设备 GPU blit: NV12 → BGRA 格式转换 [L3955]
    │ │ └─ wined3d_device_context_emit_blt_sub_resource(src_device)
    │ │
    │ ├─ CPU map 读取临时 BGRA 纹理 [L3962]
    │ │ └─ wined3d_resource_map(temp_BGRA, MAP_READ)
    │ │
    │ ├─ CPU 上传到目标 D3D11 设备 [L3970]
    │ │ └─ wined3d_device_context_update_sub_resource(dst_device)
    │ │
    │ └─ wined3d_resource_unmap(temp_BGRA) [L3974]

    └─ mpv 显示 D3D11 的 BGRA 纹理 → 正常画面 ✓

dxva2模块划分

wine5.11移植到wine10