0

SO basically, I need performance. Currently in my job we use GDI+ graphics to draw bitmap. Gdi+ graphics contains a method called DrawImage(Bitmap,Points[]). That array contains 3 points and the rendered image result with a skew effect.

Here is an image of what is a skew effect :

Skew effect At work, we need to render between 5000 and 6000 different images each single frame which takes ~ 80ms.

Now I thought of using SharpDX since it provides GPU accelerations. I use direct2D since all I need is in 2 dimensions. However, the only way I saw to reproduce a skew effect is the use the SharpDX.effects.Skew and calculate matrix to draw the initial bitmap with a skew effect ( I will provide the code below). The rendered image is exactly the same as GDI+ and it is what I want. The only problem is it takes 600-700ms to render the 5000-6000images.

Here is the code of my SharpDX :

To initiate device :

private void InitializeSharpDX()
      {
         swapchaindesc = new SwapChainDescription()
         {
            BufferCount = 2,
            ModeDescription = new ModeDescription(this.Width, this.Height, new Rational(60, 1), Format.B8G8R8A8_UNorm),
            IsWindowed = true,
            OutputHandle = this.Handle,
            SampleDescription = new SampleDescription(1, 0),
            SwapEffect = SwapEffect.Discard,
            Usage = Usage.RenderTargetOutput,
            Flags = SwapChainFlags.None
         };
         SharpDX.Direct3D11.Device.CreateWithSwapChain(DriverType.Hardware, DeviceCreationFlags.BgraSupport | DeviceCreationFlags.Debug, swapchaindesc, out device, out swapchain);
         SharpDX.DXGI.Device dxgiDevice = device.QueryInterface<SharpDX.DXGI.Device>();
         surface = swapchain.GetBackBuffer<Surface>(0);
         factory = new SharpDX.Direct2D1.Factory1(FactoryType.SingleThreaded, DebugLevel.Information);
         d2device = new SharpDX.Direct2D1.Device(factory, dxgiDevice);
         d2deviceContext = new SharpDX.Direct2D1.DeviceContext(d2device, SharpDX.Direct2D1.DeviceContextOptions.EnableMultithreadedOptimizations);
         bmpproperties = new BitmapProperties(new SharpDX.Direct2D1.PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, SharpDX.Direct2D1.AlphaMode.Premultiplied),
           96, 96);
         d2deviceContext.AntialiasMode = AntialiasMode.Aliased;

         bmp = new SharpDX.Direct2D1.Bitmap(d2deviceContext, surface, bmpproperties);
         d2deviceContext.Target = bmp;

      }

And here is my code I use to recalculate every image positions each frame (each time I do a mouse zoom in or out, I asked for a redraw). You can see in the code two loop of 5945 images where I asked to draw the image. No effects takes 60ms and with effects, it takes up to 700ms as I mentionned before :

  private void DrawSkew()
      {

         d2deviceContext.BeginDraw();
         d2deviceContext.Clear(SharpDX.Color.Blue);

         //draw skew effect to 5945 images using SharpDX (370ms)
         for (int i = 0; i < 5945; i++)
         {
            AffineTransform2D effect = new AffineTransform2D(d2deviceContext);
            PointF[] points = new PointF[3];
            points[0] = new PointF(50, 50);
            points[1] = new PointF(400, 40);
            points[2] = new PointF(40, 400);


            effect.SetInput(0, actualBmp, true);
            float xAngle = (float)Math.Atan(((points[1].Y - points[0].Y) / (points[1].X - points[0].X)));
            float yAngle = (float)Math.Atan(((points[2].X - points[0].X) / (points[2].Y - points[0].Y)));

            Matrix3x2 Matrix = Matrix3x2.Identity;
            Matrix3x2.Skew(xAngle, yAngle, out  Matrix);
            Matrix.M11 = Matrix.M11 * (((points[1].X - points[0].X) + (points[2].X - points[0].X)) / actualBmp.Size.Width);
            Matrix.M22 = Matrix.M22 * (((points[1].Y - points[0].Y) + (points[2].Y - points[0].Y)) / actualBmp.Size.Height);
            effect.TransformMatrix = Matrix;

            d2deviceContext.DrawImage(effect, new SharpDX.Vector2(points[0].X, points[0].Y));

            effect.Dispose();
         }
         //draw no effects, only actual bitmap 5945 times using SharpDX (60ms)
         for (int i = 0; i < 5945; i++)
         {
            d2deviceContext.DrawBitmap(actualBmp, 1.0f, BitmapInterpolationMode.NearestNeighbor);
         }
         d2deviceContext.EndDraw();
         swapchain.Present(1, PresentFlags.None);

      }

After benching a lot, I realized the line that make it really slow is :

 d2deviceContext.DrawImage(effect, new SharpDX.Vector2(points[0].X, points[0].Y));

My guess is my code or my setup does not use GPU acceleration of SharpDX like it should and this is why the code is really slow. I would expect at least better performance from SharpDX than GDI+ for this kind of stuff.

NickD
  • 191
  • 15
  • Can I ask, why so many images? You're likely still hitting a limitation of how fast things can be drawn. I have a similar project that draws lines and arcs, and i needed to performance test it for 40,000+ objects. I ended up checking each indvidiual object to make sure it was within view, and only rendering it if it was. – oppassum Feb 14 '18 at 17:23
  • We have an image that is composed by many many images (5000+) that are smaller and place up front. These image are there to show defect and our main image. Since this is job related I cannot really answer on why that many has to be drawn because I don't know the answer myself since it's been there for years, but I believe it has to be that way. Of course, there is frustum cullling so the deeper you're zooming in the image, the less you have to render, but still.. – NickD Feb 14 '18 at 18:05
  • You might see much better performance by stitching all the images together and then skewing the composite image. That may only be possible using Direct3D directly; I have worked with 2D graphics, but using Direct3D directly because Direct2D has additional overhead and less flexibility. Calling DrawImage thousands of times is likely slow, since it forces the GPU to flush state, stop, synchronize state with the device driver, and then render the new image. You really only want to call DrawImage 1-2 times in your case. – NextInLine Feb 15 '18 at 16:59
  • So basically, if I understand, I should draw all the images into a bitmap instead of in the device, and then draw the result bitmap to the device ? – NickD Feb 21 '18 at 15:06
  • You could draw all the images into a bitmap, then resize the bitmap down (it has to be less than 16K or 8K or 4K pixels in width/height depending on the graphics card). However that would all happen on the CPU. A more performant solution would be to put all the images on the GPU as separate Direct3D11.Texture2D textures, with multiple mipmap levels (basically, different pre-calculated zoom renderings), and then combine and skew all those diferent textures at once on the GPU. That would likely require Direct3D though instead of Direct2D unless there's a way for Direct2D to do that. – NextInLine Feb 21 '18 at 17:05

0 Answers0