最近碰到这样的现象,在一些笔记本上使用D3DImage控件渲染时,会有一定几率无法显示图形。图形显示流程大概是C++ 创建Surface,渲染好后传递指针给D3DImage显示。查了一遍C++ 层,返回值都是对的,连个错误码都没有,而且C++写的渲染Demo在同样的环境下可以正常运行。
然后就怀疑C# 层渲染出了问题,果不其然D3DImage的一个属性IsFrontBufferAvailable为false了,MSDN说该属性偶尔可能为 false,为false的时候D3DImage是无法渲染图形的,说明wpf丢失了D3D设备。google了一下,网上说这种情况是D3DImage 失去D3DDevice的控制权,什么原因引起的,还不清楚,直到 MSDN 上一篇文章引起我的注意:https://social.msdn.microsoft.com/Forums/vstudio/en-US/2779ab41-6f84-47cf-a1d6-7851cc168096/d3dimage-losing-its-d3ddevice?forum=wpf
大意是当使用远程桌面时,wpf识别出有用户使用远程桌面,或者有程序调用类似mirror display 驱动时,wpf出于性能和稳定考虑,可能下调渲染级别,严重的由Tier2降到Tier0,用以下代码查了下,渲染等级还真是降为0了(0表示硬件加速不可用)。
int level = System.Windows.Media.RenderCapability.Tier >> 16;
什么原因引起渲染等级下调呢?发现向日葵远程控制有一个虚拟显示器驱动,看过抓屏相关的代码,第一直觉就是它搞得鬼。
将改驱动禁用后重启,发现D3DImage 能正常显示图形,渲染级别也变为2了。这个驱动会使wpf渲染等级降为tier0,但不是对所有PC都有影响,台式机几乎不受影响,个别性能差的笔记本直接缴械投降,aero特效也没了。Tier 0是无法利用硬件加速的,好在D3DImage 提供CPU软渲染的方法。
public void SetBackBuffer(D3DResourceType backBufferType, IntPtr backBuffer, bool enableSoftwareFallback);
所以最后的解决办法就是,在渲染前先查询目前wpf所在的渲染等级,决定使用软渲染还是硬件加速。
private void CheckHardWareSupport()//查询硬件支持
{
int level = System.Windows.Media.RenderCapability.Tier >> 16;
useSoftRender = level == 0?true:false;
}
var showRect = new Int32Rect(0, 0, d3dImage.PixelWidth, d3dImage.PixelHeight);
private void Render(IntPtr surface)
{
if(!useSoftRender)//硬件加速,IsFrontBufferAvailable为false返回
{
if(!d3dImage.IsFrontBufferAvailable)
{
return;
}
}
d3dImage.Lock();
d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, surface, useSoftRender);
d3dImage.AddDirtyRect(showRect);
d3dImage.Unlock();
}
注:软渲染会耗费较多CPU,而且软渲染检查IsFrontBufferAvailable是没有意义的
参考链接:
https://stackoverflow.com/questions/61122452/d3dimage-and-remote-desktop