views:

447

answers:

3

Hello,

I have problems with WPF drawing performance. There are a lot of small EllipseGeometry objects (1024 ellipses, for example), which are added to three separate GeometryGroups with different foreground brushes. After, I render it all on simple Image control. Code:

DrawingGroup tmpDrawing = new DrawingGroup();
GeometryGroup onGroup = new GeometryGroup();
GeometryGroup offGroup = new GeometryGroup();
GeometryGroup disabledGroup = new GeometryGroup();

for (int x = 0; x < DisplayWidth; ++x)
{
    for (int y = 0; y < DisplayHeight; ++y)
    {
        if (States[x, y] == true) onGroup.Children.Add(new EllipseGeometry(new Rect((double)x * EDGE, (double)y * EDGE, EDGE, EDGE)));
        else if (States[x, y] == false) offGroup.Children.Add(new EllipseGeometry(new Rect((double)x * EDGE, (double)y * EDGE, EDGE, EDGE)));
        else disabledGroup.Children.Add(new EllipseGeometry(new Rect((double)x * EDGE, (double)y * EDGE, EDGE, EDGE)));
    }
}

tmpDrawing.Children.Add(new GeometryDrawing(OnBrush, null, onGroup));
tmpDrawing.Children.Add(new GeometryDrawing(OffBrush, null, offGroup));
tmpDrawing.Children.Add(new GeometryDrawing(DisabledBrush, null, disabledGroup));
DisplayImage.Source = new DrawingImage(tmpDrawing);

It works fine, but takes too much time - >0.5s on Core 2 Quad, >2s on Pentium 4. I need <0.1s everywhere. All Ellipses, how you can see, are equal. Background of control, where is my DisplayImage, is solid (black, for example), so we can use this fact. I tried to use 1024 Ellipse elements instead of Image with EllipseGeometries, and it was working much faster (~0.5s), but not enough. How to speed up it?

Regards, Oleg Eremeev

P.S. Sorry for my English.

+1  A: 

Get it running under the IDE, and while it's painting hit the "pause" button. Display the call stack. Then do it again several times. That is this method. If it finishes too fast for you to "pause" it, wrap a temporary outer loop around it of 10 or 100 times.

You may find that a lot, if not most, of the time is going into allocating, initializing, and subsequently deleting the objects, rather than actually rendering them. If so, you can probably find a way around that.

My preference is not to "build" graphics, but to "draw" graphics. I don't like to make any more data structure than I absolutely need, in order to remember things the user has told me and that I will need later.

For painting, I always render to a bitmap, which I then block-transfer to the screen. It looks faster to the user, and it may actually be faster because it is not doing clipping logic.

Mike Dunlavey
Thank you for your answer, but I already know why it is so slow - I am using wrong method. Rendering to bitmap is good alternative, I thought about that. But it does not solve my problem, because I have low performance only while rendering something somewhere, not after that. This trick maybe speed up it a little, but not to <0.1s. The core issue is in rendering part of my program. For example, I tried to use 1024 Ellipse elements instead of Image with EllipseGeometries, and it was working much faster (~0.5s).
MyFaJoArCo
@MyFaJoArCo: I appreciate that, but if you know why it is slow, you may find yourself surprised that it is doing something else, and that is accounting for the time. Either way, a few samples of the call stack while it is slow will prove that you are right, if you are. If you are not, it will tell you what the problem is. Often people say "I know what it's doing, and there's not much I can do about it." Even if you are right about what it's doing, there could well be something you can do about it. Good luck.
Mike Dunlavey
A: 

I left my old rendering method, but creating new EllipseGeometry object each time was bad idea, so I optimized it in this way:

for (int x = 0; x < newWidth; ++x)
{
    for (int y = 0; y < newHeight; ++y)
    {
        States[x, y] = null;
        OnEllipses[x, y] = new EllipseGeometry(new Rect((double)x * EDGE + 0.5f, (double)y * EDGE + 0.5f, EDGE - 1f, EDGE - 1f));
        OffEllipses[x, y] = new EllipseGeometry(new Rect((double)x * EDGE + 0.5f, (double)y * EDGE + 0.5f, EDGE - 1f, EDGE - 1f));
        DisabledEllipses[x, y] = new EllipseGeometry(new Rect((double)x * EDGE + 0.5f, (double)y * EDGE + 0.5f, EDGE - 1f, EDGE - 1f));
    }
}

// . . .

DrawingGroup tmpDrawing = new DrawingGroup();
GeometryGroup onGroup = new GeometryGroup();
GeometryGroup offGroup = new GeometryGroup();
GeometryGroup disabledGroup = new GeometryGroup();

for (int x = 0; x < DisplayWidth; ++x)
{
    for (int y = 0; y < DisplayHeight; ++y)
    {
        if (States[x, y] == true) onGroup.Children.Add(OnEllipses[x, y]);
        else if (States[x, y] == false) offGroup.Children.Add(OffEllipses[x, y]);
        else disabledGroup.Children.Add(DisabledEllipses[x, y]);
    }
}

tmpDrawing.Children.Add(new GeometryDrawing(OnBrush, null, onGroup));
tmpDrawing.Children.Add(new GeometryDrawing(OffBrush, null, offGroup));
tmpDrawing.Children.Add(new GeometryDrawing(DisabledBrush, null, disabledGroup));
DisplayImage.Source = new DrawingImage(tmpDrawing);

For x = 128 and y = 8 it works really fast, even on Pentium III systems.

MyFaJoArCo
A: 

Even if I am a little late: This post by Charles Petzold helped a lot in a similar scenario.

Jens
Thanks for answer, I may use it in another project in future. My real solution works fine, so I think I leave it as is.
MyFaJoArCo