开发者

Direct2D / GDI+ and slow Windows forms drawing - What can be done?

I'm working a lot with Visual Studio 2008, .NET C# 2.0-3.5 and Windows Forms and I have noticed, like many before me, that GDI+ is extremely slow in drawing Controls. Note that I do not deal with images (JPG, GIF etc) very much. Images are only as icons in certain places. This is actually Controls/Forms/etc that are slow to draw.

The issue is that you can see Controls being drawn and it can take several seconds for a seemingly easy set of Controls to be drawn. Ie, its lagging and horrible.

I have made tests where I just put a number of Labels (40-50) on a form, hitting F5 to run and have to wait for them to be drawn. Again, lag and not a very nice experience.

So, then there is WPF that might address this problem, but I/we are not ready to move to WPF. So I'm looking around for workarounds or fixes and I stumbled upon Direct2D, and when reading on that some other libraries.

Put Im a tad puzzled and thus these questions:

1) First of, what I want is a fairly neat and simple way to just replace GDI+ with something faster and hardware accelerated approach. Is it possible to do that without going over to WPF and without having to rewrite all my Windows Forms code?

Whenever I read anything on Direct2D I see long blocks of usually horrible C++ code, telling me on how to manually write code to for drawing. I do not want that.

2) While reading on the net, I stumbled upon SlimDX, but I cannot figure out how to use it (and I admit, I havent tried very much as of writing). Lets say I already have a GUI-application (Windows Forms, standard C# code) - can I somehow use SlimDX (or something like it) to just "replace" GDI+ without too much rewriting?

My problem is I cannot find any examples or such telling me if it is possible to use SlimDX, Direct2D or other similiar things in my already-created Windows Forms software, and if it is possible - how to do it.

Hope Im not too fuzzy =)

==EDIT== 2010-09-22

I have made some tests in my real app, and isolated one of the slow things to this:

When I add text to some Labels in a UserControl, the Controls resize themselves to fit the text. For example, the containing GroupControl adapts a bit to the size of the text that was just added to the .Text-property of the Labels.

There are about 10 Label cont开发者_运维技巧rols. The first time the labels are updated, and thus sizes are changed, the whole process takes about 500 ms. The second time the labels are updated, and no size changes, it takes about 0 ms.

==EDIT 2== 2010-09-22

Found one of the slow-downs. Then adding a String to the Text-property it is slow if the text that is being added differs in string length from the text that was there before the update.

Im using DevExpress libraries, and the LabelControls can be set to AutoSizeMode. If I set that to "None" then the lag will go away when adding text that differs in length from the previous text. I guess this problem will be the same for the normal Label-control as it also has a AutoSize = true/false setting.

However, its a "workaround" but still proves my point - its really slow when resizing which is pretty lame.


Many of the posters above come with good points. I've created a 3D CAD application in GDI+ myself, and found it plenty fast enough if it's implemented correctly. Using Controls, however, immediately strikes me as a very awkward way to do things. A Control is a fairly big object and there are numerous reasons to make your own in this case.

I'd advise you to look into a retained mode drawing system. It's easy to implement and would cover your situation in most cases. You'd have to create the drawing logic yourself, but that's just fun and would give you more flexibility :)


On your first question, I had to use GDI to do some image processing stuff which was taking ages under GDI+. This was 4-5 years ago and working with GDI using managed C# was a pain - not sure how much it has changed now. There are many good and fast functions such as BitBlt which are very fast in drawing but you need to be very careful with releasing resources (handles) and memory. I also had another issue and that was saving the result as JPEG and it is non-existent in GDI so I had to use CxImage to read the HBitmap and then save it.

All in all, GDI is very fast and robust. If there better abstractions in DirectX, probably you are better off using them.


I'm looking at some of the same issues. I am writing an application that needs to be able to render 2d graphics very efficiently since some users could have 10 - 50 windows open simultaneously. One thing to consider that no one else talked about here is the fact that direct2d can only be used on computers with Vista with service pack 2 and up. Also, according to this link:

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

38.5% of all Windows users were still using XP as of Nov, 2011. So, if selling the app to a significant amount of users still running XP is a concern (or your marketbase is to 3rd world countries that are mainly using XP), then you should either go with:

  1. Direct2d for newer operating systems and GDI+ for XP systems.

  2. XNA - which is compatible with XP and can also be used with newer operating systems. See this link: http://msdn.microsoft.com/en-us/library/bb203925.aspx

  3. SlimDX - mentioned in the first answer. Supports XP as well as newer operating systems. See: http://slimdx.org/ and http://slimdx.org/features.php

  4. OpenTK if you care about compatibility between Windows, Linux, Max, etc.

You should also be aware that there was a bug with GDI+ that caused it to suffer very poor performance when it was initially released. See the following link why some developers claim that Microsoft broke the gui for Windows7 for apps using GDI+:

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

or do a web search from your favorite search engine with this string: "gdi+ bug slow on windows 7".


You could try managed directx, but they no longer support it (moved on to XNA). Honestly, unless you've got a shitty computer or a ton of controls, I don't know why it'd be lagging so bad. If you're doing some cpu intensive stuff on your main thread, move it to a separate thread. That's the only other reason I can think of that'd cause that kind of lag.


We use SlimDX in our C# app.... But we're actually doing 3D. We wrote our own 2D lib to be able to do simple 2D drawing. SlimDX is just a lightweight wrapper around DirectX. So you'll get all of the pro's and cons of DirectX. Like that it's your problem to emulate the videocard if its not present.

If you want something for drawing to offscreen bitmaps, I'd go for WPF, as it is well integrated with C#, works mostly everywhere, and accellerated when there's hardware available. You can copy the output to a bitmap and use that in regular GDI/Winforms. But it will only be faster than GDI+ if you do fairly complex stuff (lots of filters, mixing textures etc...).

Edit:

In response to the comments, I built a little sample form. There is a seconds long wait when switching the first time, but after that it's responsive. Slower than I'd expect, but by all means usable. Would like Ted to comment if this is about the performance he is seeing in his app.

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;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