Saving Surface to Bitmap and optimizing DirectX screen capture in C#

15,781

Solution 1

If you don't want to use SlimDX library you can also try

public Bitmap GimmeBitmap(Surface s)
{
    GraphicsStream gs = SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, s);
    return new Bitmap(gs);
}

and try the same for .png - I did not test performance but it have to be faster than using disc temporary file :)

and as for 1st question - try to only once create surface and then on every screenshot only put into it device's buffer data and create the bitmap

d.GetFrontBufferData(0, s);
return new Bitmap(SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, s));

this should save you some time :)

Solution 2

If performance really is an issue, you should consider writing your code in C++ instead. Therefor you dont need an external library but can directly access the backend-buffer of your video card via Windows-API + DirectX.

Accessing the backend(-video)-buffer is a lot faster than reading from the frontend-buffer.

To optimize performance (which also awnsers your question 1) use multithreading (see TPL or threading depending on your needs).

Here is an inside of how to do it in C++ CodeProject examples in C++. From my personal experience, DirectX was by far the fastest.

These steps

1. reading backend-buffer into a bitmap to process the data
2. spawning new thread to repeat step 1 while previous thread is still busy

take about 10-40ms (together) - implemented in C++ (on NVIDIA GeForce GTX 970M) and depending on the current workload of the hardware

Possible middle course

If you want to stick with C# but also need the performance, writing a C++-dll for .NET (see .NET Programming with C++/CLI (Visual C++)) which reads the video buffer and returns the data to your C#-Code will do the trick.

Share:
15,781

Related videos on Youtube

Alex
Author by

Alex

Updated on February 13, 2022

Comments

  • Alex
    Alex over 2 years

    after a whole day of testing I came up with this code, which captures current screen using DirectX (SlimDX) and saves it into a file:

    Device d;
    
    public DxScreenCapture()
    {
        PresentParameters present_params = new PresentParameters();
        present_params.Windowed = true;
        present_params.SwapEffect = SwapEffect.Discard;
        d = new Device(new Direct3D(), 0, DeviceType.Hardware, IntPtr.Zero, CreateFlags.SoftwareVertexProcessing, present_params);
    }
    
    public Surface CaptureScreen()
    {
        Surface s = Surface.CreateOffscreenPlain(d, Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, Format.A8R8G8B8, Pool.Scratch);
        d.GetFrontBufferData(0, s);
        return s;
    }
    

    Then I do the following:

       DxScreenCapture sc = new DxScreenCapture();
    

    ..code here

        private void button1_Click(object sender, EventArgs e)
        {
    
            Stopwatch stopwatch = new Stopwatch();
    
            // Begin timing
            stopwatch.Start();
    
            Surface s = sc.CaptureScreen();
            Surface.ToFile(s, @"c:\temp\test.png", ImageFileFormat.Png);
    
            s.Dispose();
    
            stopwatch.Stop();
    
            textBox1.Text = ("Elapsed:" + stopwatch.Elapsed.TotalMilliseconds);
        }
    

    The results are:

    0. when I don't save surface: avg. elapsed time: 80-90ms

    1. when I also save Surface to BMP file: format: ImageFileFormat.Bmp , avg. elapsed time: 120ms, file size: 7mb

    2. when I also save Surface to PNG file: format: ImageFileFormat.Png , avg. elapsed time: 800ms, file size: 300kb

    The questions are:

    1. Is it possible to optimise current image capture? According to this article - Directx screen capture should be faster than GDI. For me, GDI usually takes 20ms to get a "Bitmap", whereas it takes 80ms to get "Surfare" using DX (both without saving).

    http://www.codeproject.com/Articles/274461/Very-fast-screen-capture-using-DirectX-in-Csharp

    2a. How to save Surface to PNG image format faster? When I save surface to 7mb BMP file it takes almost 6 times less time, than when I save the same surface to 300kb PNG file..

    2b. Is it possible to save Surface directly to Bitmap so I don't have to create temporary files?

    So I don't have to do following: Surface -> image file; image file open -> bitmap;, but instead: Surface -> bitmap

    that's all for now. I'll gladly accept any tips, thanks!

    Edit:

    Just solved 2b by doing:

    Bitmap bitmap = new Bitmap(SlimDX.Direct3D9.Surface.ToStream(s, SlimDX.Direct3D9.ImageFileFormat.Bmp));
    

    Edit2:

    Surface.ToFile(s, @"C:\temp\test.bmp", ImageFileFormat.Bmp);
    Bitmap bitmap = new Bitmap(@"C:\temp\test.bmp");
    

    is faster than:

    Bitmap bitmap = new Bitmap(SlimDX.Direct3D9.Surface.ToStream(s, SlimDX.Direct3D9.ImageFileFormat.Bmp));
    

    by 100 ms!!! Yeah, I couldn't believe my eyes too ;) I don't like the idea of temporary file creation, but a 50% performance increase (100-200ms instead of 200-300+) is a very good thing.

    • Admin
      Admin about 12 years
      I think png can compress faster than 800 ms. Try if it is faster if you first write to a memorystream.
  • Alex
    Alex about 12 years
    SlimDX doesn't work that way.. I get tons of errors with "SurfaceLoader" (The name 'SurfaceLoader' does not exist in the current context). I think that I has something to do with "Managed DirectX" library, which is not supported in .net 4 so I can't add the reference. It's like C# VisualStudion doesn't know what "SurfaceLoader" and "GraphicsStream" are..
  • Alex
    Alex about 12 years
    P.S. I've already installed DX SDK but unfortunattely there is no "Managed DX" reference in VS2010. After some digging I found out that we no longder(June 2010 SDK+) can use DX, all that's left is SlimDX (wrapper) or XNA (used mainly for game dev).
  • Runaurufu
    Runaurufu about 12 years
    SurfaceLoader is not in SlimDX but in native DirectX (Microsoft.DirectX.Direct3D.SurfaceLoader) and it works on .NET4... And if you cannot find dll for managed code go look at "C:\Windows\Microsoft.NET\DirectX for Managed Code".