[host] fixed support for high DPI desktops (4K+)

This commit is contained in:
Geoffrey McRae 2017-12-08 06:24:17 +11:00
parent 65ba649b36
commit a61293b551
8 changed files with 133 additions and 70 deletions

View File

@ -211,19 +211,6 @@ void DXGI::DeInitialize()
m_initialized = false; m_initialized = false;
} }
bool DXGI::ReInitialize()
{
DeInitialize();
/*
DXGI needs some time when mode switches occur, failing to do so causes
failure to start and exceptions internal to DXGI
*/
Sleep(200);
return Initialize(m_options);
}
FrameType DXGI::GetFrameType() FrameType DXGI::GetFrameType()
{ {
if (!m_initialized) if (!m_initialized)
@ -240,10 +227,10 @@ size_t DXGI::GetMaxFrameSize()
return (m_width * m_height * 4); return (m_width * m_height * 4);
} }
bool DXGI::GrabFrame(FrameInfo & frame) GrabStatus DXGI::GrabFrame(FrameInfo & frame)
{ {
if (!m_initialized) if (!m_initialized)
return false; return GRAB_STATUS_ERROR;
DXGI_OUTDUPL_FRAME_INFO frameInfo; DXGI_OUTDUPL_FRAME_INFO frameInfo;
CComPtr<IDXGIResource> res; CComPtr<IDXGIResource> res;
@ -258,6 +245,8 @@ bool DXGI::GrabFrame(FrameInfo & frame)
switch (status) switch (status)
{ {
case DXGI_ERROR_ACCESS_LOST: // desktop switch, mode change or switch DWM on or off case DXGI_ERROR_ACCESS_LOST: // desktop switch, mode change or switch DWM on or off
return GRAB_STATUS_REINIT;
case WAIT_ABANDONED: // this can happen also during desktop switches, not documented by MS though case WAIT_ABANDONED: // this can happen also during desktop switches, not documented by MS though
{ {
// see if we can open the desktop, if not the secure desktop // see if we can open the desktop, if not the secure desktop
@ -275,7 +264,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
if (!ReInitialize()) if (!ReInitialize())
{ {
DEBUG_ERROR("Failed to ReInitialize after lost access to desktop"); DEBUG_ERROR("Failed to ReInitialize after lost access to desktop");
return false; return GRAB_STATUS_ERROR;
} }
continue; continue;
} }
@ -283,7 +272,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
default: default:
// unknown failure // unknown failure
DEBUG_INFO("AcquireNextFrame failed: %08x", status); DEBUG_INFO("AcquireNextFrame failed: %08x", status);
return false; return GRAB_STATUS_ERROR;
} }
} }
@ -292,7 +281,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
{ {
m_dup->ReleaseFrame(); m_dup->ReleaseFrame();
DEBUG_ERROR("Failed to acquire next frame"); DEBUG_ERROR("Failed to acquire next frame");
return false; return GRAB_STATUS_ERROR;
} }
CComQIPtr<ID3D11Texture2D> src = res; CComQIPtr<ID3D11Texture2D> src = res;
@ -300,7 +289,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
{ {
m_dup->ReleaseFrame(); m_dup->ReleaseFrame();
DEBUG_ERROR("Failed to get src ID3D11Texture2D"); DEBUG_ERROR("Failed to get src ID3D11Texture2D");
return false; return GRAB_STATUS_ERROR;
} }
D3D11_TEXTURE2D_DESC desc; D3D11_TEXTURE2D_DESC desc;
@ -324,7 +313,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
{ {
m_dup->ReleaseFrame(); m_dup->ReleaseFrame();
DEBUG_ERROR("Failed to get the new pointer shape: %08x", status); DEBUG_ERROR("Failed to get the new pointer shape: %08x", status);
return false; return GRAB_STATUS_ERROR;
} }
} }
@ -336,7 +325,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
if (!surface) if (!surface)
{ {
DEBUG_ERROR("Failed to get IDXGISurface1"); DEBUG_ERROR("Failed to get IDXGISurface1");
return false; return GRAB_STATUS_ERROR;
} }
DXGI_MAPPED_RECT rect; DXGI_MAPPED_RECT rect;
@ -344,7 +333,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_ERROR("Failed to map surface: %08x", status); DEBUG_ERROR("Failed to map surface: %08x", status);
return false; return GRAB_STATUS_ERROR;
} }
m_width = desc.Width; m_width = desc.Width;
@ -381,7 +370,7 @@ bool DXGI::GrabFrame(FrameInfo & frame)
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : type = CURSOR_TYPE_MONOCHROME ; break; case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : type = CURSOR_TYPE_MONOCHROME ; break;
default: default:
DEBUG_ERROR("Invalid cursor type"); DEBUG_ERROR("Invalid cursor type");
return false; return GRAB_STATUS_ERROR;
} }
POINT cursorPos; POINT cursorPos;
@ -404,8 +393,8 @@ bool DXGI::GrabFrame(FrameInfo & frame)
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_ERROR("Failed to unmap surface: %08x", status); DEBUG_ERROR("Failed to unmap surface: %08x", status);
return false; return GRAB_STATUS_ERROR;
} }
return true; return GRAB_STATUS_OK;
} }

