Direct2D / GDI+与Windows表单绘图缓慢 - 有什么解决方法?

5
我经常使用Visual Studio 2008、.NET C# 2.0-3.5和Windows Forms进行工作,我注意到,就像许多人一样,GDI+在绘制控件时非常缓慢。请注意,我不太处理图像(JPG、GIF等)。图像仅作为某些地方的图标。实际上是控件/窗体等绘制速度慢。
问题在于您可以看到控件正在被绘制,并且可能需要几秒钟才能绘制一组看似简单的控件。也就是说,它很卡顿,非常糟糕。
我进行了测试,只是将一些标签(40-50)放在一个窗体上,按F5运行并等待它们被绘制。同样,有延迟,不是很好的体验。
因此,有WPF可以解决这个问题,但我们还没有准备好转向WPF。所以我在寻找解决方法或修复方法时,偶然发现了Direct2D,以及其他一些库。
但我有些困惑,因此有以下问题:
1) 首先,我想要的是一种相当简洁而简单的方法,只需用更快和硬件加速的方法替换GDI+。是否可以在不转向WPF且无需重写所有Windows Forms代码的情况下做到这一点?
每当我在Direct2D上阅读任何内容时,我都会看到通常是可怕的C++代码块,告诉我如何手动编写绘图代码。我不想要那个。
2) 在网上阅读时,我偶然发现了SlimDX,但我无法弄清楚如何使用它(我承认,我还没有尝试过)。假设我已经有一个GUI应用程序(Windows Forms,标准C#代码)-我是否可以以某种方式使用SlimDX(或类似的东西)来“替换”GDI+而无需太多重写?
我的问题是,我找不到任何示例或类似的东西,告诉我是否可以在我已经创建的Windows Forms软件中使用SlimDX、Direct2D或其他类似的东西,如果可能的话-如何做到这一点。
希望我没有太模糊 =)
==编辑== 2010-09-22
我在我的真实应用程序中进行了一些测试,并将其中一个缓慢的事情隔离出来:
当我向UserControl中的某些标签添加文本时,控件会调整自身大小以适应文本。例如,包含的GroupControl会根据刚刚添加到标签的.Text属性的文本大小进行适应。有大约10个标签控件。第一次更新标签时,因此大小发生更改,整个过程需要大约500毫秒。第二次更新标签时,没有大小更改,需要大约0毫秒。

==编辑2== 2010-09-22

发现其中一个减速的原因。当将一个字符串添加到文本属性中时,如果被添加的文本与更新前的文本长度不同,则添加操作会很慢。

我正在使用DevExpress库,并且LabelControl可以设置为AutoSizeMode。如果我将其设置为“None”,那么在添加与先前文本长度不同的文本时,延迟就会消失。我猜普通的Label控件也有一个AutoSize = true/false设置,所以这个问题可能是一样的。

然而,这只是一个“变通方法”,但仍然证明了我的观点 - 调整大小时非常缓慢,这相当糟糕。


Ted,请问你能否发布一些简单的Winforms示例项目,以演示你的性能问题吗? - user180326
不太清楚如何在这里发布文件。但是我尝试的方法是创建一个表单,在该表单上放置1个带有3个页面的TabControl。每个页面我都用Label、TextBox和ComboBox填充(每个tabPage有200个控件)。然后我只需要运行它,点击/切换页面,就可以看到它的延迟情况。在正常的应用程序中也是一样的,通常更糟 =( - Ted
我正在使用DevExpress库。首先要尝试的是使用标准内置的Windows控件,看看它们是否更快。在我看来,你使用第三方控件库的事实应该出现在你的第一段中!(我现在已经添加了适当的DevExpress标签,希望能更明显地表明这一点。) - ToolmakerSteve
5个回答

3
许多上面的海报提出了好的观点。我自己使用GDI+创建了一个3D CAD应用程序,如果实现正确,则发现它足够快。但是,立即使用控件似乎是一种非常笨拙的方法。控件是一个相当大的对象,在这种情况下有许多理由要制作自己的控件。
我建议您研究保留模式绘图系统。它很容易实现,并且在大多数情况下都可以满足您的需求。您必须自己创建绘图逻辑,但那只是有趣的,并且会给您更多的灵活性 :)

