refactor Fischless.WindowCapture & add DwmSharedSurface capture

This commit is contained in:
huiyadanli
2023-10-07 23:26:56 +08:00
parent a8fa869842
commit 1fc0301551
22 changed files with 268 additions and 211 deletions

View File

@@ -0,0 +1,49 @@
using System.Runtime.InteropServices;
using Windows.Graphics.Capture;
using WinRT;
namespace Fischless.GameCapture.Graphics.Helpers;
public static class CaptureHelper
{
static readonly Guid GraphicsCaptureItemGuid = new("79C3F95B-31F7-4EC2-A464-632EF5D30760");
[ComImport]
[Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IInitializeWithWindow
{
void Initialize(
nint hWnd);
}
[ComImport]
[Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IGraphicsCaptureItemInterop
{
nint CreateForWindow(
[In] nint window,
[In] ref Guid iid);
nint CreateForMonitor(
[In] nint monitor,
[In] ref Guid iid);
}
public static void SetWindow(this GraphicsCapturePicker picker, nint hWnd)
{
var interop = picker.As<IInitializeWithWindow>();
interop.Initialize(hWnd);
}
public static GraphicsCaptureItem CreateItemForWindow(nint hWnd)
{
var factory = WinrtModule.GetActivationFactory("Windows.Graphics.Capture.GraphicsCaptureItem");
var interop = factory.AsInterface<IGraphicsCaptureItemInterop>();
var itemPointer = interop.CreateForWindow(hWnd, GraphicsCaptureItemGuid);
return GraphicsCaptureItem.FromAbi(itemPointer);
}
}

View File

@@ -0,0 +1,115 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Windows.Graphics.DirectX.Direct3D11;
using WinRT;
namespace Fischless.GameCapture.Graphics.Helpers;
[SuppressMessage("CodeQuality", "IDE0052:")]
public static class Direct3D11Helper
{
internal static Guid IInspectable = new("AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90");
internal static Guid ID3D11Resource = new("dc8e63f3-d12b-4952-b47b-5e45026a862d");
internal static Guid IDXGIAdapter3 = new("645967A4-1392-4310-A798-8053CE3E93FD");
internal static Guid ID3D11Device = new("db6f6ddb-ac77-4e88-8253-819df9bbf140");
internal static Guid ID3D11Texture2D = new("6f15aaf2-d208-4e89-9ab4-489535d34f9c");
[ComImport]
[Guid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
interface IDirect3DDxgiInterfaceAccess
{
nint GetInterface([In] ref Guid iid);
};
[DllImport(
"d3d11.dll",
EntryPoint = "CreateDirect3D11DeviceFromDXGIDevice",
SetLastError = true,
CharSet = CharSet.Unicode,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall
)]
static extern uint CreateDirect3D11DeviceFromDXGIDevice(nint dxgiDevice, out nint graphicsDevice);
[DllImport(
"d3d11.dll",
EntryPoint = "CreateDirect3D11SurfaceFromDXGISurface",
SetLastError = true,
CharSet = CharSet.Unicode,
ExactSpelling = true,
CallingConvention = CallingConvention.StdCall
)]
static extern uint CreateDirect3D11SurfaceFromDXGISurface(nint dxgiSurface, out nint graphicsSurface);
public static IDirect3DDevice CreateDevice()
{
return CreateDevice(false);
}
public static IDirect3DDevice CreateDevice(bool useWARP)
{
var d3dDevice = new SharpDX.Direct3D11.Device(
useWARP ? SharpDX.Direct3D.DriverType.Software : SharpDX.Direct3D.DriverType.Hardware,
SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport);
var device = CreateDirect3DDeviceFromSharpDXDevice(d3dDevice);
return device;
}
public static IDirect3DDevice CreateDirect3DDeviceFromSharpDXDevice(SharpDX.Direct3D11.Device d3dDevice)
{
IDirect3DDevice device = null;
// Acquire the DXGI interface for the Direct3D device.
using (var dxgiDevice = d3dDevice.QueryInterface<SharpDX.DXGI.Device3>())
{
// Wrap the native device using a WinRT interop object.
uint hr = CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice.NativePointer, out nint pUnknown);
if (hr == 0)
{
device = MarshalInterface<IDirect3DDevice>.FromAbi(pUnknown);
Marshal.Release(pUnknown);
}
}
return device;
}
public static IDirect3DSurface CreateDirect3DSurfaceFromSharpDXTexture(SharpDX.Direct3D11.Texture2D texture)
{
IDirect3DSurface surface = null;
// Acquire the DXGI interface for the Direct3D surface.
using (var dxgiSurface = texture.QueryInterface<SharpDX.DXGI.Surface>())
{
// Wrap the native device using a WinRT interop object.
uint hr = CreateDirect3D11SurfaceFromDXGISurface(dxgiSurface.NativePointer, out nint pUnknown);
if (hr == 0)
{
surface = Marshal.GetObjectForIUnknown(pUnknown) as IDirect3DSurface;
Marshal.Release(pUnknown);
}
}
return surface;
}
public static SharpDX.Direct3D11.Device CreateSharpDXDevice(IDirect3DDevice device)
{
var access = device.As<IDirect3DDxgiInterfaceAccess>();
var d3dPointer = access.GetInterface(ID3D11Device);
var d3dDevice = new SharpDX.Direct3D11.Device(d3dPointer);
return d3dDevice;
}
public static SharpDX.Direct3D11.Texture2D CreateSharpDXTexture2D(IDirect3DSurface surface)
{
var access = surface.As<IDirect3DDxgiInterfaceAccess>();
var d3dPointer = access.GetInterface(ID3D11Texture2D);
var d3dSurface = new SharpDX.Direct3D11.Texture2D(d3dPointer);
return d3dSurface;
}
}

View File

@@ -0,0 +1,71 @@
using System.Diagnostics;
using System.Drawing.Imaging;
using Windows.Graphics.Capture;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using Windows.Devices.HumanInterfaceDevice;
namespace Fischless.GameCapture.Graphics.Helpers;
public static class Texture2DExtensions
{
public static Bitmap? ToBitmap(this Direct3D11CaptureFrame frame, ResourceRegion? region = null)
{
var texture2dBitmap = Direct3D11Helper.CreateSharpDXTexture2D(frame.Surface);
var d3dDevice = texture2dBitmap.Device;
// Create texture copy
var staging = new Texture2D(d3dDevice, new Texture2DDescription
{
Width = region == null ? frame.ContentSize.Width : region.Value.Right - region.Value.Left,
Height = region == null ? frame.ContentSize.Height : region.Value.Bottom - region.Value.Top,
MipLevels = 1,
ArraySize = 1,
Format = texture2dBitmap.Description.Format,
Usage = ResourceUsage.Staging,
SampleDescription = new SampleDescription(1, 0),
BindFlags = BindFlags.None,
CpuAccessFlags = CpuAccessFlags.Read,
OptionFlags = ResourceOptionFlags.None
});
return staging.CreateBitmap(d3dDevice, texture2dBitmap, region);
}
public static Bitmap? CreateBitmap(this Texture2D staging, SharpDX.Direct3D11.Device d3dDevice, Texture2D surfaceTexture, ResourceRegion? region = null)
{
try
{
// Copy data
if (region != null)
{
d3dDevice.ImmediateContext.CopySubresourceRegion(surfaceTexture, 0, region, staging, 0);
}
else
{
d3dDevice.ImmediateContext.CopyResource(surfaceTexture, staging);
}
var dataBox = d3dDevice.ImmediateContext.MapSubresource(staging, 0, 0, MapMode.Read,
SharpDX.Direct3D11.MapFlags.None,
out DataStream stream);
var bitmap = new Bitmap(staging.Description.Width, staging.Description.Height, dataBox.RowPitch,
PixelFormat.Format32bppArgb, dataBox.DataPointer);
return bitmap;
}
catch (Exception e)
{
Debug.WriteLine("Failed to copy texture to bitmap.");
Debug.WriteLine(e.StackTrace);
return null;
}
finally
{
staging.Dispose();
}
}
}

View File

@@ -0,0 +1,111 @@
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using WinRT;
namespace Fischless.GameCapture.Graphics;
#pragma warning disable CS0649
[ComImport]
[Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IGraphicsCaptureItemInterop
{
int CreateForWindow([In] nint window, [In] ref Guid iid, out nint result);
int CreateForMonitor([In] nint monitor, [In] ref Guid iid, out nint result);
}
[Guid("00000035-0000-0000-C000-000000000046")]
internal unsafe struct IActivationFactoryVftbl
{
public readonly IInspectable.Vftbl IInspectableVftbl;
private readonly void* _ActivateInstance;
public delegate* unmanaged[Stdcall]<nint, nint*, int> ActivateInstance => (delegate* unmanaged[Stdcall]<nint, nint*, int>)_ActivateInstance;
}
internal class Platform
{
[DllImport("api-ms-win-core-com-l1-1-0.dll")]
internal static extern int CoDecrementMTAUsage(nint cookie);
[DllImport("api-ms-win-core-com-l1-1-0.dll")]
internal static extern unsafe int CoIncrementMTAUsage(nint* cookie);
[DllImport("api-ms-win-core-winrt-l1-1-0.dll")]
internal static extern unsafe int RoGetActivationFactory(nint runtimeClassId, ref Guid iid, nint* factory);
}
/// <summary>
/// https://github.com/zlatanov/windows-screen-recorder
/// </summary>
internal static class WinrtModule
{
private static readonly Dictionary<string, ObjectReference<IActivationFactoryVftbl>> Cache = new();
public static ObjectReference<IActivationFactoryVftbl> GetActivationFactory(string runtimeClassId)
{
lock (Cache)
{
if (Cache.TryGetValue(runtimeClassId, out var factory))
return factory;
var m = MarshalString.CreateMarshaler(runtimeClassId);
try
{
var instancePtr = GetActivationFactory(MarshalString.GetAbi(m));
factory = ObjectReference<IActivationFactoryVftbl>.Attach(ref instancePtr);
Cache.Add(runtimeClassId, factory);
return factory;
}
finally
{
m.Dispose();
}
}
}
private static unsafe nint GetActivationFactory(nint hstrRuntimeClassId)
{
if (s_cookie == IntPtr.Zero)
{
lock (s_lock)
{
if (s_cookie == IntPtr.Zero)
{
nint cookie;
Marshal.ThrowExceptionForHR(Platform.CoIncrementMTAUsage(&cookie));
s_cookie = cookie;
}
}
}
Guid iid = typeof(IActivationFactoryVftbl).GUID;
nint instancePtr;
int hr = Platform.RoGetActivationFactory(hstrRuntimeClassId, ref iid, &instancePtr);
if (hr == 0)
return instancePtr;
throw new Win32Exception(hr);
}
public static bool ResurrectObjectReference(IObjectReference objRef)
{
var disposedField = objRef.GetType().GetField("disposed", BindingFlags.NonPublic | BindingFlags.Instance)!;
if (!(bool)disposedField.GetValue(objRef)!)
return false;
disposedField.SetValue(objRef, false);
GC.ReRegisterForFinalize(objRef);
return true;
}
private static nint s_cookie;
private static readonly object s_lock = new();
}