View File

@ -38,16 +38,25 @@ namespace Capture
bool Initialize(CaptureOptions * options); bool Initialize(CaptureOptions * options);
void DeInitialize(); void DeInitialize();
bool ReInitialize()
{
DeInitialize();
/*
DXGI needs some time when mode switches occur, failing to do so causes
failure to start and exceptions internal to DXGI
*/
Sleep(200);
return Initialize(m_options);
}
enum FrameType GetFrameType(); enum FrameType GetFrameType();
enum FrameComp GetFrameCompression(); enum FrameComp GetFrameCompression();
size_t GetMaxFrameSize(); size_t GetMaxFrameSize();
bool GrabFrame(struct FrameInfo & frame); enum GrabStatus GrabFrame(struct FrameInfo & frame);
private: private:
CaptureOptions * m_options; CaptureOptions * m_options;
bool ReInitialize();
bool m_initialized; bool m_initialized;
unsigned int m_width; unsigned int m_width;
unsigned int m_height; unsigned int m_height;

View File

@ -50,7 +50,7 @@ bool NvFBC::Initialize(CaptureOptions * options)
if (m_initialized) if (m_initialized)
DeInitialize(); DeInitialize();
m_options = options; m_options = options;
m_optNoCrop = false; m_optNoCrop = false;
for (CaptureOptions::const_iterator it = options->cbegin(); it != options->cend(); ++it) for (CaptureOptions::const_iterator it = options->cbegin(); it != options->cend(); ++it)
{ {
@ -212,12 +212,14 @@ size_t NvFBC::GetMaxFrameSize()
return m_maxCaptureWidth * m_maxCaptureHeight * 4; return m_maxCaptureWidth * m_maxCaptureHeight * 4;
} }
bool NvFBC::GrabFrame(struct FrameInfo & frame) enum GrabStatus NvFBC::GrabFrame(struct FrameInfo & frame)
{ {
if (!m_initialized) if (!m_initialized)
return false; return GRAB_STATUS_ERROR;
const HWND hDesktop = GetDesktopWindow(); const HWND hDesktop = GetDesktopWindow();
const unsigned int screenWidth = GetSystemMetrics(SM_CXSCREEN);
const unsigned int screenHeight = GetSystemMetrics(SM_CYSCREEN);
RECT desktop; RECT desktop;
GetWindowRect(hDesktop, &desktop); GetWindowRect(hDesktop, &desktop);
@ -249,22 +251,22 @@ bool NvFBC::GrabFrame(struct FrameInfo & frame)
if (m_optNoCrop) if (m_optNoCrop)
{ {
dataWidth = m_grabInfo.dwWidth * 4; dataWidth = m_grabInfo.dwWidth * 4;
dataOffset = 0; dataOffset = 0;
frame.width = m_grabInfo.dwWidth; frame.width = m_grabInfo.dwWidth;
frame.height = m_grabInfo.dwHeight; frame.height = m_grabInfo.dwHeight;
} }
else else
{ {
const unsigned int realHeight = min(m_grabInfo.dwHeight, (unsigned int)(desktop.bottom - desktop.top)); const unsigned int realHeight = min(m_grabInfo.dwHeight, screenHeight);
const unsigned int realWidth = min(m_grabInfo.dwWidth , (unsigned int)(desktop.right - desktop.left)); const unsigned int realWidth = min(m_grabInfo.dwWidth, screenWidth);
dataWidth = realWidth * 4; dataWidth = realWidth * 4;
dataOffset = dataOffset =
(((m_grabInfo.dwHeight - realHeight) >> 1) * m_grabInfo.dwBufferWidth + (((m_grabInfo.dwHeight - realHeight) >> 1) * m_grabInfo.dwBufferWidth +
((m_grabInfo.dwWidth - realWidth ) >> 1)) * 4; ((m_grabInfo.dwWidth - realWidth) >> 1)) * 4;
frame.width = realWidth; frame.width = realWidth;
frame.height = realHeight; frame.height = realHeight;
} }
@ -276,27 +278,22 @@ bool NvFBC::GrabFrame(struct FrameInfo & frame)
for(unsigned int y = 0; y < frame.height; ++y, dst += dataWidth, src += m_grabInfo.dwBufferWidth * 4) for(unsigned int y = 0; y < frame.height; ++y, dst += dataWidth, src += m_grabInfo.dwBufferWidth * 4)
memcpySSE(dst, src, dataWidth); memcpySSE(dst, src, dataWidth);
return true; return GRAB_STATUS_OK;
} }
if (status == NVFBC_ERROR_DYNAMIC_DISABLE) if (status == NVFBC_ERROR_DYNAMIC_DISABLE)
{ {
DEBUG_ERROR("NvFBC was disabled by someone else"); DEBUG_ERROR("NvFBC was disabled by someone else");
return false; return GRAB_STATUS_ERROR;
} }
if (status == NVFBC_ERROR_INVALIDATED_SESSION) if (status == NVFBC_ERROR_INVALIDATED_SESSION)
{ {
DEBUG_WARN("Session was invalidated, attempting to restart"); DEBUG_WARN("Session was invalidated, attempting to restart");
DeInitialize(); return GRAB_STATUS_REINIT;
if (!Initialize(m_options))
{
DEBUG_ERROR("Failed to re-iniaialize");
return false;
}
} }
} }
DEBUG_ERROR("Failed to grab frame"); DEBUG_ERROR("Failed to grab frame");
return false; return GRAB_STATUS_ERROR;
} }

