9
\$\begingroup\$

I'm trying to use direct2d to render images off-screen using WindowsAPICodePack. This is easily achieved using WicBitmapRenderTarget but sadly it's not hardware accelerated.

So I'm trying this route:

  1. Create direct3d device
  2. Create texture2d
  3. Use texture surface to create render target using CreateDxgiSurfaceRenderTarget
  4. Draw some shapes

While this renders the image it appears GPU isn't being used at all while CPU is used heavily.

Am I doing something wrong? Is there a way to check whether hardware or software rendering is used?

Code sample:

var device = D3DDevice1.CreateDevice1(null,
                DriverType.Hardware,
                null,
                CreateDeviceOptions.SupportBgra,
                FeatureLevel.Ten
                );

var txd = new Texture2DDescription();
txd.Width = 256;
txd.Height = 256;
txd.MipLevels = 1;
txd.ArraySize = 1;
txd.Format = Format.B8G8R8A8UNorm; //DXGI_FORMAT_R32G32B32A32_FLOAT;
txd.SampleDescription = new SampleDescription(1,0);
txd.Usage = Usage.Default;
txd.BindingOptions = BindingOptions.RenderTarget | BindingOptions.ShaderResource;
txd.MiscellaneousResourceOptions = MiscellaneousResourceOptions.None;
txd.CpuAccessOptions = CpuAccessOptions.None;

var tx = device.CreateTexture2D(txd);

var srfc = tx.GraphicsSurface;

var d2dFactory = D2DFactory.CreateFactory();

var renderTargetProperties = new RenderTargetProperties
{
    PixelFormat = new PixelFormat(Format.Unknown, AlphaMode.Premultiplied),
    DpiX = 96,
    DpiY = 96,
    RenderTargetType = RenderTargetType.Default,
};

using(var renderTarget = d2dFactory.CreateGraphicsSurfaceRenderTarget(srfc, renderTargetProperties))
{
    renderTarget.BeginDraw();
    var clearColor = new ColorF(1f,1f,1f,1f);
    renderTarget.Clear(clearColor);
    using (var strokeBrush = renderTarget.CreateSolidColorBrush(new ColorF(0.2f,0.2f,0.2f,1f)))
    {
        for (var i = 0; i < 100000; i++)
        {
            renderTarget.DrawEllipse(new Ellipse(new Point2F(i, i), 10, 10), strokeBrush, 2);
        }
    }

    var hr = renderTarget.EndDraw();
}
\$\endgroup\$

2 Answers 2

6
\$\begingroup\$

Direct2D is using the GPU unless you specified not to use it... but unfortunately, Direct2D is in fact not as efficient as we would expect. There was a similar question on gamedev forum "Fastest way to draw connected lines?" I ran again the test on your test case and the results are again a bit scary...

When you are trying to draw 10000 ellipse on the screen (size 10,10), the GPU is effectively receiving:

  • 1,500,000 vertices => 500,000 triangles to render, if you turn off anti-aliasing mode on the render target
  • 4,411,734 vertices => 1,470,578 triangles to render, with antialias mode enabled (by default in your case)

Because the geometry is not know in advance, whenever you call DrawEllipse between renderTarget.BeginDraw and EndDraw, the part of Direct2D code that needs to run on the CPU is quite intensive, as It needs to generate all those vertices (It as to triangularize the whole surface and generate tiny triangles in order to cover the outer line of the ellipse) and send all commands to the GPU (several hundreds Direct3D10 commands). Each triangle is in fact covering almost a pixel region.

You can lower CPU usage by turning of AntiAliased mode, but then you would have to use AntiAliased post-effects shaders in order to reduce the aliasing.

In order to check this yourself, you can run your application with PIX debugger that is available from DirectX SDK.

On a side note, you should not use WindowsAPICodePack, as It is an API that is not really efficient and not actively maintained. I would suggest you to use SharpDX, which is supporting all Direct2D/DirectWrite features (including callbacks, for example for tessellation, which are not supported by any other managed API).

\$\endgroup\$
3
  • \$\begingroup\$ Thank you for taking the time. And thanks for the SharpDX suggestion. Will test the project on my usecase... \$\endgroup\$
    – Goran
    Commented Nov 22, 2011 at 22:36
  • \$\begingroup\$ Your answer basically covers it, but there is a 3rd option for AA. You can turn off D2D AntiAliasing but render to a MSAA D3D target and then use ResolveSubresource. I've found that this results in similar quality to D2D's analytic method but with much better perf. \$\endgroup\$
    – Lucas
    Commented Nov 23, 2011 at 20:26
  • \$\begingroup\$ @Lucas, indeed for MSAA as It requires less setup than using post-effects anti-aliasing techniques such as FXAA, SMAA... etc. \$\endgroup\$
    – xoofx
    Commented Nov 25, 2011 at 1:25
1
\$\begingroup\$

It does use hardware acceleration, but GPU is used much more less because there is no drawing into visible area. GPU usage depends on cards performance, so if you try this on highend, there will really little GPU usege. When you try that on low end (with enough CPU power), usage will magically raise.

So GPU usage will tell you if acc is on. If RenderTargetType is default, acceleration will be used if supported. If you set hardware, it will be used or error otherwise.

Anyway, if you arent using Direct3D, you should use BitmapRenderTarget, and d2dbitmap as a off-screen buffer. Its faster.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .