From 53cda02071e7de20b1415d9e5770437dcf002e61 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sat, 1 Jun 2024 22:53:56 +0800 Subject: [PATCH] screen capture preview --- .../Snap.Hutao/Properties/launchSettings.json | 2 +- .../Game/Automation/ScreenCapture/DirectX.cs | 86 +++++++++++ .../ScreenCapture/GameScreenCaptureContext.cs | 142 ++++++++++++++++-- .../GameScreenCaptureContextCreationResult.cs | 25 +++ ...eScreenCaptureContextCreationResultKind.cs | 14 ++ .../GameScreenCaptureDebugPreviewWindow.xaml | 14 ++ ...ameScreenCaptureDebugPreviewWindow.xaml.cs | 47 ++++++ .../ScreenCapture/GameScreenCaptureService.cs | 83 +++------- .../ScreenCapture/GameScreenCaptureSession.cs | 24 ++- .../IGameScreenCaptureService.cs | 2 +- src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 6 + .../Snap.Hutao/View/Page/TestPage.xaml | 5 + .../Snap.Hutao/ViewModel/TestViewModel.cs | 21 +++ .../Snap.Hutao/Win32/Foundation/HRESULT.cs | 2 + .../Win32/Graphics/Dxgi/IDXGIFactory6.cs | 16 ++ .../Win32/Graphics/Dxgi/IDXGISwapChain1.cs | 20 ++- .../WinRT/Xaml/ISwapChainPanelNative.cs | 18 +++ 17 files changed, 449 insertions(+), 78 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/DirectX.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResult.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResultKind.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Xaml/ISwapChainPanelNative.cs diff --git a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json index a5fb2a14..36a6b395 100644 --- a/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json +++ b/src/Snap.Hutao/Snap.Hutao/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Snap.Hutao": { "commandName": "MsixPackage", - "nativeDebugging": false, + "nativeDebugging": true, "doNotLaunchApp": false, "allowLocalNetworkLoopbackProperty": true }, diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/DirectX.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/DirectX.cs new file mode 100644 index 00000000..2fdd8016 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/DirectX.cs @@ -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(); + 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; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContext.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContext.cs index 4c0d10f8..de6620a5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContext.cs @@ -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().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); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResult.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResult.cs new file mode 100644 index 00000000..903d7c31 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResult.cs @@ -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; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResultKind.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResultKind.cs new file mode 100644 index 00000000..d51c449d --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureContextCreationResultKind.cs @@ -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, +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml new file mode 100644 index 00000000..a7a8b02a --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml @@ -0,0 +1,14 @@ + + + + diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml.cs new file mode 100644 index 00000000..07918412 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureDebugPreviewWindow.xaml.cs @@ -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().ObjRef.AsInterface(); + native.SetSwapChain((IDXGISwapChain*)swapChain1); + } + + public unsafe void UnsafeUpdatePreview(ID3D11Device* device, IDirect3DSurface surface) + { + IDirect3DDxgiInterfaceAccess access = surface.As(); + 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); + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureService.cs index 4adb31d9..f31f44cc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureService.cs @@ -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 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 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(); - - GameScreenCaptureContext captureContext = new(direct3DDevice, hwnd); - session = new(captureContext, logger); - - return true; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureSession.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureSession.cs index 91391bac..3e2be4bf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureSession.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/GameScreenCaptureSession.cs @@ -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) { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/IGameScreenCaptureService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/IGameScreenCaptureService.cs index 84aefae5..aeccbc49 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/IGameScreenCaptureService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Automation/ScreenCapture/IGameScreenCaptureService.cs @@ -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); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj index 37fbbba5..40283196 100644 --- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj +++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj @@ -142,6 +142,7 @@ + @@ -361,6 +362,11 @@ + + + MSBuild:Compile + + MSBuild:Compile diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/TestPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/TestPage.xaml index d0229615..75d81062 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/TestPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/TestPage.xaml @@ -119,6 +119,11 @@ Header="Compensation GachaLog Service Time For 15 Days" IsClickEnabled="True"/> + +