View File

@ -37,10 +37,15 @@ namespace Capture
bool Initialize(CaptureOptions * options); bool Initialize(CaptureOptions * options);
void DeInitialize(); void DeInitialize();
bool ReInitialize()
{
DeInitialize();
return Initialize(m_options);
}
enum FrameType GetFrameType(); enum FrameType GetFrameType();
enum FrameComp GetFrameCompression(); enum FrameComp GetFrameCompression();
size_t GetMaxFrameSize(); size_t GetMaxFrameSize();
bool GrabFrame(struct FrameInfo & frame); enum GrabStatus GrabFrame(struct FrameInfo & frame);
private: private:
CaptureOptions * m_options; CaptureOptions * m_options;

View File

@ -34,6 +34,13 @@ struct FrameInfo
int mouseX, mouseY; int mouseX, mouseY;
}; };
enum GrabStatus
{
GRAB_STATUS_OK,
GRAB_STATUS_REINIT,
GRAB_STATUS_ERROR
};
typedef std::vector<const char *> CaptureOptions; typedef std::vector<const char *> CaptureOptions;
__interface ICapture __interface ICapture
@ -43,7 +50,8 @@ public:
bool Initialize(CaptureOptions * options); bool Initialize(CaptureOptions * options);
void DeInitialize(); void DeInitialize();
bool ReInitialize();
enum FrameType GetFrameType(); enum FrameType GetFrameType();
size_t GetMaxFrameSize(); size_t GetMaxFrameSize();
bool GrabFrame(struct FrameInfo & frame); enum GrabStatus GrabFrame(struct FrameInfo & frame);
}; };

View File

