我正在使用 D3DImage 显示使用 Direct3D 渲染的图像。 Direct3D 渲染需要在它自己的线程中进行,而 GUI 线程在需要时获取表面并使用 D3DImage 将其放在屏幕上。
起初我尝试使用单个 D3D 渲染目标来执行此操作,但是即使锁定到位,我也有严重的撕裂,即渲染线程正在覆盖表面,因为 WPF 正在其前缓冲区中复制它。似乎 WPF 很难预测何时复制数据(即它不在 D3DImage.Unlock() 上,甚至不在下一个 D3DImage.Lock() 上,正如文档所建议的那样)。
现在我正在做的是我有两个渲染目标,每次 WPF 显示一个帧时,它都会要求渲染线程交换它的目标。所以我总是渲染到目标 WPF 未使用。
这意味着在窗口的每次图形更新时,我都会做类似的事情
m_d3dImage.Lock();
m_d3dImage.SetBackBuffer(D3DResourceType.IDirect3DSurface9, m_d3dRenderer.OutputSurface);
m_d3dImage.Unlock();
m_d3dRenderer.SwapSurfaces();
其中 OutputSurface 是一个指向我们当前未渲染到的 D3D 渲染目标的 IntPtr,而 SwapSurfaces 只是交换两个表面指针并调用 IDirect3DDevice9::SetRenderTarget 与我们将用于下一次渲染的目标。
编辑:根据要求,这里是 SwapSurfaces() 的代码:
var temp = m_renderingSurface;
m_renderingSurface = m_outputSurface;
m_outputSurface = temp;
m_d3dDevice.SetRenderTarget(0, m_renderingSurface);
其中 m_renderingSurface
和 m_outputSurface
是两个渲染目标(SharpDX.Direct3D9.Surface
),m_d3dDevice
是 DeviceEx
对象。
这工作得很好,即没有撕裂,但是几秒钟后我得到一个 OutOfMemoryException,并且 Direct3D 有以下调试输出:
Direct3D9: (ERROR) :Invalid iBackBuffer parameter passed to GetBackBuffer
Direct3D9: (ERROR) :Error during initialization of texture. CreateTexture failed.
Direct3D9: (ERROR) :Failure trying to create a texture
Direct3D9: (ERROR) :Error during initialization of texture. CreateTexture failed.
Direct3D9: (ERROR) :Failure trying to create a texture
MIL FAILURE: Unexpected HRESULT 0x8876017c in caller: CInteropDeviceBitmap::Present D3D failure
Direct3D9: (WARN) :Alloc of size 1577660 FAILED!
Direct3D9: (ERROR) :Out of memory allocating memory for surfaces.
Direct3D9: (ERROR) :Failure trying to create offscreen plain surface
我在这里找到了一个相关主题,其中建议的解决方案是调用 D3DImage.SetBackBuffer() 并传递 IntPtr.Zero,但是我在现有调用之前添加了这个,但它没有解决问题。我还尝试在 SetBackBuffer(... IntPtr.Zero) 周围调用 Lock() 和 Unlock(),但这也没有解决问题。
此时我想知道 D3DImage 中是否存在错误,或者我是否应该完全使用不同的方法。我可以用 D3D 交换链替换我的 2 个渲染目标吗,这样我就不必一直用不同的指针调用 SetBackBuffer 了吗?我是 Direct3D 的新手。
编辑:我使用 .NET Reflector 查看了 D3DImage.SetBackBuffer() 的代码,它每次都在创建一个 InteropBitmap。它不会为 IntPtr.Zero 做任何特别的事情。由于我每秒调用多次,也许资源没有时间被释放。在这一点上,我正在考虑使用 2 个不同的 D3DImages 并交替它们的可见性以避免必须一直调用它们的 SetBackBuffer()。
谢谢。
最佳答案
每次您将其后台缓冲区指针设置为不同的内容时,D3DImage 似乎都会创建一个新的 Direct3D 纹理。这最终被清理了,但是像我所做的那样将它设置为每秒 30 次并没有留下足够的时间,而且无论如何都是一个很大的性能 killer 。我采用的方法是创建多个 D3DImage,每个都有自己的表面,将它们全部放在彼此之上并切换它们的 Visibility 属性,以便一次只显示一个。这似乎工作得很好并且不会泄漏任何内存。
关于c# - 调用 SetBackBuffer 时出现 D3DImage OutOfMemoryException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14612259/