mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
screen capture preview
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"Snap.Hutao": {
|
||||
"commandName": "MsixPackage",
|
||||
"nativeDebugging": false,
|
||||
"nativeDebugging": true,
|
||||
"doNotLaunchApp": false,
|
||||
"allowLocalNetworkLoopbackProperty": true
|
||||
},
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D11;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using Windows.Graphics.DirectX.Direct3D11;
|
||||
using WinRT;
|
||||
using static Snap.Hutao.Win32.ConstValues;
|
||||
using static Snap.Hutao.Win32.D3d11;
|
||||
using static Snap.Hutao.Win32.Dxgi;
|
||||
using static Snap.Hutao.Win32.Macros;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
|
||||
internal static class DirectX
|
||||
{
|
||||
public static unsafe bool TryCreateDXGIFactory(uint flags, out IDXGIFactory6* factory, out HRESULT hr)
|
||||
{
|
||||
hr = CreateDXGIFactory2(flags, in IDXGIFactory6.IID, out factory);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
public static unsafe bool TryGetHighPerformanceAdapter(IDXGIFactory6* factory, out IDXGIAdapter* adapter, out HRESULT hr)
|
||||
{
|
||||
hr = factory->EnumAdapterByGpuPreference(0U, DXGI_GPU_PREFERENCE.DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, in IDXGIAdapter.IID, out adapter);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(adapter);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool TryCreateD3D11Device(IDXGIAdapter* adapter, D3D11_CREATE_DEVICE_FLAG flags, out ID3D11Device* device, out HRESULT hr)
|
||||
{
|
||||
hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE, default, flags, [], D3D11_SDK_VERSION, out device, out _, out _);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(device);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool TryAsDXGIDevice(ID3D11Device* device, out IDXGIDevice* dxgiDevice, out HRESULT hr)
|
||||
{
|
||||
hr = IUnknownMarshal.QueryInterface(device, in IDXGIDevice.IID, out dxgiDevice);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(dxgiDevice);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool TryCreateDirect3D11Device(IDXGIDevice* dxgiDevice, [NotNullWhen(true)] out IDirect3DDevice? direct3DDevice, out HRESULT hr)
|
||||
{
|
||||
hr = CreateDirect3D11DeviceFromDXGIDevice(dxgiDevice, out Win32.System.WinRT.IInspectable* inspectable);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
direct3DDevice = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
direct3DDevice = IInspectable.FromAbi((nint)inspectable).ObjRef.AsInterface<IDirect3DDevice>();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static unsafe bool TryCreateSwapChainForComposition(IDXGIFactory6* factory, ID3D11Device* device, ref readonly DXGI_SWAP_CHAIN_DESC1 desc, out IDXGISwapChain1* swapChain, out HRESULT hr)
|
||||
{
|
||||
hr = factory->CreateSwapChainForComposition((IUnknown*)device, in desc, default, out swapChain);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//IUnknownMarshal.Release(swapChain);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,44 +4,106 @@
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D11;
|
||||
using Snap.Hutao.Win32.Graphics.Dwm;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi.Common;
|
||||
using Snap.Hutao.Win32.Graphics.Gdi;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using Snap.Hutao.Win32.System.WinRT.Graphics.Capture;
|
||||
using Windows.Graphics.Capture;
|
||||
using Windows.Graphics.DirectX;
|
||||
using Windows.Graphics.DirectX.Direct3D11;
|
||||
using static Snap.Hutao.Win32.ConstValues;
|
||||
using static Snap.Hutao.Win32.DwmApi;
|
||||
using static Snap.Hutao.Win32.Gdi32;
|
||||
using static Snap.Hutao.Win32.User32;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
|
||||
internal readonly struct GameScreenCaptureContext
|
||||
internal struct GameScreenCaptureContext : IDisposable
|
||||
{
|
||||
public readonly GraphicsCaptureItem Item;
|
||||
public readonly bool PreviewEnabled;
|
||||
|
||||
private const uint CreateDXGIFactoryFlag =
|
||||
#if DEBUG
|
||||
DXGI_CREATE_FACTORY_DEBUG;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
|
||||
private const D3D11_CREATE_DEVICE_FLAG D3d11CreateDeviceFlag =
|
||||
D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_BGRA_SUPPORT
|
||||
#if DEBUG
|
||||
| D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_DEBUG
|
||||
#endif
|
||||
;
|
||||
|
||||
private readonly IDirect3DDevice direct3DDevice;
|
||||
private readonly HWND hwnd;
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
public GameScreenCaptureContext(IDirect3DDevice direct3DDevice, HWND hwnd)
|
||||
{
|
||||
this.direct3DDevice = direct3DDevice;
|
||||
this.hwnd = hwnd;
|
||||
private unsafe IDXGIFactory6* factory;
|
||||
private unsafe IDXGIAdapter* adapter;
|
||||
private unsafe ID3D11Device* d3d11Device;
|
||||
private unsafe IDXGIDevice* dxgiDevice;
|
||||
private IDirect3DDevice? direct3DDevice;
|
||||
private unsafe IDXGISwapChain1* swapChain;
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
private unsafe GameScreenCaptureContext(HWND hwnd, bool preview)
|
||||
{
|
||||
this.hwnd = hwnd;
|
||||
GraphicsCaptureItem.As<IGraphicsCaptureItemInterop>().CreateForWindow(hwnd, out Item);
|
||||
|
||||
PreviewEnabled = preview;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
public static unsafe GameScreenCaptureContextCreationResult Create(HWND hwnd, bool preview)
|
||||
{
|
||||
GameScreenCaptureContext context = new(hwnd, preview);
|
||||
|
||||
if (!DirectX.TryCreateDXGIFactory(CreateDXGIFactoryFlag, out context.factory, out HRESULT hr))
|
||||
{
|
||||
return new(GameScreenCaptureContextCreationResultKind.CreateDxgiFactoryFailed, hr);
|
||||
}
|
||||
|
||||
if (!DirectX.TryGetHighPerformanceAdapter(context.factory, out context.adapter, out hr))
|
||||
{
|
||||
return new(GameScreenCaptureContextCreationResultKind.EnumAdapterByGpuPreferenceFailed, hr);
|
||||
}
|
||||
|
||||
if (!DirectX.TryCreateD3D11Device(default, D3d11CreateDeviceFlag, out context.d3d11Device, out hr))
|
||||
{
|
||||
return new(GameScreenCaptureContextCreationResultKind.D3D11CreateDeviceFailed, hr);
|
||||
}
|
||||
|
||||
if (!DirectX.TryAsDXGIDevice(context.d3d11Device, out context.dxgiDevice, out hr))
|
||||
{
|
||||
return new(GameScreenCaptureContextCreationResultKind.D3D11DeviceQueryDXGIDeviceFailed, hr);
|
||||
}
|
||||
|
||||
if (!DirectX.TryCreateDirect3D11Device(context.dxgiDevice, out context.direct3DDevice, out hr))
|
||||
{
|
||||
return new(GameScreenCaptureContextCreationResultKind.CreateDirect3D11DeviceFromDXGIDeviceFailed, hr);
|
||||
}
|
||||
|
||||
return new GameScreenCaptureContextCreationResult(GameScreenCaptureContextCreationResultKind.Success, context);
|
||||
}
|
||||
|
||||
public Direct3D11CaptureFramePool CreatePool()
|
||||
{
|
||||
return Direct3D11CaptureFramePool.CreateFreeThreaded(direct3DDevice, DeterminePixelFormat(hwnd), 2, Item.Size);
|
||||
(DirectXPixelFormat winrt, DXGI_FORMAT dx) = DeterminePixelFormat(hwnd);
|
||||
CreateOrUpdateDXGISwapChain(dx);
|
||||
return Direct3D11CaptureFramePool.CreateFreeThreaded(direct3DDevice, winrt, 2, Item.Size);
|
||||
}
|
||||
|
||||
public void RecreatePool(Direct3D11CaptureFramePool framePool)
|
||||
{
|
||||
framePool.Recreate(direct3DDevice, DeterminePixelFormat(hwnd), 2, Item.Size);
|
||||
(DirectXPixelFormat winrt, DXGI_FORMAT dx) = DeterminePixelFormat(hwnd);
|
||||
CreateOrUpdateDXGISwapChain(dx);
|
||||
framePool.Recreate(direct3DDevice, winrt, 2, Item.Size);
|
||||
}
|
||||
|
||||
public GraphicsCaptureSession CreateSession(Direct3D11CaptureFramePool framePool)
|
||||
public readonly GraphicsCaptureSession CreateSession(Direct3D11CaptureFramePool framePool)
|
||||
{
|
||||
GraphicsCaptureSession session = framePool.CreateCaptureSession(Item);
|
||||
session.IsCursorCaptureEnabled = false;
|
||||
@@ -49,7 +111,7 @@ internal readonly struct GameScreenCaptureContext
|
||||
return session;
|
||||
}
|
||||
|
||||
public bool TryGetClientBox(uint width, uint height, out D3D11_BOX clientBox)
|
||||
public readonly bool TryGetClientBox(uint width, uint height, out D3D11_BOX clientBox)
|
||||
{
|
||||
clientBox = default;
|
||||
|
||||
@@ -88,8 +150,39 @@ internal readonly struct GameScreenCaptureContext
|
||||
return clientBox.right <= width && clientBox.bottom <= height;
|
||||
}
|
||||
|
||||
public unsafe readonly void AttachPreview(GameScreenCaptureDebugPreviewWindow? window)
|
||||
{
|
||||
if (PreviewEnabled && window is not null)
|
||||
{
|
||||
window.UpdateSwapChain(swapChain);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe readonly void UpdatePreview(GameScreenCaptureDebugPreviewWindow? window, IDirect3DSurface surface)
|
||||
{
|
||||
if (PreviewEnabled && window is not null)
|
||||
{
|
||||
window.UnsafeUpdatePreview(d3d11Device, surface);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe readonly void DetachPreview(GameScreenCaptureDebugPreviewWindow? window)
|
||||
{
|
||||
if (PreviewEnabled && window is not null)
|
||||
{
|
||||
window.UpdateSwapChain(null);
|
||||
window.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe readonly void Dispose()
|
||||
{
|
||||
IUnknownMarshal.Release(factory);
|
||||
IUnknownMarshal.Release(swapChain);
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
private static DirectXPixelFormat DeterminePixelFormat(HWND hwnd)
|
||||
private static (DirectXPixelFormat WinRTFormat, DXGI_FORMAT DXFormat) DeterminePixelFormat(HWND hwnd)
|
||||
{
|
||||
HDC hdc = GetDC(hwnd);
|
||||
if (hdc != HDC.NULL)
|
||||
@@ -98,10 +191,31 @@ internal readonly struct GameScreenCaptureContext
|
||||
_ = ReleaseDC(hwnd, hdc);
|
||||
if (bitsPerPixel >= 32)
|
||||
{
|
||||
return DirectXPixelFormat.R16G16B16A16Float;
|
||||
return (DirectXPixelFormat.R16G16B16A16Float, DXGI_FORMAT.DXGI_FORMAT_R16G16B16A16_FLOAT);
|
||||
}
|
||||
}
|
||||
|
||||
return DirectXPixelFormat.B8G8R8A8UIntNormalized;
|
||||
return (DirectXPixelFormat.B8G8R8A8UIntNormalized, DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM);
|
||||
}
|
||||
|
||||
private unsafe void CreateOrUpdateDXGISwapChain(DXGI_FORMAT format)
|
||||
{
|
||||
if (!PreviewEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 desc = default;
|
||||
desc.Width = (uint)Item.Size.Width;
|
||||
desc.Height = (uint)Item.Size.Height;
|
||||
desc.Format = format;
|
||||
desc.SampleDesc.Count = 1;
|
||||
desc.BufferUsage = DXGI_USAGE.DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
desc.BufferCount = 2;
|
||||
desc.Scaling = DXGI_SCALING.DXGI_SCALING_STRETCH;
|
||||
desc.SwapEffect = DXGI_SWAP_EFFECT.DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
||||
desc.AlphaMode = DXGI_ALPHA_MODE.DXGI_ALPHA_MODE_PREMULTIPLIED;
|
||||
|
||||
DirectX.TryCreateSwapChainForComposition(factory, d3d11Device, in desc, out swapChain, out HRESULT hr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
|
||||
internal readonly struct GameScreenCaptureContextCreationResult
|
||||
{
|
||||
public readonly GameScreenCaptureContextCreationResultKind Kind;
|
||||
public readonly HRESULT HResult;
|
||||
public readonly GameScreenCaptureContext Context;
|
||||
|
||||
public GameScreenCaptureContextCreationResult(GameScreenCaptureContextCreationResultKind kind, HRESULT hResult)
|
||||
{
|
||||
Kind = kind;
|
||||
HResult = hResult;
|
||||
}
|
||||
|
||||
public GameScreenCaptureContextCreationResult(GameScreenCaptureContextCreationResultKind kind, GameScreenCaptureContext context)
|
||||
{
|
||||
Kind = kind;
|
||||
Context = context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
|
||||
internal enum GameScreenCaptureContextCreationResultKind
|
||||
{
|
||||
Success,
|
||||
CreateDxgiFactoryFailed,
|
||||
EnumAdapterByGpuPreferenceFailed,
|
||||
D3D11CreateDeviceFailed,
|
||||
D3D11DeviceQueryDXGIDeviceFailed,
|
||||
CreateDirect3D11DeviceFromDXGIDeviceFailed,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<Window
|
||||
x:Class="Snap.Hutao.Service.Game.Automation.ScreenCapture.GameScreenCaptureDebugPreviewWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Snap.Hutao.Service.Game.Automation.ScreenCapture"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<SwapChainPanel
|
||||
x:Name="Presenter"
|
||||
AllowFocusOnInteraction="False"
|
||||
AllowFocusWhenDisabled="False"/>
|
||||
</Window>
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D11;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi;
|
||||
using Snap.Hutao.Win32.System.WinRT.Graphics.Capture;
|
||||
using Snap.Hutao.Win32.System.WinRT.Xaml;
|
||||
using Windows.Graphics.DirectX.Direct3D11;
|
||||
using WinRT;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
|
||||
internal sealed partial class GameScreenCaptureDebugPreviewWindow : Window
|
||||
{
|
||||
private unsafe IDXGISwapChain1* swapChain1;
|
||||
|
||||
public GameScreenCaptureDebugPreviewWindow()
|
||||
{
|
||||
if (AppWindow.Presenter is OverlappedPresenter presenter)
|
||||
{
|
||||
presenter.IsMaximizable = false;
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
this.InitializeController(Ioc.Default);
|
||||
}
|
||||
|
||||
public unsafe void UpdateSwapChain(IDXGISwapChain1* swapChain1)
|
||||
{
|
||||
this.swapChain1 = swapChain1;
|
||||
ISwapChainPanelNative native = Presenter.As<IInspectable>().ObjRef.AsInterface<ISwapChainPanelNative>();
|
||||
native.SetSwapChain((IDXGISwapChain*)swapChain1);
|
||||
}
|
||||
|
||||
public unsafe void UnsafeUpdatePreview(ID3D11Device* device, IDirect3DSurface surface)
|
||||
{
|
||||
IDirect3DDxgiInterfaceAccess access = surface.As<IDirect3DDxgiInterfaceAccess>();
|
||||
swapChain1->GetBuffer(0, in ID3D11Texture2D.IID, out ID3D11Texture2D* buffer);
|
||||
device->GetImmediateContext(out ID3D11DeviceContext* deviceContext);
|
||||
access.GetInterface(in ID3D11Resource.IID, out ID3D11Resource* resource);
|
||||
deviceContext->CopyResource((ID3D11Resource*)buffer, resource);
|
||||
swapChain1->Present(0, default);
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,9 @@
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D11;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using Windows.Graphics.Capture;
|
||||
using Windows.Graphics.DirectX.Direct3D11;
|
||||
using WinRT;
|
||||
using static Snap.Hutao.Win32.ConstValues;
|
||||
using static Snap.Hutao.Win32.D3d11;
|
||||
using static Snap.Hutao.Win32.Dxgi;
|
||||
using static Snap.Hutao.Win32.Macros;
|
||||
|
||||
namespace Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
|
||||
@@ -55,61 +47,34 @@ internal sealed partial class GameScreenCaptureService : IGameScreenCaptureServi
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
public unsafe bool TryStartCapture(HWND hwnd, [NotNullWhen(true)] out GameScreenCaptureSession? session)
|
||||
public unsafe bool TryStartCapture(HWND hwnd, bool preview, [NotNullWhen(true)] out GameScreenCaptureSession? session)
|
||||
{
|
||||
session = default;
|
||||
|
||||
HRESULT hr;
|
||||
hr = CreateDXGIFactory2(CreateDXGIFactoryFlag, in IDXGIFactory6.IID, out IDXGIFactory6* factory);
|
||||
if (FAILED(hr))
|
||||
GameScreenCaptureContextCreationResult result = GameScreenCaptureContext.Create(hwnd, preview);
|
||||
|
||||
switch (result.Kind)
|
||||
{
|
||||
logger.LogWarning("CreateDXGIFactory2 failed with code: {Code}", hr);
|
||||
return false;
|
||||
case GameScreenCaptureContextCreationResultKind.Success:
|
||||
session = new(result.Context, logger);
|
||||
return true;
|
||||
case GameScreenCaptureContextCreationResultKind.CreateDxgiFactoryFailed:
|
||||
logger.LogWarning("CreateDXGIFactory2 failed with code: {Code}", result.HResult);
|
||||
return false;
|
||||
case GameScreenCaptureContextCreationResultKind.EnumAdapterByGpuPreferenceFailed:
|
||||
logger.LogWarning("IDXGIFactory6.EnumAdapterByGpuPreference failed with code: {Code}", result.HResult);
|
||||
return false;
|
||||
case GameScreenCaptureContextCreationResultKind.D3D11CreateDeviceFailed:
|
||||
logger.LogWarning("D3D11CreateDevice failed with code: {Code}", result.HResult);
|
||||
return false;
|
||||
case GameScreenCaptureContextCreationResultKind.D3D11DeviceQueryDXGIDeviceFailed:
|
||||
logger.LogWarning("ID3D11Device.QueryInterface<IDXGIDevice> failed with code: {Code}", result.HResult);
|
||||
return false;
|
||||
case GameScreenCaptureContextCreationResultKind.CreateDirect3D11DeviceFromDXGIDeviceFailed:
|
||||
logger.LogWarning("CreateDirect3D11DeviceFromDXGIDevice failed with code: {Code}", result.HResult);
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(factory);
|
||||
|
||||
hr = factory->EnumAdapterByGpuPreference(0U, DXGI_GPU_PREFERENCE.DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, in IDXGIAdapter.IID, out IDXGIAdapter* adapter);
|
||||
if (hr != HRESULT.DXGI_ERROR_NOT_FOUND)
|
||||
{
|
||||
logger.LogWarning("IDXGIFactory6.EnumAdapterByGpuPreference failed with code: {Code}", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(adapter);
|
||||
|
||||
hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE, default, D3d11CreateDeviceFlag, [], D3D11_SDK_VERSION, out ID3D11Device* pD3D11Device, out _, out _);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
logger.LogWarning("D3D11CreateDevice failed with code: {Code}", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(pD3D11Device);
|
||||
|
||||
hr = IUnknownMarshal.QueryInterface(pD3D11Device, in IDXGIDevice.IID, out IDXGIDevice* pDXGIDevice);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
logger.LogWarning("ID3D11Device.QueryInterface<IDXGIDevice> failed with code: {Code}", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(pDXGIDevice);
|
||||
|
||||
hr = CreateDirect3D11DeviceFromDXGIDevice(pDXGIDevice, out Win32.System.WinRT.IInspectable* inspectable);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
logger.LogWarning("CreateDirect3D11DeviceFromDXGIDevice failed with code: {Code}", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(inspectable);
|
||||
|
||||
IDirect3DDevice direct3DDevice = IInspectable.FromAbi((nint)inspectable).ObjRef.AsInterface<IDirect3DDevice>();
|
||||
|
||||
GameScreenCaptureContext captureContext = new(direct3DDevice, hwnd);
|
||||
session = new(captureContext, logger);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Win32.Graphics.Direct3D11;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi.Common;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using Snap.Hutao.Win32.System.WinRT.Graphics.Capture;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -24,6 +25,7 @@ internal sealed class GameScreenCaptureSession : IDisposable
|
||||
private static readonly Half ByteMaxValue = 255;
|
||||
|
||||
private readonly GameScreenCaptureContext captureContext;
|
||||
private readonly GameScreenCaptureDebugPreviewWindow? previewWindow;
|
||||
private readonly Direct3D11CaptureFramePool framePool;
|
||||
private readonly GraphicsCaptureSession session;
|
||||
private readonly ILogger logger;
|
||||
@@ -35,16 +37,23 @@ internal sealed class GameScreenCaptureSession : IDisposable
|
||||
private bool isDisposed;
|
||||
|
||||
[SuppressMessage("", "SH002")]
|
||||
public GameScreenCaptureSession(GameScreenCaptureContext captureContext, ILogger logger)
|
||||
public unsafe GameScreenCaptureSession(GameScreenCaptureContext captureContext, ILogger logger)
|
||||
{
|
||||
this.captureContext = captureContext;
|
||||
this.logger = logger;
|
||||
|
||||
contentSize = captureContext.Item.Size;
|
||||
|
||||
if (captureContext.PreviewEnabled)
|
||||
{
|
||||
previewWindow = new();
|
||||
}
|
||||
|
||||
captureContext.Item.Closed += OnItemClosed;
|
||||
|
||||
framePool = captureContext.CreatePool();
|
||||
captureContext.AttachPreview(previewWindow);
|
||||
|
||||
framePool.FrameArrived += OnFrameArrived;
|
||||
|
||||
session = captureContext.CreateSession(framePool);
|
||||
@@ -76,6 +85,7 @@ internal sealed class GameScreenCaptureSession : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
captureContext.DetachPreview(previewWindow);
|
||||
session.Dispose();
|
||||
framePool.Dispose();
|
||||
isDisposed = true;
|
||||
@@ -111,7 +121,8 @@ internal sealed class GameScreenCaptureSession : IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
UnsafeProcessFrameSurface(frame.Surface);
|
||||
captureContext.UpdatePreview(previewWindow, frame.Surface);
|
||||
//UnsafeProcessFrameSurface(frame.Surface);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -135,6 +146,8 @@ internal sealed class GameScreenCaptureSession : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(pDXGISurface);
|
||||
|
||||
if (FAILED(pDXGISurface->GetDesc(out DXGI_SURFACE_DESC dxgiSurfaceDesc)))
|
||||
{
|
||||
return;
|
||||
@@ -151,6 +164,8 @@ internal sealed class GameScreenCaptureSession : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(pD3D11Device);
|
||||
|
||||
D3D11_TEXTURE2D_DESC d3d11Texture2DDesc = default;
|
||||
d3d11Texture2DDesc.Width = textureWidth;
|
||||
d3d11Texture2DDesc.Height = textureHeight;
|
||||
@@ -166,12 +181,17 @@ internal sealed class GameScreenCaptureSession : IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(pD3D11Texture2D);
|
||||
|
||||
if (FAILED(access.GetInterface(in ID3D11Resource.IID, out ID3D11Resource* pD3D11Resource)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IUnknownMarshal.Release(pD3D11Resource);
|
||||
|
||||
pD3D11Device->GetImmediateContext(out ID3D11DeviceContext* pD3D11DeviceContext);
|
||||
IUnknownMarshal.Release(pD3D11DeviceContext);
|
||||
|
||||
if (boxAvailable)
|
||||
{
|
||||
|
||||
@@ -9,5 +9,5 @@ internal interface IGameScreenCaptureService
|
||||
{
|
||||
bool IsSupported();
|
||||
|
||||
bool TryStartCapture(HWND hwnd, [NotNullWhen(true)] out GameScreenCaptureSession? session);
|
||||
bool TryStartCapture(HWND hwnd, bool preview, [NotNullWhen(true)] out GameScreenCaptureSession? session);
|
||||
}
|
||||
@@ -142,6 +142,7 @@
|
||||
<None Remove="Resource\Navigation\WikiWeapon.png" />
|
||||
<None Remove="Resource\Segoe Fluent Icons.ttf" />
|
||||
<None Remove="Resource\WelcomeView_Background.png" />
|
||||
<None Remove="Service\Game\Automation\ScreenCapture\GameScreenCaptureDebugPreviewWindow.xaml" />
|
||||
<None Remove="stylecop.json" />
|
||||
<None Remove="View\Card\AchievementCard.xaml" />
|
||||
<None Remove="View\Card\CardBlock.xaml" />
|
||||
@@ -361,6 +362,11 @@
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Service\Game\Automation\ScreenCapture\GameScreenCaptureDebugPreviewWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Core\Windowing\NotifyIcon\NotifyIconContextMenu.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -119,6 +119,11 @@
|
||||
Header="Compensation GachaLog Service Time For 15 Days"
|
||||
IsClickEnabled="True"/>
|
||||
|
||||
<cwc:SettingsCard
|
||||
Command="{Binding ScreenCaptureCommand}"
|
||||
Header="Screen Capture Test"
|
||||
IsClickEnabled="True"/>
|
||||
|
||||
<cwc:SettingsCard Header="Crash">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<Button Command="{Binding ExceptionCommand}" Content="Activate"/>
|
||||
|
||||
@@ -7,9 +7,12 @@ using Snap.Hutao.Core.ExceptionService;
|
||||
using Snap.Hutao.Core.LifeCycle;
|
||||
using Snap.Hutao.Core.Setting;
|
||||
using Snap.Hutao.Core.Windowing;
|
||||
using Snap.Hutao.Service.Game.Automation.ScreenCapture;
|
||||
using Snap.Hutao.Service.Notification;
|
||||
using Snap.Hutao.View.Converter;
|
||||
using Snap.Hutao.ViewModel.Guide;
|
||||
using Snap.Hutao.Web.Hutao.HutaoAsAService;
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
|
||||
namespace Snap.Hutao.ViewModel;
|
||||
|
||||
@@ -27,6 +30,7 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
|
||||
private readonly ILogger<TestViewModel> logger;
|
||||
private readonly IMemoryCache memoryCache;
|
||||
private readonly ITaskContext taskContext;
|
||||
private readonly IGameScreenCaptureService gameScreenCaptureService;
|
||||
|
||||
private UploadAnnouncement announcement = new();
|
||||
|
||||
@@ -142,4 +146,21 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel
|
||||
logger.LogInformation("Failed ImageCache download tasks: [{Tasks}]", set?.ToString(','));
|
||||
}
|
||||
}
|
||||
|
||||
[Command("ScreenCaptureCommand")]
|
||||
private async Task ScreenCaptureAsync()
|
||||
{
|
||||
HWND hwnd = currentXamlWindowReference.GetWindowHandle();
|
||||
if (gameScreenCaptureService.TryStartCapture(hwnd, true, out GameScreenCaptureSession? session))
|
||||
{
|
||||
using (session)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
await session.RequestFrameAsync();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ internal readonly partial struct HRESULT
|
||||
public static readonly HRESULT S_OK = unchecked((int)0x00000000);
|
||||
public static readonly HRESULT E_FAIL = unchecked((int)0x80004005);
|
||||
public static readonly HRESULT DXGI_ERROR_NOT_FOUND = unchecked((int)0x887A0002);
|
||||
public static readonly HRESULT DXGI_ERROR_DEVICE_REMOVED = unchecked((int)0x887A0005);
|
||||
public static readonly HRESULT DXGI_ERROR_DEVICE_RESET = unchecked((int)0x887A0007);
|
||||
|
||||
public readonly int Value;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.System.Com;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -20,6 +21,21 @@ internal unsafe struct IDXGIFactory6
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe HRESULT CreateSwapChainForComposition(IUnknown* pDevice, ref readonly DXGI_SWAP_CHAIN_DESC1 desc, IDXGIOutput* pRestrictToOutput, out IDXGISwapChain1* pSwapChain)
|
||||
{
|
||||
fixed (DXGI_SWAP_CHAIN_DESC1* pDesc = &desc)
|
||||
{
|
||||
fixed (IDXGISwapChain1** ppSwapChain = &pSwapChain)
|
||||
{
|
||||
return ThisPtr->IDXGIFactory5Vftbl
|
||||
.IDXGIFactory4Vftbl
|
||||
.IDXGIFactory3Vftbl
|
||||
.IDXGIFactory2Vftbl
|
||||
.CreateSwapChainForComposition((IDXGIFactory2*)Unsafe.AsPointer(ref this), pDevice, pDesc, pRestrictToOutput, ppSwapChain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("", "SA1313")]
|
||||
public HRESULT EnumAdapterByGpuPreference<T>(uint Adapter, DXGI_GPU_PREFERENCE GpuPreference, ref readonly Guid iid, out T* pvAdapter)
|
||||
where T : unmanaged
|
||||
|
||||
@@ -8,7 +8,8 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Win32.Graphics.Dxgi;
|
||||
|
||||
internal unsafe readonly struct IDXGISwapChain1
|
||||
[SuppressMessage("", "SA1313")]
|
||||
internal unsafe struct IDXGISwapChain1
|
||||
{
|
||||
public readonly Vftbl* ThisPtr;
|
||||
|
||||
@@ -21,6 +22,23 @@ internal unsafe readonly struct IDXGISwapChain1
|
||||
}
|
||||
}
|
||||
|
||||
public HRESULT Present(uint SyncInterval, uint Flags)
|
||||
{
|
||||
return ThisPtr->IDXGISwapChainVftbl.Present((IDXGISwapChain*)Unsafe.AsPointer(ref this), SyncInterval, Flags);
|
||||
}
|
||||
|
||||
public HRESULT GetBuffer<T>(uint Buffer, ref readonly Guid iid, out T* pSurface)
|
||||
where T : unmanaged
|
||||
{
|
||||
fixed (Guid* riid = &iid)
|
||||
{
|
||||
fixed (T** ppSurface = &pSurface)
|
||||
{
|
||||
return ThisPtr->IDXGISwapChainVftbl.GetBuffer((IDXGISwapChain*)Unsafe.AsPointer(ref this), Buffer, riid, (void**)ppSurface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal readonly struct Vftbl
|
||||
{
|
||||
internal readonly IDXGISwapChain.Vftbl IDXGISwapChainVftbl;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Win32.Foundation;
|
||||
using Snap.Hutao.Win32.Graphics.Dxgi;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Win32.System.WinRT.Xaml;
|
||||
|
||||
[SuppressMessage("", "SYSLIB1096")]
|
||||
[ComImport]
|
||||
[Guid("63AAD0B8-7C24-40FF-85A8-640D944CC325")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
internal interface ISwapChainPanelNative
|
||||
{
|
||||
[PreserveSig]
|
||||
unsafe HRESULT SetSwapChain(IDXGISwapChain* swapChain);
|
||||
}
|
||||
Reference in New Issue
Block a user