1
似乎那个人在2014年就离开了技术界:http://bobpowelldotnet.blogspot.co.uk/ - Drew Noakes
很遗憾,Bob Powell在几年前去世了。他的网站是关于GDI+编程最好的资源之一,不幸的是我认为没有完整的归档副本存在。 - Pedery
这在多个层面上都是令人难过的。 - Drew Noakes
2
幸运的是,互联网档案馆在被无情的互联网吸血鬼接管之前备份了他的一些工作:https://web.archive.org/web/20120826121342/http://www.bobpowell.net/manipulate_graphics.htm - Drew Noakes

1
关于你的第一个问题,我不得不使用GDI来进行一些图像处理工作,而在GDI+下这需要很长时间。这是4-5年前的事情了,使用托管C#来使用GDI非常麻烦 - 不确定现在有多少改变。有许多好的和快速的函数,如BitBlt,在绘制时非常快,但您需要非常小心地释放资源(句柄)和内存。我还遇到了另一个问题,那就是将结果保存为JPEG格式,在GDI中不存在,因此我必须使用CxImage来读取HBitmap,然后保存它。
总的来说,GDI非常快速和强大。如果DirectX中有更好的抽象,那么您最好使用它们。

谢谢。其实我不太涉及图片方面的工作。对于绘制普通Windows窗体控件、弹出窗体等操作,速度确实会变慢。GDI是快的吗?但我看到很多地方都说它并不快? - Ted

1
我正在研究一些相同的问题。我正在编写一个应用程序,需要能够非常高效地呈现2D图形,因为有些用户可能同时打开10-50个窗口。还有一件事需要考虑,就是没有人在这里谈论的一点,那就是Direct2D只能在安装了Vista服务包2及以上版本的计算机上使用。此外,根据这个链接:

http://www.tomshardware.com/news/msft-windows-xp-windows-7-market-share-win7,13876.html

截至2011年11月,所有Windows用户中有38.5%仍在使用XP。因此,如果向仍在使用XP的大量用户销售应用程序是一个问题(或者您的市场基础是主要使用XP的第三世界国家),那么您应该选择以下之一:
  1. 对于较新的操作系统,请使用Direct2d;对于XP系统,请使用GDI+。

  2. XNA-与XP兼容,也可以与较新的操作系统一起使用。请参见此链接:http://msdn.microsoft.com/en-us/library/bb203925.aspx

  3. SlimDX-在第一个答案中提到。支持XP以及较新的操作系统。请参见: http://slimdx.org/http://slimdx.org/features.php

  4. 如果您关心Windows,Linux,Max等之间的兼容性,请使用OpenTK。

你还应该知道,GDI+ 在最初发布时存在一个 Bug,导致其性能非常糟糕。请参见以下链接,了解为什么一些开发人员声称 Microsoft 破坏了使用 GDI+ 的 Windows7 应用程序的 GUI:

http://www.windows7taskforce.com/view/3607

或者使用此字符串在您喜欢的搜索引擎上进行网络搜索:"gdi+ bug slow on windows 7"。


0

我们在C#应用程序中使用SlimDX....但实际上我们正在进行3D。我们编写了自己的2D库以便能够进行简单的2D绘图。SlimDX只是DirectX的轻量级包装器。因此,您将获得DirectX的所有优点和缺点。例如,如果不存在显卡,则需要模拟它。

如果您想要为离屏位图绘制东西,我建议使用WPF,因为它与C#很好地集成,在大多数地方都可以使用,并且在有硬件可用时加速。您可以将输出复制到位图中,并在常规GDI / Winforms中使用该位图。但是,仅当您执行相当复杂的操作(许多过滤器,混合纹理等)时,它才比GDI +更快。

编辑:

针对评论,我构建了一个小示例表单。第一次切换时会有几秒钟的等待时间,但之后它就会响应。比我预期的慢,但完全可用。希望Ted评论一下他在应用程序中看到的性能。

