capture test

This commit is contained in:
DismissedLight
2024-05-05 15:41:46 +08:00
parent f9c18d2555
commit 3e9edd2f62
10 changed files with 204 additions and 24 deletions

View File

@@ -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<IDirect3DDevice>(WinRT.IInspectable.FromAbi((nint)inspectable));
IDirect3DDevice direct3DDevice = WinRT.IInspectable.FromAbi((nint)inspectable).ObjRef.AsInterface<IDirect3DDevice>();
SizeInt32 size = new(1920, 1080);
using (Direct3D11CaptureFramePool framePool = Direct3D11CaptureFramePool.CreateFreeThreaded(direct3DDevice, DirectXPixelFormat.B8G8R8A8UIntNormalized, 2, size))
HWND hwnd = serviceProvider.GetRequiredService<ICurrentWindowReference>().GetWindowHandle();
GraphicsCaptureItem.As<IGraphicsCaptureItemInterop>().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<IDirect3DDxgiInterfaceAccess>();
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<D3D11_SUBRESOURCE_DATA>(), 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<IMemoryBufferByteAccess>().GetBuffer(out Span<byte> 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<ICurrentWindowReference>().GetWindowHandle();
GraphicsCaptureItem.As<IGraphicsCaptureItemInterop>().CreateForWindow(hwnd, out GraphicsCaptureItem item);
using (GraphicsCaptureSession captureSession = framePool.CreateCaptureSession(item))
{
captureSession.IsCursorCaptureEnabled = false;

View File

@@ -3,6 +3,7 @@
namespace Snap.Hutao.Win32.Graphics.Direct3D11;
[SuppressMessage("", "SA1307")]
internal struct D3D11_MAPPED_SUBRESOURCE
{
public unsafe void* pData;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<T>(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;

View File

@@ -7,14 +7,14 @@ namespace Snap.Hutao.Win32.System.Com;
internal static class IUnknownMarshal
{
public static unsafe HRESULT QueryInterface<TInterface>(void* pIUnknown, ref readonly Guid riid, out TInterface* pvObject)
public static unsafe HRESULT QueryInterface<TInterface>(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);
}
}
}

View File

@@ -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);
}

View File

@@ -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<T>(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);
}
}
}
}

View File

@@ -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;

View File

@@ -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)
{