diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs index a2f1a400..95ed2f77 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/TestViewModel.cs @@ -13,13 +13,19 @@ 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.Graphics.Dxgi.Common; using Snap.Hutao.Win32.System.Com; using Snap.Hutao.Win32.System.WinRT; using Snap.Hutao.Win32.System.WinRT.Graphics.Capture; -using Windows.Graphics; +using System.IO; +using System.Runtime.CompilerServices; +using Windows.Foundation; using Windows.Graphics.Capture; using Windows.Graphics.DirectX; using Windows.Graphics.DirectX.Direct3D11; +using Windows.Graphics.Imaging; +using Windows.Storage.Streams; +using WinRT; using static Snap.Hutao.Win32.ConstValues; using static Snap.Hutao.Win32.D3D11; using static Snap.Hutao.Win32.Macros; @@ -42,8 +48,6 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel private readonly ITaskContext taskContext; private readonly MainWindow mainWindow; - private long counter; - private UploadAnnouncement announcement = new(); public UploadAnnouncement Announcement { get => announcement; set => SetProperty(ref announcement, value); } @@ -159,10 +163,8 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel [Command("TestWindowsGraphicsCaptureCommand")] private unsafe void TestWindowsGraphicsCapture() { - counter = 0; - // https://github.com/obsproject/obs-studio/blob/master/libobs-winrt/winrt-capture.cpp - if (!UniversalApiContract.IsPresent(WindowsVersion.Windows10Version1903)) + if (!Core.UniversalApiContract.IsPresent(WindowsVersion.Windows10Version1903)) { logger.LogWarning("Windows 10 Version 1903 or later is required for Windows.Graphics.Capture API."); return; @@ -180,21 +182,100 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel { if (SUCCEEDED(IUnknownMarshal.QueryInterface(pD3D11Device, in IDXGIDevice.IID, out IDXGIDevice* pDXGIDevice))) { - if (SUCCEEDED(CreateDirect3D11DeviceFromDXGIDevice(pDXGIDevice, out IInspectable* inspectable))) + if (SUCCEEDED(CreateDirect3D11DeviceFromDXGIDevice(pDXGIDevice, out Win32.System.WinRT.IInspectable* inspectable))) { - IDirect3DDevice direct3DDevice = WinRT.CastExtensions.As(WinRT.IInspectable.FromAbi((nint)inspectable)); + IDirect3DDevice direct3DDevice = WinRT.IInspectable.FromAbi((nint)inspectable).ObjRef.AsInterface(); - SizeInt32 size = new(1920, 1080); - using (Direct3D11CaptureFramePool framePool = Direct3D11CaptureFramePool.CreateFreeThreaded(direct3DDevice, DirectXPixelFormat.B8G8R8A8UIntNormalized, 2, size)) + HWND hwnd = serviceProvider.GetRequiredService().GetWindowHandle(); + GraphicsCaptureItem.As().CreateForWindow(hwnd, out GraphicsCaptureItem item); + + using (Direct3D11CaptureFramePool framePool = Direct3D11CaptureFramePool.CreateFreeThreaded(direct3DDevice, DirectXPixelFormat.B8G8R8A8UIntNormalized, 2, item.Size)) { - framePool.FrameArrived += (pool, obj) => + framePool.FrameArrived += (pool, _) => { - Interlocked.Increment(ref counter); - using (Direct3D11CaptureFrame frame = framePool.TryGetNextFrame()) + using (Direct3D11CaptureFrame frame = pool.TryGetNextFrame()) { if (frame is not null) { - logger.LogInformation("Content Size: {Width} x {Height} {Count}", frame.ContentSize.Width, frame.ContentSize.Height, Volatile.Read(ref counter)); + logger.LogInformation("Content Size: {Width} x {Height}", frame.ContentSize.Width, frame.ContentSize.Height); + + IDirect3DDxgiInterfaceAccess access = frame.Surface.As(); + if (FAILED(access.GetInterface(in IDXGISurface.IID, out IDXGISurface* pDXGISurface))) + { + return; + } + + if (FAILED(pDXGISurface->GetDesc(out DXGI_SURFACE_DESC surfaceDesc))) + { + return; + } + + D3D11_TEXTURE2D_DESC texture2DDesc = default; + texture2DDesc.Width = surfaceDesc.Width; + texture2DDesc.Height = surfaceDesc.Height; + texture2DDesc.ArraySize = 1; + texture2DDesc.CPUAccessFlags = D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_READ; + texture2DDesc.Format = DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM; + texture2DDesc.MipLevels = 1; + texture2DDesc.SampleDesc.Count = 1; + texture2DDesc.Usage = D3D11_USAGE.D3D11_USAGE_STAGING; + + if (FAILED(pDXGISurface->GetDevice(in ID3D11Device.IID, out ID3D11Device* pD3D11Device))) + { + return; + } + + if (FAILED(pD3D11Device->CreateTexture2D(ref texture2DDesc, ref Unsafe.NullRef(), out ID3D11Texture2D* pTexture2D))) + { + return; + } + + if (FAILED(access.GetInterface(in ID3D11Resource.IID, out ID3D11Resource* pD3D11Resource))) + { + return; + } + + pD3D11Device->GetImmediateContext(out ID3D11DeviceContext* pDeviceContext); + pDeviceContext->CopyResource((ID3D11Resource*)pTexture2D, pD3D11Resource); + + if (FAILED(pDeviceContext->Map((ID3D11Resource*)pTexture2D, 0, D3D11_MAP.D3D11_MAP_READ, 0, out D3D11_MAPPED_SUBRESOURCE mappedSubresource))) + { + return; + } + + int size = (int)(mappedSubresource.RowPitch * texture2DDesc.Height * 4); + + SoftwareBitmap softwareBitmap = new(BitmapPixelFormat.Bgra8, (int)texture2DDesc.Width, (int)texture2DDesc.Height, BitmapAlphaMode.Premultiplied); + using (BitmapBuffer bitmapBuffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Write)) + { + using (IMemoryBufferReference reference = bitmapBuffer.CreateReference()) + { + reference.As().GetBuffer(out Span bufferSpan); + fixed (byte* p = bufferSpan) + { + for (uint i = 0; i < texture2DDesc.Height; i++) + { + System.Buffer.MemoryCopy(((byte*)mappedSubresource.pData) + (i * mappedSubresource.RowPitch), p + (i * texture2DDesc.Width * 4), texture2DDesc.Width * 4, texture2DDesc.Width * 4); + } + } + } + } + + using (InMemoryRandomAccessStream stream = new()) + { + BitmapEncoder encoder = BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream).AsTask().Result; + encoder.SetSoftwareBitmap(softwareBitmap); + encoder.FlushAsync().AsTask().Wait(); + + using (FileStream fileStream = new("D:\\test.png", FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) + { + stream.AsStream().CopyTo(fileStream); + } + } + + _ = 1; + + pDeviceContext->Unmap((ID3D11Resource*)pTexture2D, 0); } else { @@ -203,9 +284,6 @@ internal sealed partial class TestViewModel : Abstraction.ViewModel } }; - HWND hwnd = serviceProvider.GetRequiredService().GetWindowHandle(); - GraphicsCaptureItem.As().CreateForWindow(hwnd, out GraphicsCaptureItem item); - using (GraphicsCaptureSession captureSession = framePool.CreateCaptureSession(item)) { captureSession.IsCursorCaptureEnabled = false; diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/D3D11_MAPPED_SUBRESOURCE.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/D3D11_MAPPED_SUBRESOURCE.cs index d4489a9d..7f2952df 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/D3D11_MAPPED_SUBRESOURCE.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/D3D11_MAPPED_SUBRESOURCE.cs @@ -3,6 +3,7 @@ namespace Snap.Hutao.Win32.Graphics.Direct3D11; +[SuppressMessage("", "SA1307")] internal struct D3D11_MAPPED_SUBRESOURCE { public unsafe void* pData; diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11Device.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11Device.cs index c098e615..11cc05fb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11Device.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11Device.cs @@ -12,7 +12,7 @@ using System.Runtime.Versioning; namespace Snap.Hutao.Win32.Graphics.Direct3D11; [SupportedOSPlatform("windows6.1")] -internal unsafe readonly struct ID3D11Device +internal unsafe struct ID3D11Device { public readonly Vftbl* ThisPtr; @@ -25,6 +25,28 @@ internal unsafe readonly struct ID3D11Device } } + public HRESULT CreateTexture2D(ref readonly D3D11_TEXTURE2D_DESC desc, [AllowNull] ref readonly D3D11_SUBRESOURCE_DATA initialData, [MaybeNull] out ID3D11Texture2D* pTexture2D) + { + fixed (D3D11_TEXTURE2D_DESC* pDesc = &desc) + { + fixed (D3D11_SUBRESOURCE_DATA* pInitialData = &initialData) + { + fixed (ID3D11Texture2D** ppTexture2D = &pTexture2D) + { + return ThisPtr->CreateTexture2D((ID3D11Device*)Unsafe.AsPointer(ref this), pDesc, pInitialData, ppTexture2D); + } + } + } + } + + public void GetImmediateContext(out ID3D11DeviceContext* pImmediateContext) + { + fixed (ID3D11DeviceContext** ppImmediateContext = &pImmediateContext) + { + ThisPtr->GetImmediateContext((ID3D11Device*)Unsafe.AsPointer(ref this), ppImmediateContext); + } + } + internal readonly struct Vftbl { internal readonly IUnknown.Vftbl IUnknownVftbl; diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11DeviceContext.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11DeviceContext.cs index 4649bfb4..692ecc0d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11DeviceContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Direct3D11/ID3D11DeviceContext.cs @@ -11,7 +11,7 @@ using System.Runtime.Versioning; namespace Snap.Hutao.Win32.Graphics.Direct3D11; [SupportedOSPlatform("windows6.1")] -internal unsafe readonly struct ID3D11DeviceContext +internal unsafe struct ID3D11DeviceContext { public readonly Vftbl* ThisPtr; @@ -24,6 +24,26 @@ internal unsafe readonly struct ID3D11DeviceContext } } + [SuppressMessage("", "SA1313")] + public HRESULT Map(ID3D11Resource* pResource, uint Subresource, D3D11_MAP MapType, uint MapFlags, [MaybeNull] out D3D11_MAPPED_SUBRESOURCE mappedResource) + { + fixed (D3D11_MAPPED_SUBRESOURCE* pMappedResource = &mappedResource) + { + return ThisPtr->Map((ID3D11DeviceContext*)Unsafe.AsPointer(ref this), pResource, Subresource, MapType, MapFlags, pMappedResource); + } + } + + [SuppressMessage("", "SA1313")] + public void Unmap(ID3D11Resource* pResource, uint Subresource) + { + ThisPtr->Unmap((ID3D11DeviceContext*)Unsafe.AsPointer(ref this), pResource, Subresource); + } + + public void CopyResource(ID3D11Resource* pDstResource, ID3D11Resource* pSrcResource) + { + ThisPtr->CopyResource((ID3D11DeviceContext*)Unsafe.AsPointer(ref this), pDstResource, pSrcResource); + } + internal readonly struct Vftbl { internal readonly ID3D11DeviceChild.Vftbl ID3D11DeviceChildVftbl; diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Dxgi/IDXGISurface.cs b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Dxgi/IDXGISurface.cs index b427e958..184aeb1d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Dxgi/IDXGISurface.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/Graphics/Dxgi/IDXGISurface.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; namespace Snap.Hutao.Win32.Graphics.Dxgi; -internal unsafe readonly struct IDXGISurface +internal unsafe struct IDXGISurface { public readonly Vftbl* ThisPtr; @@ -20,6 +20,26 @@ internal unsafe readonly struct IDXGISurface } } + public HRESULT GetDevice(ref readonly Guid iid, out T* pDevice) + where T : unmanaged + { + fixed (Guid* riid = &iid) + { + fixed (T** ppDevice = &pDevice) + { + return ThisPtr->IDXGIDeviceSubObjectVftbl.GetDevice((IDXGIDeviceSubObject*)Unsafe.AsPointer(ref this), riid, (void**)ppDevice); + } + } + } + + public HRESULT GetDesc(out DXGI_SURFACE_DESC desc) + { + fixed (DXGI_SURFACE_DESC* pDesc = &desc) + { + return ThisPtr->GetDesc((IDXGISurface*)Unsafe.AsPointer(ref this), pDesc); + } + } + internal readonly struct Vftbl { internal readonly IDXGIDeviceSubObject.Vftbl IDXGIDeviceSubObjectVftbl; diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/System/Com/IUnknownMarshal.cs b/src/Snap.Hutao/Snap.Hutao/Win32/System/Com/IUnknownMarshal.cs index e0460636..a3aeb9f1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/System/Com/IUnknownMarshal.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/System/Com/IUnknownMarshal.cs @@ -7,14 +7,14 @@ namespace Snap.Hutao.Win32.System.Com; internal static class IUnknownMarshal { - public static unsafe HRESULT QueryInterface(void* pIUnknown, ref readonly Guid riid, out TInterface* pvObject) + public static unsafe HRESULT QueryInterface(void* pIUnknown, ref readonly Guid iid, out TInterface* pvObject) where TInterface : unmanaged { - fixed (Guid* riid2 = &riid) + fixed (Guid* riid = &iid) { fixed (TInterface** ppvObject = &pvObject) { - return ((IUnknown*)pIUnknown)->ThisPtr->QueryInterface((IUnknown*)pIUnknown, riid2, (void**)ppvObject); + return ((IUnknown*)pIUnknown)->ThisPtr->QueryInterface((IUnknown*)pIUnknown, riid, (void**)ppvObject); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IDirect3DDxgiInterfaceAccess.cs b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IDirect3DDxgiInterfaceAccess.cs new file mode 100644 index 00000000..322fd4c3 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IDirect3DDxgiInterfaceAccess.cs @@ -0,0 +1,16 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Win32.Foundation; +using System.Runtime.InteropServices; + +namespace Snap.Hutao.Win32.System.WinRT.Graphics.Capture; + +[ComImport] +[Guid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")] +[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +internal interface IDirect3DDxgiInterfaceAccess +{ + [PreserveSig] + unsafe HRESULT GetInterface(Guid* iid, void** p); +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IDirect3DDxgiInterfaceAccessExtension.cs b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IDirect3DDxgiInterfaceAccessExtension.cs new file mode 100644 index 00000000..64a52d44 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IDirect3DDxgiInterfaceAccessExtension.cs @@ -0,0 +1,21 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Win32.Foundation; + +namespace Snap.Hutao.Win32.System.WinRT.Graphics.Capture; + +internal static class IDirect3DDxgiInterfaceAccessExtension +{ + public static unsafe HRESULT GetInterface(this IDirect3DDxgiInterfaceAccess access, ref readonly Guid iid, out T* p) + where T : unmanaged + { + fixed (Guid* riid = &iid) + { + fixed (T** pp = &p) + { + return access.GetInterface(riid, (void**)pp); + } + } + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IGraphicsCaptureItemInterop.cs b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IGraphicsCaptureItemInterop.cs index 0221adff..77fe1f56 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IGraphicsCaptureItemInterop.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IGraphicsCaptureItemInterop.cs @@ -1,8 +1,10 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Microsoft.UI.Composition; using Snap.Hutao.Win32.Foundation; using Snap.Hutao.Win32.Graphics.Gdi; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Snap.Hutao.Win32.System.WinRT.Graphics.Capture; diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/GraphicsCaptureItemInteropExtension.cs b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IGraphicsCaptureItemInteropExtension.cs similarity index 95% rename from src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/GraphicsCaptureItemInteropExtension.cs rename to src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IGraphicsCaptureItemInteropExtension.cs index fa2d3ef2..1b68b1ce 100644 --- a/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/GraphicsCaptureItemInteropExtension.cs +++ b/src/Snap.Hutao/Snap.Hutao/Win32/System/WinRT/Graphics/Capture/IGraphicsCaptureItemInteropExtension.cs @@ -8,7 +8,7 @@ using Windows.Graphics.Capture; namespace Snap.Hutao.Win32.System.WinRT.Graphics.Capture; -internal static class GraphicsCaptureItemInteropExtension +internal static class IGraphicsCaptureItemInteropExtension { public static unsafe HRESULT CreateForWindow(this IGraphicsCaptureItemInterop interop, HWND window, out GraphicsCaptureItem result) {