用NV_DX_interop扩展让D3D和OpenGL共享资源

DirectX和OpenGL是不同的2个框架,相互之间数据不是互联互通的。如果双方需要做一些数据的交换,通常需要用CPU先把显存里的数据读到系统内存里,再把数据写到另一个框架的显存区域。

 

但是实际上OpenGL有个扩展是可以实现这2个框架之间数据的共享的。

 

在多媒体图像处理里有个小众功能需求,即在Windows上用Windows DXVA做基于显卡的硬件解码以后获得的存放YUV数据的D3D Surface/Texture,不通过CPU的拷贝,直接在GPU的显存里把数据共享给OpenGL, 再由OpenGL做图像的处理。以此来节约CPU和内存带宽资源,提高代码运行效率。

举个例子,基于windows的视频播放器,前端的视频解码部分基于DirectX DXVA的硬件解码,得到解码出来的YUV数据以后再利用现有的OpenGL的算法来做一些视频的后期特效处理,再返回来用DirectX的接口来显示

传统的处理流程应该是这样

现在使用NV_DX_interop扩展之后数据流就可以这样走

数据直接在GPU的显存里像传统C语言的指针一样在不同的图像处理框架间传递,省去了CPU读写显存的过程,大大提高了效率 

 

这个功能在网上能搜到的相关信息不多,只在Intel开发者专区里发现一个靠谱的帖子 Texture Sharing from Intel® Media SDK to OpenGL* 大致的思路是,

  1. 拿到NV12或者YUV的解码图像后先利用IDirect3DDevice9::StretchRect 或者 IDirectXVideoProcessor::VideoProcessBlt 把数据先转化成RGB的D3D 二维纹理 2DTexture (Surface也是一种2DTexture), 这2个函数里是用硬件做转换和拷贝,不走GPU
  2. 利用OpenGL的扩展 NV_DX_interop (DX9)/NV_DX_interop2 (DX11), 让OpenGL能够直接访问D3D的资源,即让OpenGL访问把上一步转换出的RGB 2D Texture数据

 

这样基本上所有的操作都是在GPU里运行,CPU不消耗资源。但是intel开发者专区里附带的代码还是基于2010 June发布的DX9 SDK来做的,利用的是D3D9Ex的API。10年后的现在,在现在Windows 10的环境下,DX9 SDK已经集成进了Windows SDK, D3DEx接口也被去掉了,所以原始代码编译不通过。只能自己参考网上其他的资源,基于Intel MediaSDK 2019R1自带的sample_decode例子重新做了一个,共享出来给大家参考一下。基于MediaSDK的例子做二次开发主要基于以下几个考虑

  • sample_decode例子默认用了硬件解码并且能显示在屏幕上,省去了自己写生成NV12 buffer并且显示到屏幕上代码的时间
  • 例子里在DX9下用了IDirect3DDevice9::StretchRect , 在DX11下用了 IDirectXVideoProcessor::VideoProcessBlt, 这样也节省了自己学习这2个函数怎么使用的时间
  • 这个例子在安装MediaSDK以后即可编译,实在是太方便了 :)

 

WGL_NV_DX_interop的具体的调用流程非常简单,用到的函数一共不超过10个,这里就不讲了。大家可以参考一下Khronos官方的介绍,也没几个字

DX9 用WGL_NV_DX_interop的扩展,https://www.khronos.org/registry/OpenGL/extensions/NV/WGL_NV_DX_interop.txt

DX11 用WGL_NV_DX_interop2的扩展,https://www.khronos.org/registry/OpenGL/extensions/NV/WGL_NV_DX_interop2.txt

 

分享一下代码实现过程中的心得和踩的坑 

  • WGL_NV_DX_interop2和WGL_NV_DX_interop在调用流程上的区别是DX11上去掉了wglDXSetResourceShareHandleNV()函数的调用
  • 根据官方文档, DX11下为了保持和DX9的调用流程兼容,调用wglDXSetResourceShareHandleNV也不会报错,调用也没有实际效果
  • WGL_NV_DX_interop最早是Nvdia写的一个扩展,现在N卡,A卡和Intel显卡驱动已经都支持了。所以理论上在各家的显卡上都可以用,但是从网上的信息看,有极小概率会在某一版的显卡驱动里会出现工作不正常。
  • D3D/OpenGL之间共享的资源可以是Texture, 也可以是Surface,还是比较宽松灵活的
  • VideoProcessBlt是VideoProcessor的成员函数,所以输入的surface的格式只支持主流解码器输出的那几种像素格式,具体的可以参考vlc的源码, vlc-master\modules\video_chroma\dxgi_fmt.c里面dxgi_formats[]和d3d_formats[]的定义
  • StretchRect不一定能同时支持颜色转换和缩放,至少我目前的代码目标surface是off-screen surface的情况下不可以,这个微软官网说非常清楚。有关能否缩放的限制可以参考官网https://docs.microsoft.com/en-us/windows/win32/api/d3d9/nf-d3d9-idirect3ddevice9-stretchrect
  • OpenGL的render context是基于线程绑定的,所以在一个线程里初始化的OGL context和glGenTextures, 并且makecurrent以后,在另一个线程里用这个context和texture句柄是无效的。目前的代码里是把初始化和渲染放在一个线程里来回避这个问题,网上看还有其他的方法来解决这个多线程共享问题。

 

最后源码奉上,代码用到Intel显卡硬件解码,需要在有Intel集显的机器上运行

https://gitee.com/tisandman/IntelMediaSDK_Sampledecode

 

 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值