mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-21 09:45:48 +08:00
refactor Fischless.WindowCapture & add DwmSharedSurface capture
This commit is contained in:
49
Fischless.GameCapture/Graphics/Helpers/CaptureHelper.cs
Normal file
49
Fischless.GameCapture/Graphics/Helpers/CaptureHelper.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
115
Fischless.GameCapture/Graphics/Helpers/Direct3D11Helper.cs
Normal file
115
Fischless.GameCapture/Graphics/Helpers/Direct3D11Helper.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Fischless.GameCapture/Graphics/Helpers/WinRT.cs
Normal file
111
Fischless.GameCapture/Graphics/Helpers/WinRT.cs
Normal 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();
|
||||
}
|
||||
Reference in New Issue
Block a user