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
14vainfo
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
280024: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 纹理 → 正常画面 ✓