From f90c258c34932e9be2b89e2ebf0d3542b1923873 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Fri, 3 Nov 2017 22:20:48 +1100 Subject: [PATCH] [host] initial rewrite of DXGI capture --- host/Capture/DXGI.cpp | 244 +++++++++++++++++++++++++++++++++++++----- host/Capture/DXGI.h | 15 ++- 2 files changed, 230 insertions(+), 29 deletions(-) diff --git a/host/Capture/DXGI.cpp b/host/Capture/DXGI.cpp index 3b1b3b2a..aa31dd06 100644 --- a/host/Capture/DXGI.cpp +++ b/host/Capture/DXGI.cpp @@ -23,7 +23,11 @@ using namespace Capture; DXGI::DXGI() : m_initialized(false), - m_manager(NULL) + m_dxgiFactory(NULL), + m_device(NULL), + m_deviceContext(NULL), + m_dup(NULL), + m_texture(NULL) { } @@ -37,31 +41,158 @@ bool DXGI::Initialize() if (m_initialized) DeInitialize(); - if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) + HRESULT status; + + status = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)(&m_dxgiFactory)); + if (FAILED(status)) { - DEBUG_ERROR("CoInitializeEx failed"); + DEBUG_ERROR("Failed to create DXGIFactory: %08x", status); return false; } - m_manager = new DXGIManager(); - m_manager->SetCaptureSource(CSDesktop); + bool done = false; + CComPtr adapter; + for (int i = 0; m_dxgiFactory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++) + { + CComPtr output; + for (int i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; i++) + { + DXGI_OUTPUT_DESC outputDesc; + output->GetDesc(&outputDesc); + if (!outputDesc.AttachedToDesktop) + { + output.Release(); + continue; + } - RECT rect; - m_manager->GetOutputRect(rect); - m_width = rect.right - rect.left; - m_height = rect.bottom - rect.top; + m_output = output; + if (!m_output) + { + output.Release(); + adapter.Release(); + DEBUG_ERROR("Failed to get IDXGIOutput1"); + DeInitialize(); + return false; + } + m_width = outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left; + m_height = outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top; + + output.Release(); + done = true; + break; + } + + if (done) + break; + + adapter.Release(); + } + + if (!done) + { + DEBUG_ERROR("Failed to locate a valid output device"); + DeInitialize(); + return false; + } + + static const D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + + status = D3D11CreateDevice( + adapter, + D3D_DRIVER_TYPE_UNKNOWN, + NULL, + D3D11_CREATE_DEVICE_DEBUG, + featureLevels, ARRAYSIZE(featureLevels), + D3D11_SDK_VERSION, + &m_device, + &m_featureLevel, + &m_deviceContext + ); + adapter.Release(); + + if (FAILED(status)) + { + DEBUG_ERROR("Failed to create D3D11 device"); + DeInitialize(); + return false; + } + + CComQIPtr device1 = m_device; + if (!device1) + { + DEBUG_ERROR("Failed to get IDXGIDevice1"); + DeInitialize(); + return false; + } + + status = m_output->DuplicateOutput(m_device, &m_dup); + if (FAILED(status)) + { + DEBUG_ERROR("DuplicateOutput Failed: %08x", status); + DeInitialize(); + return false; + } + + D3D11_TEXTURE2D_DESC texDesc; + ZeroMemory(&texDesc, sizeof(texDesc)); + texDesc.Width = m_width; + texDesc.Height = m_height; + texDesc.MipLevels = 1; + texDesc.ArraySize = 1; + texDesc.SampleDesc.Count = 1; + texDesc.SampleDesc.Quality = 0; + texDesc.Usage = D3D11_USAGE_STAGING; + texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + texDesc.BindFlags = 0; + texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + texDesc.MiscFlags = 0; + + status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture); + if (FAILED(status)) + { + DEBUG_ERROR("Failed to create texture: %08x", status); + DeInitialize(); + return false; + } + + if (!m_memcpy.Initialize()) + { + DEBUG_ERROR("Failed to initialize memcpy"); + return false; + } + m_initialized = true; return true; } void DXGI::DeInitialize() { - if (m_manager) - { - delete m_manager; - m_manager = NULL; - } + m_memcpy.DeInitialize(); + + if (m_texture) + m_texture.Release(); + + if (m_dup) + m_dup.Release(); + + if (m_output) + m_output.Release(); + + if (m_deviceContext) + m_deviceContext.Release(); + + if (m_device) + m_device.Release(); + + if (m_dxgiFactory) + m_dxgiFactory.Release(); m_initialized = false; } @@ -84,7 +215,7 @@ FrameComp DXGI::GetFrameCompression() size_t DXGI::GetMaxFrameSize() { - if (!m_initialized); + if (!m_initialized) return 0; return m_width * m_height * 4; @@ -92,26 +223,85 @@ size_t DXGI::GetMaxFrameSize() bool DXGI::GrabFrame(FrameInfo & frame) { - RECT rect; - m_manager->GetOutputRect(rect); - m_width = rect.right - rect.left; - m_height = rect.bottom - rect.top; + DXGI_OUTDUPL_FRAME_INFO frameInfo; + CComPtr res; - HRESULT result; + HRESULT status; for(int i = 0; i < 2; ++i) { - result = m_manager->GetOutputBits((BYTE*)frame.buffer, rect); - if (SUCCEEDED(result)) + status = m_dup->AcquireNextFrame(INFINITE, &frameInfo, &res); + if (SUCCEEDED(status)) break; + + // desktop switch, mode change or switch DWM on or off + if (status == DXGI_ERROR_ACCESS_LOST) + { + DeInitialize(); + if (!Initialize()) + { + DEBUG_ERROR("Failed to re-initialize after access was lost"); + return false; + } + continue; + } + + // unknown failure + DEBUG_INFO("AcquireNextFrame failed: %08x", status); + return false; } - if (FAILED(result)) + // retry count exceeded + if (FAILED(status)) + { + DEBUG_ERROR("Failed to acquire next frame"); return false; + } + CComQIPtr src = res; + if (!src) + { + DEBUG_ERROR("Failed to get src ID3D11Texture2D"); + return false; + } + + D3D11_TEXTURE2D_DESC desc; + src->GetDesc(&desc); + + m_deviceContext->CopyResource(m_texture, src); + m_dup->ReleaseFrame(); + res.Release(); + src.Release(); + + CComQIPtr surface = m_texture; + if (!surface) + { + DEBUG_ERROR("Failed to get IDXGISurface1"); + return false; + } + + DXGI_MAPPED_RECT rect; + status = surface->Map(&rect, DXGI_MAP_READ); + if (FAILED(status)) + { + DEBUG_ERROR("Failed to map surface: %08x", status); + return false; + } + + m_width = desc.Width; + m_height = desc.Height; + + frame.width = desc.Width; + frame.height = desc.Height; + frame.stride = rect.Pitch / 4; + frame.outSize = min(frame.bufferSize, m_height * rect.Pitch); + m_memcpy.Copy(frame.buffer, rect.pBits, frame.outSize); + + status = surface->Unmap(); + if (FAILED(status)) + { + DEBUG_ERROR("Failed to unmap surface: %08x", status); + return false; + } - frame.width = m_width; - frame.height = m_height; - frame.stride = m_width; - frame.outSize = m_width * m_height * 4; return true; } \ No newline at end of file diff --git a/host/Capture/DXGI.h b/host/Capture/DXGI.h index 782e62da..29eb1f6f 100644 --- a/host/Capture/DXGI.h +++ b/host/Capture/DXGI.h @@ -19,10 +19,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA #pragma once #include "ICapture.h" -#include +#include "MTMemcpy.h" #define W32_LEAN_AND_MEAN #include +#include +#include +#include namespace Capture { @@ -40,8 +43,16 @@ namespace Capture private: bool m_initialized; - DXGIManager * m_manager; + MTMemcpy m_memcpy; unsigned int m_width; unsigned int m_height; + + CComPtr m_dxgiFactory; + CComPtr m_device; + D3D_FEATURE_LEVEL m_featureLevel; + CComPtr m_deviceContext; + CComQIPtr m_output; + CComPtr m_dup; + CComPtr m_texture; }; }; \ No newline at end of file