c# - Wpf 的 InteropBitmap 连同 GDI+ : high cpu usage

标签 c# wpf interop gdi+

我创建了一个小的 wpf 测试应用程序,它使用 System.Drawing.Graphics 和 wpf 的 InteropBitmap 每 30 毫秒渲染一些随机矩形。我认为 InteropBitmap 会比 WriteableBitmap 更快:它具有从内存部分更新自身的能力。

在执行应用程序(屏幕尺寸 1600 * 1200)时,应用程序的 CPU 使用率只有大约 2-10%,双核 3GHz。但是整体cpu使用率大约是80-90%,因为进程“System (NT Kernel & System)”上升到70%! 编辑:我注意到 RAM 使用量在 15 秒内周期性地增加超过 1 GB,然后突然回落到正常水平等等。

也许可以优化以下代码? :

namespace InteropBitmapTest{

 using System;
 using System.Drawing;
 using System.Runtime.InteropServices;
 using System.Windows;
 using System.Windows.Interop;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using Color = System.Drawing.Color;

public partial class Window1 : Window
{

    private System.Drawing.Bitmap gdiBitmap;
    private Graphics graphics;


    InteropBitmap interopBitmap;

    const uint FILE_MAP_ALL_ACCESS = 0xF001F;
    const uint PAGE_READWRITE = 0x04;

    private int bpp = PixelFormats.Bgr32.BitsPerPixel / 8;

    private Random random;
    private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();


    SolidBrush[] brushes = new SolidBrush[] { new SolidBrush(Color.Lime), new SolidBrush(Color.White) };


    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr CreateFileMapping(IntPtr hFile,
    IntPtr lpFileMappingAttributes,
    uint flProtect,
    uint dwMaximumSizeHigh,
    uint dwMaximumSizeLow,
    string lpName);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject,
    uint dwDesiredAccess,
    uint dwFileOffsetHigh,
    uint dwFileOffsetLow,
    uint dwNumberOfBytesToMap);

    public Window1()
    {
        InitializeComponent();

        Loaded += Window1_Loaded;

        WindowState = WindowState.Maximized;

        timer.Tick += timer_Tick;
        timer.Interval = 30;

        random = new Random();

    }

    void Window1_Loaded(object sender, RoutedEventArgs e)
    {
        // create interopbitmap, gdi bitmap, get Graphics object
        CreateBitmaps();


        // start drawing 100 gdi+ rectangles every 30 msec:
        timer.Start();
    }


    void timer_Tick(object sender, EventArgs e)
    {
        int width = 50;


        // Draw 100 gdi+ rectangles :


        for (int i = 0; i < 100; i++)
        {
            int left = random.Next((int)(ActualWidth - width));
            int top = random.Next((int)(ActualHeight - width));


            graphics.FillRectangle(brushes[left % 2], left, top, width, width);

        }


        interopBitmap.Invalidate(); // should only update video memory (and not copy the whole bitmap to video memory before)

    }


    void CreateBitmaps()
    {

        uint byteCount = (uint) (ActualWidth * ActualHeight * bpp);


        //Allocate/reserve memory to write to

        var sectionPointer = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, PAGE_READWRITE, 0, byteCount, null);

        var mapPointer = MapViewOfFile(sectionPointer, FILE_MAP_ALL_ACCESS, 0, 0, byteCount);

        var format = PixelFormats.Bgr32;

        //create the InteropBitmap

        interopBitmap = Imaging.CreateBitmapSourceFromMemorySection(sectionPointer, (int)ActualWidth, (int)ActualHeight, format,
            (int)(ActualWidth * format.BitsPerPixel / 8), 0) as InteropBitmap;


        //create the GDI Bitmap

        gdiBitmap = new System.Drawing.Bitmap((int)ActualWidth, (int)ActualHeight,
                                    (int)ActualWidth * bpp,
                                     System.Drawing.Imaging.PixelFormat.Format32bppPArgb,
                                    mapPointer);

        // Get good old GDI Graphics

        graphics = Graphics.FromImage(gdiBitmap);


        // set the interopbitmap as Source to the wpf image defined in XAML 

        wpfImage.Source = (BitmapSource) interopBitmap; 

    }





}}

XAML:

<Window x:Class="InteropBitmapTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
  <Image Name="wpfImage" Stretch="None" />
</Window>

最佳答案

WTF:这是 InteropBitmap 内存泄漏.NET 4.0 中的新功能:

http://connect.microsoft.com/VisualStudio/feedback/details/603004/massive-gpu-memory-leak-with-interopbitmap

https://connect.microsoft.com/VisualStudio/feedback/details/585875/interopbitmap-is-way-less-performant-in-net-4-0-vs-net-3-5

将目标框架设置为 3.5,一切正常!

另一种选择是将“GC.Collect();”在每个“interopBitmap.Invalidate();”之后打电话。

关于c# - Wpf 的 InteropBitmap 连同 GDI+ : high cpu usage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4473515/

相关文章:

c# - 如何使用 IPropertyNotifySink 触发从 C# 到 COM 的属性更改通知?

python - 带 WPF 的 IronPython 中的消息框

c# - 为什么++i 和 i++ 都会生成临时文件

c# - 使用 REST API 从 Salesforce.com 调用 "Get Updated"记录时出现问题

c# - 指定 DataContext (WPF) 的正确方法

c# - 我的数据绑定(bind)怎么会写出 Length 属性?

WPF 3d——理解纹理坐标

C# Excel 互操作 : Excel process remains in memory until parent form closed

java - 如何使用 .Net 以外的技术连接并交换 2010 服务器?

c# - 一个 Where() 子句中的多个 Any() LINQ