public class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        // *** EDIT ***
        this.tabPage1.SuspendLayout();
        this.tabPage2.SuspendLayout();
        this.tabControl1.SuspendLayout();
        this.SuspendLayout();

        FillTab(tabPage1, Color.White);
        FillTab(tabPage2, Color.Yellow);

        // *** EDIT ***
        this.tabPage1.ResumeLayout(false);
        this.tabPage2.ResumeLayout(false);
        this.tabControl1.ResumeLayout(false);
        this.ResumeLayout(false);
    }

    private static void FillTab(TabPage tabPage, Color color)
    {
        for (int i = 0; i < 200; ++ i)
        {
            int left = (i % 5) * 320;
            int topOffset = (i / 5) * 23;
            tabPage.Controls.Add(new Label { Left = left, Top = topOffset, Width = 100, Text = "Label" });
            tabPage.Controls.Add(new TextBox() { BackColor = color, Left = left + 100, Top = topOffset, Width = 100, Text = tabPage.Text });
            tabPage.Controls.Add(new ComboBox { BackColor = color, Left = left + 200, Top = topOffset, Width = 100 });
        }

    }

    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.tabControl1 = new System.Windows.Forms.TabControl();
        this.tabPage1 = new System.Windows.Forms.TabPage();
        this.tabPage2 = new System.Windows.Forms.TabPage();
        this.tabControl1.SuspendLayout();
        this.SuspendLayout();
        // 
        // tabControl1
        // 
        this.tabControl1.Controls.Add(this.tabPage1);
        this.tabControl1.Controls.Add(this.tabPage2);
        this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
        this.tabControl1.Location = new System.Drawing.Point(0, 0);
        this.tabControl1.Name = "tabControl1";
        this.tabControl1.SelectedIndex = 0;
        this.tabControl1.Size = new System.Drawing.Size(292, 266);
        this.tabControl1.TabIndex = 0;
        // 
        // tabPage1
        // 
        this.tabPage1.Location = new System.Drawing.Point(4, 22);
        this.tabPage1.Name = "tabPage1";
        this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
        this.tabPage1.Size = new System.Drawing.Size(284, 240);
        this.tabPage1.TabIndex = 0;
        this.tabPage1.Text = "tabPage1";
        this.tabPage1.UseVisualStyleBackColor = true;
        // 
        // tabPage2
        // 
        this.tabPage2.Location = new System.Drawing.Point(4, 22);
        this.tabPage2.Name = "tabPage2";
        this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
        this.tabPage2.Size = new System.Drawing.Size(245, 217);
        this.tabPage2.TabIndex = 1;
        this.tabPage2.Text = "tabPage2";
        this.tabPage2.UseVisualStyleBackColor = true;
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(292, 266);
        this.Controls.Add(this.tabControl1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.tabControl1.ResumeLayout(false);
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.TabControl tabControl1;
    private System.Windows.Forms.TabPage tabPage1;
    private System.Windows.Forms.TabPage tabPage2;
}

我不认为我跟上了。屏幕外位图?呃,我只希望我的UserControls/forms上的控件可以快速绘制,而且没有延迟。你是说GDI+的速度已经达到极限了吗?因为它真的很慢。我刚刚尝试在空表单中添加大约160个TextBoxes,当重新绘制该区域时,你可以看到延迟... - Ted
@Ted:在这种情况下,WPF可能不太合适。你能否发布一些代码,展示你如何使用这些控件?160个空文本框,即160个矩形,在任何技术中都应该很快。 - user180326
@Alex 这不是重点。这只是一个示例,以展示它有多慢。那只是一个测试项目。在我的正常应用程序中,我们使用许多不同的控件,而它非常慢,以至于让我想哭。 - Ted
我用“编辑2”更新了一下,解释了我发现的问题。问题出在框架上,当“调整大小”(或者其他操作)时非常慢。它本应该不是这个样子的。 - Ted
@Ted:是的,我确实看到了一些延迟(我想我已经确认过了)。它大约是你提到的200毫秒左右。但这只是一个比你所需求的控件多三倍的例子。为什么你不修改示例,使其与你实际需要的复杂度相当呢?当我将我的示例中的控件数量减少到200时,就没有明显的延迟了。所以我不明白问题出在哪里。 - user180326
显示剩余13条评论

0
你可以尝试使用托管 DirectX,但它已经不再得到支持(转向 XNA)。老实说,除非你的电脑很差或者有大量控件,否则我不知道为什么会出现如此严重的延迟。如果你在主线程上进行了一些 CPU 密集型操作,请将其移动到单独的线程中。这是我能想到的唯一可能导致这种延迟的原因。

我有一台Intel 920 2.6 Ghz的电脑,配备了6 GB的内存,运行在Win7 64位系统上,可用内存非常充足,但CPU却很闲。不,问题不在电脑上 =) 我进行了一个测试项目,只是在TabControl中添加了大量的标签、文本框等控件,结果出现了问题。当切换选项卡时(而且选项卡页包含数百个标签、文本框或其他控件),明显会出现卡顿现象... - Ted

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接