I'm currently working on the user interface of a win forms application. The primary window is a borderless form whose surface area is almost entirely rendered in the Form.Paint event. A back buffer is created and it's drawn fairly conventionally:
private void form_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawImage(BackBuffer, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);
}
Certain regions of the back buffer get redrawn under various conditions; mouse-over effects are quite common. The class which does redrawing carefully invalidates only applicable areas. In spite of this, simply swiping the mouse across the form is enough to crank a high-end CPU to > 50% usage for several seconds.
I've profiled the application and well over 80% of the CPU time is being burnt on the call to DrawImage above. I knew that GDI+ is slow and employs no (little?) use of GPU... and the target platform for the application makes no guarantee that the GPU wont be integrated in the first place. But I had no idea that it was this bad.
Should I face the fact that GDI+ is just not fast enough for what we wish to do, or is there an opportunity for improvement in the code, still?
-edit-
BackBuffer represents a bitmap which is created during system startup. Its size matches the screen resolution. Various regions are drawn on it during a variety of events such as mouse overs and clicks. It is created rather simply:
this.BufferBmp = new Bitmap(screenWidth, screenHeight);
this.Gfx = Graphics.FromImage(BufferBmp);
Gfx.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
Creating your own 'back buffer' instead of using the built-in support for double-buffering is very hard to get performant. What matters a great deal is the pixel format of the buffer. On most any modern machine, any I tried anyway, the 32bppPArgb format is ten times as fast as any other. Favor the built-in double-buffering support for its superior perf.
Another possible loss is your attempt to keep the clipping region as small as possible. That can byte when the region gets complicated. It sounds like you're close to that if you let fast mouse movement generate small rectangular update regions. When the painting falls behind, it almost always will since the mouse gets a higher priority, you could build up a pretty intricate chain of small rectangles. First test this by just invalidating the entire area. Next approach is to expand the area yourself, always keeping it a single rectangle. Reset that when OnPaint runs.