Desktop region capture with Vortice.DXGI return black bitmap
See the question and my original answer on StackOverflowThere are issues with your code. Here are the issue that explain why it doesn't work more than once and requires a strange Sleep
call:
- You don't dispose everything (you should, I guess this is releasing underlying COM interfaces)
- You don't Unmap what you've mapped.
- You don't ReleaseFrame acquired frames.
But there are other issues
- You can use the device context created by D3D11CreateDevice, you don't need to use this ImmediateContext property.
- If you want to maximize duplication performance, you shouldn't use CPU mapped textures directly in the acquiring frames loop which you must keep very fast. You should copy them (like you do) and pass them to the CPU using another thread for example
- The Duplication API is not a screen-capture API, it's a screen duplication API (!) which means it will copy rendered frames to like a "mailbox" you query when you can and want. The timeout can be kept relatively important. When you get a timeout, it just means nothing has changed on the screen. The
frameInfo
parameter has useful information about all this.
Here is a version that seems to work (acquires 10 frames and saves them as bitmap files):
class Program
{
private static readonly FeatureLevel[] _featureLevels = new[]
{
FeatureLevel.Level_11_0,
FeatureLevel.Level_10_1,
FeatureLevel.Level_10_0,
FeatureLevel.Level_9_3,
FeatureLevel.Level_9_2,
FeatureLevel.Level_9_1,
};
static void Main()
{
DXGI.CreateDXGIFactory1<IDXGIFactory1>(out var factory);
if (factory == null)
return;
using (factory)
{
using var adapter = factory.GetAdapter(0);
using var output = adapter.GetOutput(0);
using var output1 = output.QueryInterface<IDXGIOutput1>();
D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, _featureLevels, out var device, out var deviceContext);
if (device == null)
return;
using (device)
{
var rectangle = new Rectangle(0, 0, output.Description.DesktopCoordinates.Right, output.Description.DesktopCoordinates.Bottom);
var texture2dDescription = new Texture2DDescription
{
ArraySize = 1,
CPUAccessFlags = CpuAccessFlags.Read | CpuAccessFlags.Write,
Format = Format.B8G8R8A8_UNorm,
MipLevels = 1,
SampleDescription = { Count = 1, Quality = 0 },
Usage = ResourceUsage.Staging,
Height = rectangle.Bottom,
Width = rectangle.Right
};
using var currentFrame = device.CreateTexture2D(texture2dDescription);
using var duplicatedOutput = output1.DuplicateOutput(device);
using var frame = new Bitmap(rectangle.Right, rectangle.Bottom, PixelFormat.Format32bppRgb);
var index = 0;
rectangle.X = 0;
do
{
duplicatedOutput.AcquireNextFrame(500, out var frameInfo, out var desktopResource);
if (desktopResource != null)
{
using (desktopResource)
{
using var tempTexture = desktopResource.QueryInterface<ID3D11Texture2D>();
deviceContext.CopyResource(currentFrame, tempTexture);
var dataBox = deviceContext.Map(currentFrame, 0, MapMode.Read);
var mapDest = frame.LockBits(rectangle, ImageLockMode.WriteOnly, frame.PixelFormat);
for (int y = rectangle.Y, sizeInBytesToCopy = rectangle.Width * 4; y < rectangle.Height; y++)
{
MemoryHelpers.CopyMemory(mapDest.Scan0 + y * rectangle.Right * 4, dataBox.DataPointer + y * dataBox.RowPitch, sizeInBytesToCopy);
}
deviceContext.Unmap(currentFrame, 0);
frame.UnlockBits(mapDest);
frame.Save("bitmap" + index++ + ".png", ImageFormat.Png);
}
}
duplicatedOutput.ReleaseFrame();
}
while (index < 10);
}
}
}
}