using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Drawing.Imaging; using System.IO; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.Versioning; using AntVortice.Helpers; using Vortice; using Vortice.DXGI; using Vortice.Direct3D11; using ID3D11Device = Vortice.Direct3D11.ID3D11Device; using MapFlags = Vortice.Direct3D11.MapFlags; using Vortice.Direct3D; using SharpGen.Runtime; namespace SharpDX_Test { public class CaptureScreenVortice { public enum DPI_AWARENESS_CONTEXT { DPI_AWARENESS_CONTEXT_DEFAULT = 0, DPI_AWARENESS_CONTEXT_UNAWARE = -1, DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = -2, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = -3, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4, DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = -5, } // for Windows 10 version RS2 and above [DllImport("user32.dll", SetLastError = true)] [ResourceExposure(ResourceScope.None)] public static extern bool SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT dpiFlag); [DllImport("msvcrt.dll", EntryPoint = "memcpy_s", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] public static extern IntPtr memcpy(IntPtr dest, ulong dstLen, IntPtr src, ulong srcLen); private static readonly FeatureLevel[] s_featureLevelsNoLevel11 = new[] { FeatureLevel.Level_11_0, FeatureLevel.Level_10_1, FeatureLevel.Level_10_0 }; public static readonly ResultDescriptor WaitTimeout = new ResultDescriptor(-2005270489, "Vortice.DXGI", "DXGI_ERROR_WAIT_TIMEOUT", "WaitTimeout"); // TODO: Split into multiple functions later public Bitmap CaptureScreen() { // Application.SetHighDpiMode(HighDpiMode.PerMonitorV2); // SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); // # of graphics card adapter const int numAdapter = 0; // # of output device (i.e. monitor) const int numOutput = 0; // 0 = primary? , 1 = secondary? // For DuplicateOutput to succeed, you must create pDevice // from SharpDX.DXGI.Factory1 or a later version of a DXGI factory interface that // inherits from SharpDX.DXGI.Factory1 // Create DXGI Factory1 DXGI.CreateDXGIFactory1(out IDXGIFactory1 factory); // contains video card adapters var adapter = factory.GetAdapter1(numAdapter); // EnumAdapters1(numAdapter), graphics card // Create device from Adapter var grakaDevice = D3D11Helper.CreateDevice(adapter); // => D3D11.D3D11CreateDevice(adapter, DriverType.Unknown //, DeviceCreationFlags.None, null, out ID3D11Device grakaDevice, // out FeatureLevel fl, out ID3D11DeviceContext context); var monitor = adapter.GetOutput(numOutput); // EnumOutputs(numOutput) var monitor_op1 = monitor.QueryInterface(); var monitor_op5 = monitor.QueryInterface(); var bounds = monitor.Description.DesktopCoordinates; var width = bounds.Right - bounds.Left; var height = bounds.Bottom - bounds.Top; // Create Staging texture CPU-accessible var textureDesc = new Texture2DDescription { CpuAccessFlags = CpuAccessFlags.Read, BindFlags = BindFlags.None, Format = Format.B8G8R8A8_UNorm, // Format.B8G8R8A8_UNorm, B8G8R8A8_UNorm_SRgb Width = width, Height = height, OptionFlags = ResourceOptionFlags.None, MipLevels = 1, ArraySize = 1, SampleDescription = { Count = 1, Quality = 0 }, Usage = Vortice.Direct3D11.Usage.Staging }; var cpuScreenTexture = grakaDevice.CreateTexture2D(textureDesc); // must be set before DuplicateOutput1 // SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); // var duplicatedOutput = monitor_op1.DuplicateOutput(grakaDevice); // IDXGIOutput5::DuplicateOutput1 is supposed to be faster, doesnt convert the image var duplicatedOutput = monitor_op1.DuplicateOutput(grakaDevice); //var duplicatedOutput = monitor_op5.DuplicateOutput1(grakaDevice, new Format[] { Format.B8G8R8A8_UNorm }); Bitmap bitmap = null; IDXGIResource gpuScreenResource; // has tons of information OutduplFrameInfo duplicateFrameInformation; // first frame is black? duplicatedOutput.AcquireNextFrame(10000, out duplicateFrameInformation, out gpuScreenResource); duplicatedOutput.ReleaseFrame(); gpuScreenResource.Dispose(); var sw = new Stopwatch(); //To correct visual effects, an application must // process the move region data before it processes the dirty rectangles // IDXGIOutputDuplication::GetFrameMoveRects, // IDXGIOutputDuplication::GetFrameDirtyRects, // IDXGIOutputDuplication::GetFramePointerShape for (int k = 0; k < 5; k++) { try { // Try to get changed frame within given time // will wait until the picture has changed... duplicatedOutput.AcquireNextFrame(10000, out duplicateFrameInformation, out gpuScreenResource); sw.Start(); // gpuScreenTexture contains the Format of current screen (GPU) // copy resource from GPU (gpuScreenTexture) into memory that can be accessed by the CPU using (var gpuScreenTexture = gpuScreenResource.QueryInterface()) grakaDevice.ImmediateContext.CopyResource(gpuScreenTexture, cpuScreenTexture); // CopyResource could silently fail? duplicatedOutput.ReleaseFrame(); // should be close to AcquireNextFrame var d = duplicateFrameInformation; Debug.WriteLine($"changes {d.AccumulatedFrames} b: {d.TotalMetadataBufferSize}"); // Create Drawing.Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); var boundsRect = new Rectangle(0, 0, width, height); // Copy pixels from screen capture Texture to GDI bitmap var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat); var dstPtr = mapDest.Scan0; var stride = mapDest.Stride; var dstLen = (ulong)(height * stride); // Get the desktop capture texture // denies the GPU access to that subresource var mapSource = grakaDevice.ImmediateContext.Map(cpuScreenTexture, 0, Map.Read, MapFlags.None); var srcPtr = mapSource.PData; var srcLen = (ulong)mapSource.DepthPitch; var testArr = new byte[srcLen]; // TODO: Error Here Marshal.Copy(srcPtr, testArr, 0, (int)srcLen); // testArr is empty byte[] { 0,0,0,0,0,0,0 } why? //memcpy(dstPtr, dstLen, srcPtr, srcLen); //// Release source and dest locks //grakaDevice.ImmediateContext.Unmap(cpuScreenTexture, 0); //bitmap.UnlockBits(mapDest); //gpuScreenResource.Dispose(); //sw.Stop(); //bitmap.Save($@"G:\DXGI_Test_{k}.jpg", ImageFormat.Jpeg); //var end43534 = 155; } catch (SharpGenException e) // SharpDXException { var desc5 = e.Descriptor; var rc5 = e.ResultCode; if (e.ResultCode.Code != WaitTimeout.Result.Code) { throw; } } } var elapsed = sw.ElapsedMilliseconds; //duplicatedOutput.Dispose(); //cpuScreenTexture.Dispose(); //// monitor_op1.Dispose(); //// monitor_op5.Dispose(); //monitor.Dispose(); // grakaDevice.ImmediateContext.Dispose(); //grakaDevice.Dispose(); //adapter.Dispose(); //factory.Dispose(); //var tr = DXGIDebug.TryCreate(); //tr.typeof(T).GetTypeInfo().GUID(new Guid(0xe48ae283, 0xda80, 0x490b, 0x87, 0xe6, 0x43, 0xe9, 0xa9, 0xcf, 0xda, 0x8), // DebugRloFlags.All); return null; } } }