feat: 实现 WGC (p3)

This commit is contained in:
Xu 2025-11-24 21:24:45 +08:00
commit 74650af46f
5 changed files with 137 additions and 72 deletions

View file

@ -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(&currentFrame.d3d12Resource));
if (FAILED(hr)) {
Logger::Get().ComError("OpenSharedHandle 失败", hr);
return false;
if (_bridgeDevice) {
hr = _bridgeDevice->OpenSharedHandle(hSharedResource.get(), IID_PPV_ARGS(&currentFrame.sharedResource));
if (FAILED(hr)) {
Logger::Get().ComError("OpenSharedHandle 失败", hr);
return false;
}
} else {
hr = _renderingDevice->OpenSharedHandle(hSharedResource.get(), IID_PPV_ARGS(&currentFrame.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&

View file

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

View file

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

View file

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

View file

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