@ -28,6 +28,7 @@ Service * Service::m_instance = NULL;
Service::Service() : Service::Service() :
m_initialized(false), m_initialized(false),
m_memory(NULL),
m_readyEvent(INVALID_HANDLE_VALUE), m_readyEvent(INVALID_HANDLE_VALUE),
m_capture(NULL), m_capture(NULL),
m_header(NULL), m_header(NULL),
@ -60,14 +61,17 @@ bool Service::Initialize(ICapture * captureDevice)
return false; return false;
} }
uint8_t * memory = static_cast<uint8_t*>(m_ivshmem->GetMemory()); m_memory = static_cast<uint8_t*>(m_ivshmem->GetMemory());
if (!memory) if (!m_memory)
{ {
DEBUG_ERROR("Failed to get IVSHMEM memory"); DEBUG_ERROR("Failed to get IVSHMEM memory");
DeInitialize(); DeInitialize();
return false; return false;
} }
if (!InitPointers())
return false;
m_readyEvent = m_ivshmem->CreateVectorEvent(0); m_readyEvent = m_ivshmem->CreateVectorEvent(0);
if (m_readyEvent == INVALID_HANDLE_VALUE) if (m_readyEvent == INVALID_HANDLE_VALUE)
{ {
@ -76,20 +80,6 @@ bool Service::Initialize(ICapture * captureDevice)
return false; return false;
} }
m_header = reinterpret_cast<KVMFRHeader *>(memory);
m_frame[0] = (uint8_t *)(((uintptr_t)memory + sizeof(KVMFRHeader *) + 0x7F) & ~0x7F);
m_frameSize = ((m_ivshmem->GetSize() - (m_frame[0] - memory)) & ~0x7F) >> 1;
m_frame[1] = m_frame[0] + m_frameSize;
m_dataOffset[0] = m_frame[0] - memory;
m_dataOffset[1] = m_frame[1] - memory;
if (m_capture->GetMaxFrameSize() > m_frameSize)
{
DEBUG_ERROR("Frame can exceed buffer size!");
DeInitialize();
return false;
}
// we save this as it might actually be valid // we save this as it might actually be valid
UINT16 hostID = m_header->hostID; UINT16 hostID = m_header->hostID;
@ -105,10 +95,32 @@ bool Service::Initialize(ICapture * captureDevice)
return true; return true;
} }
bool Service::InitPointers()
{
m_header = reinterpret_cast<KVMFRHeader *>(m_memory);
m_frame[0] = (uint8_t *)(((uintptr_t)m_memory + sizeof(KVMFRHeader *) + 0x7F) & ~0x7F);
m_frameSize = ((m_ivshmem->GetSize() - (m_frame[0] - m_memory)) & ~0x7F) >> 1;
m_frame[1] = m_frame[0] + m_frameSize;
m_dataOffset[0] = m_frame[0] - m_memory;
m_dataOffset[1] = m_frame[1] - m_memory;
if (m_capture->GetMaxFrameSize() > m_frameSize)
{
DEBUG_ERROR("Frame can exceed buffer size!");
DeInitialize();
return false;
}
return true;
}
void Service::DeInitialize() void Service::DeInitialize()
{ {
if (m_readyEvent != INVALID_HANDLE_VALUE) if (m_readyEvent != INVALID_HANDLE_VALUE)
{
CloseHandle(m_readyEvent); CloseHandle(m_readyEvent);
m_readyEvent = INVALID_HANDLE_VALUE;
}
m_header = NULL; m_header = NULL;
m_frame[0] = NULL; m_frame[0] = NULL;
@ -124,6 +136,7 @@ void Service::DeInitialize()
m_capture = NULL; m_capture = NULL;
} }
m_memory = NULL;
m_initialized = false; m_initialized = false;
} }
@ -165,11 +178,38 @@ bool Service::Process()
} }
ResetEvent(m_readyEvent); ResetEvent(m_readyEvent);
// capture a frame of data bool ok = false;
if (!m_capture->GrabFrame(frame)) for(int i = 0; i < 2; ++i)
{ {
m_header->dataLen = 0; // capture a frame of data
DEBUG_ERROR("Capture failed"); switch (m_capture->GrabFrame(frame))
{
case GRAB_STATUS_OK:
ok = true;
break;
case GRAB_STATUS_ERROR:
m_header->dataLen = 0;
DEBUG_ERROR("Capture failed");
return false;
case GRAB_STATUS_REINIT:
DEBUG_INFO("ReInitialize Requested");
if (!m_capture->ReInitialize() || !InitPointers())
{
DEBUG_ERROR("ReInitialize Failed");
return false;
}
continue;
}
if (ok)
break;
}
if (!ok)
{
DEBUG_ERROR("Capture retry count exceeded");
return false; return false;
} }
@ -190,7 +230,7 @@ bool Service::Process()
else else
{ {
POINT cursorPos; POINT cursorPos;
GetCursorPos(&cursorPos); GetPhysicalCursorPos(&cursorPos);
m_header->mouseX = cursorPos.x; m_header->mouseX = cursorPos.x;
m_header->mouseY = cursorPos.y; m_header->mouseY = cursorPos.y;
} }

View File

@ -40,12 +40,15 @@ public:
bool Process(); bool Process();
private: private:
bool InitPointers();
static Service * m_instance; static Service * m_instance;
Service(); Service();
~Service(); ~Service();
bool m_initialized; bool m_initialized;
uint8_t * m_memory;
IVSHMEM * m_ivshmem; IVSHMEM * m_ivshmem;
HANDLE m_readyEvent; HANDLE m_readyEvent;
ICapture * m_capture; ICapture * m_capture;

View File

@ -100,6 +100,9 @@
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
@ -117,6 +120,9 @@
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
@ -138,6 +144,9 @@
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
@ -159,6 +168,9 @@
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;setupapi.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link> </Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\vendor\getopt\getopt.c" /> <ClCompile Include="..\vendor\getopt\getopt.c" />