使用SharpDX位图绘制增加内存

时间:2017-05-10 22:01:42

标签: memory-leaks bitmap directx sharpdx hardware-acceleration

我是DirectX和Direct2D的新手。我正在尝试使用SharpDx卸载硬件上的工作而不是消耗CPU来进行持续渲染。

我有一个媒体基础管道。我实现了一个自定义渲染器。我的应用程序是在C#和WPF中。

我从管道中获取原始像素并将它们推送到正在更新Image控件的WritableBitamps。显然,图像控制存在一个已知的问题,它存在于画笔上。我的应用程序可以抛出内存异常但这个问题。如果原始框架不大,则问题不明显。让我们说800x600视频。

如果播放1920 x 1080视频,我会遇到更多问题。

我尝试创建一个BitmapSource并更新后台缓冲区(以防止不断创建bitamp),但这并没有帮助。在某些情况下,记忆会变得像疯了一样。截至目前,我正在为每个绘图创建新的bitamp。我知道这是测试的临时解决方案。

所以现在我正在尝试的是SharpDx。我正在使用一个名为D2DControl的包装器,它包装了一个D3DImage和设置代码,并允许创建一个WPF控件,你可以插入你的视图(类似于D3DImage.D2DControl来自D3DImage)我知道有些人讨厌外部链接,但这里是如果有人有兴趣,请链接。

https://github.com/dalance/D2dControl/tree/master/D2dControl

这个包装器的作用是它隐藏了很多细节。它公开了一个允许绘图发生的Render()方法。

这就是我所拥有的。

public class MediaDisplayControl : D2dControl.D2dControl
{
    private Bitmap backBufferBmp;        
    public FrameRenderer Renderer { get; set; }

    public override void Render(RenderTarget target)
    {
        var sampleData = Renderer.Samples.FirstOrDefault();

        if (sampleData != null)
        {
            if (backBufferBmp == null)
                backBufferBmp = new Bitmap(target, new SharpDX.Size2(sampleData.Width, sampleData.Height), new BitmapProperties(target.PixelFormat));

            backBufferBmp.CopyFromMemory(sampleData.Pixels, sampleData.Stride);

            target.Transform = new RawMatrix3x2 { M11 = target.Size.Width / backBufferBmp.Size.Width, M22 = target.Size.Height / backBufferBmp.Size.Height };
            target.DrawBitmap(backBufferBmp, 1f, BitmapInterpolationMode.Linear);
        }
    }
}

我尝试了在每个渲染上创建新bitamp或重用它的各种变体。在一个1920x1080的视频帧上,内存一次跳300毫克,直到它达到1.2GB并停止。

我正在缩放位图,因为D3DImage没有缩放比特。使用Image控件,您可以编写一个大的bitamp,它将缩放到视图大小。使用此代码,如果图像大于容器,则会裁剪图像。

无论如何,转型并没有改变结果。 CopyFromMemory和DrawBitamp的行为增加了内存。

所以我的问题是: 1)我所做的事情是否有意义? 2)我可以采取其他措施来防止记忆问题吗?

感谢您花时间阅读本文。

2 个答案:

答案 0 :(得分:1)

我看到您尝试通过将Unmanaged memory复制到Managed memory并再次复制到Unmanaged memory来解决您的任务。首先,你得到渲染的原始像素 - 这不是一个好主意,因为代码不能使用DirectX Video Acceleration来加速解码 - 它会解码到CPU内存中。其次,在您的代码中有Bitmap backBufferBmp - 它是一个托管内存对象,您无法快速从内存中清除它,但您可以尝试GC.Collect Method ()。第三,在渲染过程中,来自系统存储器中backBufferBmp的像素复制到WPF的视频存储器中。因此,在您的解决方案中,有三个瓶颈。我可以推荐尝试System.Windows.Interop.D3DImage - 它允许将Direct3DSurface9附加到渲染过程WPF而不从内存中复制,然后您可以使用Direct3DSurface9作为渲染目标来使用{{1} }}。但是,它需要为Media Foundation管道编写新的自定义渲染器。

因此,没有用于解决问题的DirectX Video Acceleration代码行 - 您需要从头开始重写代码。

问候。

P.S。您可以通过链接 - WPFViewerEVRDisplay

找到使用magic的示例

答案 1 :(得分:0)

更新

非常感谢@Evgeny Pereguda的详细解答。它帮助我实现了一些我忽视的想法。我采用了在Cli / C ++中包装Direct3D9接口的途径。

在他的示例项目中,他使用ComImport将Direct3D9拉入C#端。这需要一些关于dll本身的知识。

在C#端的每个COM文档中,您无法部分拉入接口。它是全部或仅仅是Guida和名称。在Direct3D9的情况下,有许多函数并试图定义所有这些函数(因为我很可能在整个类中使用1个方法)是一种矫枉过正。

他的方法使用反射来引入基于dll方法地址的方法,这需要对方法地址进行硬编码。我选择了使用头文件和D3D9.lib来获取Direct3D9类的包装器,因为我对于硬编码事情有点挑剔。

我创建了一个完整的用户控件,可以插入我们的应用程序。

感谢有关内存问题的指导。最好的途径是使用非托管内存,我没有意识到我使用托管。切换有助于提高大量管理的性能和内存。

SharpDx很好,但很多细节都是隐藏的,我希望有更多的控制权。

截至目前,我将非托管像素写入管道并且内存正常运行。