12

I'm developing a Dotnet 4.0 application on Win7 that needs to perform mapping. As a mapping application it outputs crap loads of high resolution antialiased polygons. It currently supports two types of rendering output, GDI+ and Direct2D.

I'm concerned because the GDI+ output is around a factor of 3 faster than the Direct2D.

Both renderers are using AA. I know I can turn it off in Direct2D which improves the throughput somewhat (down to about a factor of 2 worse than GDI+). But that isn't a solution, since I can turn AA off in GDI+ too and get even better performance there. For the purpose of this benchmark my rendering code is trivial. I'm hoping I've made some ghastly mistake that someone can point out to me that will remedy the situation.

_renderTarget.BeginDraw();

// Paint background.
RectF rf = new RectF(0.0f, 0.0f, renderTargetSize.Width, renderTargetSize.Height);
_renderTarget.FillRectangle(rf, _backgroundBrush);

// Draw polygons
foreach (GisTypes.Polygon polygon in _polygons)
{
    using (PathGeometry path = _factory.CreatePathGeometry())
    {
        using (GeometrySink sink = path.Open())
        {
            sink.SetFillMode(Microsoft.WindowsAPICodePack.DirectX.Direct2D1
                .FillMode.Alternate);

            Point2F[] points = Array.ConvertAll(polygon.Points,
                x => new Point2F((float)x.X, (float)x.Y));

            sink.BeginFigure(points[0], FigureBegin.Filled);

            for (int i = 1; i < points.Length; ++i)
            {
                sink.AddLine(points[i]);
            }

            sink.EndFigure(FigureEnd.Closed);
            sink.Close();
        }

        using (TransformedGeometry transformedPath = _factory.CreateTransformedGeometry(
            path, WorldToPage))
        {
            _renderTarget.FillGeometry(transformedPath, _fillBrush);
            _renderTarget.DrawGeometry(transformedPath, _borderBrush, 1.0f);
        }
    }
}

_renderTarget.EndDraw();

In this sample code I'm using one path and one figure per polygon. The reason is because it more closely mirrors the GDI+ implemention and in the actual application rather than the sample code it simplifies rendering of the selected polyons. I know I could use a single path and multiple figures, and I have tried it that way, it is marginally faster but not by enough to have a bearing on the general issue.

I'm also using a TransformedGeometry rather than setting the transform on the RenderTarget. The reason for that is because although I want the geometry transformed I do not want the outline transformed because the scaling factor would cause it to disappear entirely.

In the particular sample data I'm using there are only a couple of hundred polygons, but each polygon can have several thousand points, so I don't think the allocation of multiple PathGeometry and TransformedGeometry during the rendering is the issue, (since there aren't many of them, and I've already tried it using just one PathGeometry and TransformedGeometry and the difference was marginal).

I wondered whether I shouldn't be rendering to an offscreen RenderTarget and blitting the result to the onscreen RenderTarget, but I've read the MSDN article on improving Direct2D performance and it made no mention of this as an optimization.

Anyone got any ideas?

zdd
  • 8,258
  • 8
  • 46
  • 75
Neutrino
  • 8,496
  • 4
  • 57
  • 83
  • You should only draw what's in view. Can't figure out from your code if you filter geometries that are in view first and then displaying only those. – CodeAngry Sep 21 '13 at 00:37
  • 1
    Everything is in view for both D2D and GDI+ and any further optimization I might make could be applied equally to both D2D and GDI+ so that wouldn't explain why the D2D performance is so poor in comparison to GDI+. – Neutrino Sep 23 '13 at 08:39
  • I'm at the same point (I'm adding support for rendering with Direct2D) and I believe that offscreen rendering should help (I think it's not mentioned in that topic because they assume dynamic scene rather than static). Have you managed to resolve this issue in some way? – Victoria Dec 07 '17 at 05:51
  • 1
    Six years later... D2D performance remains an issue for many people. It would be very nice if the OP would return to tell us whether an explanation was ever found. – Craig.Feied Apr 19 '18 at 00:56

2 Answers2

3

I believe that your drawing routine is creating resources too often, which is really time consuming. Geometries and Sinks being device-independent resources, you should create and retain them as long as they are not modified. Those modifications usually occur when you... change what should be drawn, of course, and when you move/resize the window or pan/zoom the content. Your drawing routine would then only draw already existing resources and should be faster.

Hope this helps.

Papaya
  • 332
  • 3
  • 15
  • Thanks for that, but what you suggest is what I'm already doing. The routine above is being called in the WPF equivalent of WM_PAINT and as such is only running when the view is panned or zoomed, and the data being rendered is only that which is within the viewable area. While it's true that were I to draw less it would render faster, but equally if I draw less with GDI that will also render even faster as well, so rendering less to improve the D2D performance doesn't explain the poor performance of D2D relative to GDI. – Neutrino Jan 08 '13 at 10:06
  • Ok, I didn't realize you were using WPF. I guess that you already profiled your routine to see what particular instruction(s) take so much time, so I have no other idea, sorry. – Papaya Jan 08 '13 at 14:47
0

Since you ask "got any ideas?", this Visual Studio tools might help: :-)

Graphics Diagnostics: lets you capture a frame and explore in excruciating detail the sequence of Direct3D calls; it may help you diagnose the problem if your render target is Direct3D (hardware). Similar to RenderDoc.

GPU Usage tool in the Visual Studio Performance and Diagnostics Hub: timing, but rather coarse grained compared to Graphics Diagnostics.

Concurrency Visualizer: timing, call stacks, GPU usage, and you can add markers/flags from code.

Also, you could use different render targets (say, GDI-compatible vs hardware) in Direct2D and compare that against pure GDI.

Pablo H
  • 609
  • 4
  • 22