mirror of
https://github.com/Blinue/Magpie.git
synced 2026-06-24 02:04:10 +00:00
feat: 实现 WGC (p3)
This commit is contained in:
parent
aa0fa24377
commit
74650af46f
5 changed files with 137 additions and 72 deletions
|
|
@ -101,7 +101,7 @@ bool GraphicsCaptureFrameSource2::Initialize(
|
|||
) noexcept {
|
||||
assert(hMonSrc);
|
||||
|
||||
_device = device;
|
||||
_renderingDevice = device;
|
||||
_isUsingScRGB = useScRGB;
|
||||
|
||||
if (!winrt::GraphicsCaptureSession::IsSupported()) {
|
||||
|
|
@ -109,10 +109,37 @@ bool GraphicsCaptureFrameSource2::Initialize(
|
|||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
const SrcTracker& srcTracker = ScalingWindow::Get().SrcTracker();
|
||||
const HWND hwndSrc = srcTracker.Handle();
|
||||
const RECT& srcRect = srcTracker.SrcRect();
|
||||
|
||||
RECT frameBounds;
|
||||
if (!CalcWindowCapturedFrameBounds(hwndSrc, frameBounds)) {
|
||||
Logger::Get().Error("CalcWindowCapturedFrameBounds 失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (srcRect.left < frameBounds.left || srcRect.top < frameBounds.top) {
|
||||
Logger::Get().Error("裁剪边框错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 在源窗口存在 DPI 缩放时有时会有一像素的偏移(取决于窗口在屏幕上的位置)
|
||||
// 可能是 DwmGetWindowAttribute 的 bug
|
||||
_frameBox = {
|
||||
UINT(srcRect.left - frameBounds.left),
|
||||
UINT(srcRect.top - frameBounds.top),
|
||||
0,
|
||||
UINT(srcRect.right - frameBounds.left),
|
||||
UINT(srcRect.bottom - frameBounds.top),
|
||||
1
|
||||
};
|
||||
}
|
||||
|
||||
// 查找源窗口所在屏幕连接的适配器
|
||||
winrt::com_ptr<IDXGIAdapter1> srcMonAdapter = FindAdapterOfMonitor(dxgiFactory, hMonSrc);
|
||||
|
||||
bool isSameAdapter = false;
|
||||
if (srcMonAdapter) {
|
||||
DXGI_ADAPTER_DESC desc;
|
||||
HRESULT hr = srcMonAdapter->GetDesc(&desc);
|
||||
|
|
@ -123,12 +150,19 @@ bool GraphicsCaptureFrameSource2::Initialize(
|
|||
|
||||
const LUID renderAdapterLUID = device->GetAdapterLuid();
|
||||
|
||||
isSameAdapter = desc.AdapterLuid.HighPart == renderAdapterLUID.HighPart &&
|
||||
desc.AdapterLuid.LowPart == renderAdapterLUID.LowPart;
|
||||
if (desc.AdapterLuid.HighPart != renderAdapterLUID.HighPart ||
|
||||
desc.AdapterLuid.LowPart != renderAdapterLUID.LowPart)
|
||||
{
|
||||
// 通过 D3D12 跨适配器共享机制共享捕获图像
|
||||
if (!_CreateBridgeDeviceResources(dxgiAdapter)) {
|
||||
// 失败则使用渲染设备捕获,WGC 内部使用内存中转
|
||||
srcMonAdapter.copy_from(dxgiAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// 找不到源窗口所在屏幕的适配器则使用渲染设备
|
||||
srcMonAdapter.copy_from(dxgiAdapter);
|
||||
isSameAdapter = true;
|
||||
}
|
||||
|
||||
if (!_CreateD3D11Device(dxgiAdapter)) {
|
||||
|
|
@ -158,32 +192,6 @@ bool GraphicsCaptureFrameSource2::Initialize(
|
|||
return false;
|
||||
}
|
||||
|
||||
const SrcTracker& srcTracker = ScalingWindow::Get().SrcTracker();
|
||||
const HWND hwndSrc = srcTracker.Handle();
|
||||
const RECT& srcRect = srcTracker.SrcRect();
|
||||
|
||||
RECT frameBounds;
|
||||
if (!CalcWindowCapturedFrameBounds(hwndSrc, frameBounds)) {
|
||||
Logger::Get().Error("CalcWindowCapturedFrameBounds 失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (srcRect.left < frameBounds.left || srcRect.top < frameBounds.top) {
|
||||
Logger::Get().Error("裁剪边框错误");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 在源窗口存在 DPI 缩放时有时会有一像素的偏移(取决于窗口在屏幕上的位置)
|
||||
// 可能是 DwmGetWindowAttribute 的 bug
|
||||
_frameBox = {
|
||||
UINT(srcRect.left - frameBounds.left),
|
||||
UINT(srcRect.top - frameBounds.top),
|
||||
0,
|
||||
UINT(srcRect.right - frameBounds.left),
|
||||
UINT(srcRect.bottom - frameBounds.top),
|
||||
1
|
||||
};
|
||||
|
||||
hr = interop->CreateForWindow(
|
||||
ScalingWindow::Get().SrcTracker().Handle(),
|
||||
winrt::guid_of<winrt::GraphicsCaptureItem>(),
|
||||
|
|
@ -210,7 +218,7 @@ bool GraphicsCaptureFrameSource2::Initialize(
|
|||
1, 1, 1, 0,
|
||||
D3D12_RESOURCE_FLAG_NONE
|
||||
);
|
||||
hr = _device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE,
|
||||
hr = _renderingDevice->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE,
|
||||
&texDesc, D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, nullptr, IID_PPV_ARGS(&_output));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommittedResource 失败", hr);
|
||||
|
|
@ -283,12 +291,12 @@ bool GraphicsCaptureFrameSource2::Update(ID3D12GraphicsCommandList* commandList,
|
|||
auto lk = _newFrameLock.lock_exclusive();
|
||||
if (currentFrame.frame != _lastestFrame) {
|
||||
currentFrame.frame = _lastestFrame;
|
||||
currentFrame.d3d12Resource = nullptr;
|
||||
currentFrame.sharedResource = nullptr;
|
||||
_isNewFrameAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!currentFrame.d3d12Resource) {
|
||||
if (!currentFrame.sharedResource) {
|
||||
winrt::IDirect3DSurface d3dSurface = currentFrame.frame.Surface();
|
||||
|
||||
auto dxgiInterfaceAccess =
|
||||
|
|
@ -309,13 +317,22 @@ bool GraphicsCaptureFrameSource2::Update(ID3D12GraphicsCommandList* commandList,
|
|||
return false;
|
||||
}
|
||||
|
||||
hr = _device->OpenSharedHandle(hSharedResource.get(), IID_PPV_ARGS(¤tFrame.d3d12Resource));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("OpenSharedHandle 失败", hr);
|
||||
return false;
|
||||
if (_bridgeDevice) {
|
||||
hr = _bridgeDevice->OpenSharedHandle(hSharedResource.get(), IID_PPV_ARGS(¤tFrame.sharedResource));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("OpenSharedHandle 失败", hr);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
hr = _renderingDevice->OpenSharedHandle(hSharedResource.get(), IID_PPV_ARGS(¤tFrame.sharedResource));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("OpenSharedHandle 失败", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 共享纹理有 D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS 标志,因此无需屏障
|
||||
D3D12_RESOURCE_BARRIER barrier = {
|
||||
.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
|
||||
.Transition = {
|
||||
|
|
@ -326,7 +343,7 @@ bool GraphicsCaptureFrameSource2::Update(ID3D12GraphicsCommandList* commandList,
|
|||
};
|
||||
commandList->ResourceBarrier(1, &barrier);
|
||||
|
||||
CD3DX12_TEXTURE_COPY_LOCATION src(currentFrame.d3d12Resource.get(), 0);
|
||||
CD3DX12_TEXTURE_COPY_LOCATION src(currentFrame.sharedResource.get(), 0);
|
||||
CD3DX12_TEXTURE_COPY_LOCATION dest(_output.get(), 0);
|
||||
commandList->CopyTextureRegion(&dest, 0, 0, 0, &src, &_frameBox);
|
||||
|
||||
|
|
@ -420,6 +437,63 @@ bool GraphicsCaptureFrameSource2::_CreateD3D11Device(IDXGIAdapter1* dxgiAdapter)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GraphicsCaptureFrameSource2::_CreateBridgeDeviceResources(IDXGIAdapter1* dxgiAdapter) noexcept {
|
||||
HRESULT hr = D3D12CreateDevice(dxgiAdapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&_bridgeDevice));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("D3D12CreateDevice 失败", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger::Get().Info("已创建 D3D12 设备");
|
||||
|
||||
{
|
||||
// 只需要复制
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = { .Type = D3D12_COMMAND_LIST_TYPE_COPY };
|
||||
hr = _bridgeDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&_bridgeCommandQueue));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommandQueue 失败", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
hr = _bridgeDevice->CreateCommandList1(0, D3D12_COMMAND_LIST_TYPE_COPY,
|
||||
D3D12_COMMAND_LIST_FLAG_NONE, IID_PPV_ARGS(&_bridgeCommandList));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommandList1 失败", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t frameCount = ScalingWindow::Get().Options().maxProducerFramesInFlight;
|
||||
_bridgeCommandAllocators.resize(frameCount);
|
||||
for (winrt::com_ptr<ID3D12CommandAllocator>& commandAllocator : _bridgeCommandAllocators) {
|
||||
if (FAILED(_bridgeDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COPY, IID_PPV_ARGS(&commandAllocator)))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT);
|
||||
CD3DX12_RESOURCE_DESC texDesc = CD3DX12_RESOURCE_DESC::Tex2D(
|
||||
_isUsingScRGB ? DXGI_FORMAT_R16G16B16A16_FLOAT : DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
UINT64(_frameBox.right - _frameBox.left),
|
||||
_frameBox.bottom - _frameBox.top,
|
||||
1, 1, 1, 0,
|
||||
D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER,
|
||||
D3D12_TEXTURE_LAYOUT_ROW_MAJOR
|
||||
);
|
||||
|
||||
_bridgeResources.resize(frameCount);
|
||||
for (winrt::com_ptr<ID3D12Resource>& resouce : _bridgeResources) {
|
||||
hr = _renderingDevice->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_SHARED | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER,
|
||||
&texDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&resouce));
|
||||
if (FAILED(hr)) {
|
||||
Logger::Get().ComError("CreateCommittedResource 失败", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GraphicsCaptureFrameSource2::_Direct3D11CaptureFramePool_FrameArrived(
|
||||
const winrt::Direct3D11CaptureFramePool& pool,
|
||||
const winrt::IInspectable&
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ public:
|
|||
private:
|
||||
bool _CreateD3D11Device(IDXGIAdapter1* dxgiAdapter) noexcept;
|
||||
|
||||
bool _CreateBridgeDeviceResources(IDXGIAdapter1* dxgiAdapter) noexcept;
|
||||
|
||||
void _Direct3D11CaptureFramePool_FrameArrived(
|
||||
const winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool& pool,
|
||||
const winrt::IInspectable&
|
||||
|
|
@ -51,11 +53,18 @@ private:
|
|||
|
||||
void _StopCapture() noexcept;
|
||||
|
||||
ID3D12Device5* _device = nullptr;
|
||||
ID3D12Device5* _renderingDevice = nullptr;
|
||||
|
||||
winrt::com_ptr<ID3D11Device5> _d3d11Device;
|
||||
winrt::com_ptr<ID3D11DeviceContext4> _d3d11DC;
|
||||
|
||||
// 用于跨适配器捕获
|
||||
winrt::com_ptr<ID3D12Device5> _bridgeDevice;
|
||||
winrt::com_ptr<ID3D12CommandQueue> _bridgeCommandQueue;
|
||||
winrt::com_ptr<ID3D12GraphicsCommandList> _bridgeCommandList;
|
||||
std::vector<winrt::com_ptr<ID3D12CommandAllocator>> _bridgeCommandAllocators;
|
||||
std::vector<winrt::com_ptr<ID3D12Resource>> _bridgeResources;
|
||||
|
||||
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice _wrappedD3DDevice{ nullptr };
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem _captureItem{ nullptr };
|
||||
winrt::Windows::Graphics::Capture::GraphicsCaptureSession _captureSession{ nullptr };
|
||||
|
|
@ -69,7 +78,7 @@ private:
|
|||
|
||||
struct _CapturedFrame {
|
||||
winrt::Windows::Graphics::Capture::Direct3D11CaptureFrame frame{ nullptr };
|
||||
winrt::com_ptr<ID3D12Resource> d3d12Resource;
|
||||
winrt::com_ptr<ID3D12Resource> sharedResource;
|
||||
};
|
||||
std::vector<_CapturedFrame> _framesInUse;
|
||||
|
||||
|
|
|
|||
|
|
@ -110,10 +110,7 @@ ScalingError Renderer2::Initialize(HWND hwndAttach, OverlayOptions& /*overlayOpt
|
|||
_producerThread = std::thread(&Renderer2::_ProducerThreadProc, this);
|
||||
|
||||
{
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc{
|
||||
.Type = D3D12_COMMAND_LIST_TYPE_DIRECT,
|
||||
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE
|
||||
};
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = { .Type = D3D12_COMMAND_LIST_TYPE_DIRECT };
|
||||
if (winrt::com_ptr<ID3D12Device9> device9 = _device.try_as<ID3D12Device9>()) {
|
||||
// 设置 CreatorID 可以提高并发度
|
||||
HRESULT hr = device9->CreateCommandQueue1(&queueDesc, COSUMER_QUEUE_ID, IID_PPV_ARGS(&_consumerCommandQueue));
|
||||
|
|
@ -164,13 +161,13 @@ bool Renderer2::Render(bool /*force*/, bool /*waitForGpu*/, bool onHandlingDevic
|
|||
{
|
||||
auto lk = _frameBufferLock.lock_exclusive();
|
||||
|
||||
const uint64_t completedFenceValue = _producerFrameBufferFence->GetCompletedValue();
|
||||
if (completedFenceValue == 0) {
|
||||
// 第一帧尚未完成
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_curConsumeIndex != _curProduceIndex) {
|
||||
const uint64_t completedFenceValue = _producerFrameBufferFence->GetCompletedValue();
|
||||
if (completedFenceValue == 0) {
|
||||
// 第一帧尚未完成
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t frameBufferCount = (uint32_t)_frameBuffers.size();
|
||||
uint32_t nextConsumeIndex = (_curConsumeIndex + 1) % frameBufferCount;
|
||||
if (completedFenceValue >= _frameBuffers[nextConsumeIndex].producerFenceValue) {
|
||||
|
|
@ -185,6 +182,7 @@ bool Renderer2::Render(bool /*force*/, bool /*waitForGpu*/, bool onHandlingDevic
|
|||
|
||||
_curConsumeIndex = nextConsumeIndex;
|
||||
|
||||
// 窗口很小,但有发生的可能
|
||||
if (_curConsumeIndex == _curProduceIndex) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -728,10 +726,7 @@ bool Renderer2::_InitProducer() noexcept {
|
|||
}
|
||||
|
||||
{
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc{
|
||||
.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE,
|
||||
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE
|
||||
};
|
||||
D3D12_COMMAND_QUEUE_DESC queueDesc = { .Type = D3D12_COMMAND_LIST_TYPE_COMPUTE };
|
||||
if (winrt::com_ptr<ID3D12Device9> device9 = _device.try_as<ID3D12Device9>()) {
|
||||
// 设置 CreatorID 可以提高并发度
|
||||
HRESULT hr = device9->CreateCommandQueue1(&queueDesc, PRODUCER_QUEUE_ID, IID_PPV_ARGS(&_producerCommandQueue));
|
||||
|
|
@ -893,6 +888,8 @@ bool Renderer2::_InitProducer() noexcept {
|
|||
}
|
||||
|
||||
bool Renderer2::_ProducerRender() noexcept {
|
||||
_stepTimer.PrepareForRender();
|
||||
|
||||
ID3D12Resource* curBuffer;
|
||||
D3D12_RESOURCE_STATES bufferState;
|
||||
{
|
||||
|
|
@ -910,8 +907,6 @@ bool Renderer2::_ProducerRender() noexcept {
|
|||
curFrameBuffer.state = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
|
||||
}
|
||||
|
||||
_stepTimer.PrepareForRender();
|
||||
|
||||
HRESULT hr = _producerCommandAllocators[_curProducerFrameIndex]->Reset();
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -108,10 +108,6 @@ bool SwapChainPresenter::Initialize(
|
|||
return false;
|
||||
}
|
||||
|
||||
if (FAILED(_fenceEvent.create())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_frameBuffers.resize(bufferCount);
|
||||
_frameBufferFenceValues.resize(bufferCount);
|
||||
|
||||
|
|
@ -131,12 +127,10 @@ HRESULT SwapChainPresenter::BeginFrame(
|
|||
_frameLatencyWaitableObject.wait(1000);
|
||||
|
||||
if (_fence->GetCompletedValue() < _frameBufferFenceValues[_curBufferIndex]) {
|
||||
HRESULT hr = _fence->SetEventOnCompletion(_frameBufferFenceValues[_curBufferIndex], _fenceEvent.get());
|
||||
HRESULT hr = _fence->SetEventOnCompletion(_frameBufferFenceValues[_curBufferIndex], nullptr);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
_fenceEvent.wait();
|
||||
}
|
||||
|
||||
*frameTex = _frameBuffers[_curBufferIndex].get();
|
||||
|
|
@ -332,13 +326,7 @@ HRESULT SwapChainPresenter::_WaitForGpu() noexcept {
|
|||
return hr;
|
||||
}
|
||||
|
||||
hr = _fence->SetEventOnCompletion(_curFenceValue, _fenceEvent.get());
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
_fenceEvent.wait();
|
||||
return S_OK;
|
||||
return _fence->SetEventOnCompletion(_curFenceValue, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ private:
|
|||
|
||||
winrt::com_ptr<ID3D12Fence1> _fence;
|
||||
uint64_t _curFenceValue = 0;
|
||||
wil::unique_event_nothrow _fenceEvent;
|
||||
|
||||
bool _isTearingSupported = false;
|
||||
bool _isRecreated = true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue