[c-host] renamed finall to just plain host

This commit is contained in:
Geoffrey McRae
2020-05-25 13:42:43 +10:00
parent d579705b10
commit bc7871f630
34 changed files with 2 additions and 2 deletions

View File

@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.0)
project(capture LANGUAGES C)
include(PreCapture)
option(USE_NVFBC "Enable NVFBC Support" OFF)
option(USE_DXGI "Enable DXGI Support" ON)
if(NOT DEFINED NVFBC_SDK)
set(NVFBC_SDK "C:/Program Files (x86)/NVIDIA Corporation/NVIDIA Capture SDK")
endif()
file(TO_CMAKE_PATH "${NVFBC_SDK}" nvfbc_sdk)
if(NOT EXISTS "${nvfbc_sdk}/inc" OR NOT IS_DIRECTORY "${nvfbc_sdk}/inc")
message("Disabling NVFBC support, can't find the SDK headers")
set(USE_NVFBC OFF)
endif()
if(USE_NVFBC)
add_capture("NVFBC")
endif()
if(USE_DXGI)
add_capture("DXGI")
endif()
include("PostCapture")
add_library(capture STATIC ${CAPTURE_C})
target_link_libraries(capture ${CAPTURE_LINK})

View File

@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.0)
project(capture_DXGI LANGUAGES C)
add_library(capture_DXGI STATIC
src/dxgi.c
)
add_definitions("-DCOBJMACROS -DINITGUID")
FIND_PROGRAM(DLLTOOL_EXECUTABLE NAMES "x86_64-w64-mingw32-dlltool" "dlltool" "dlltool.exe" DOC "dlltool executable")
ADD_CUSTOM_COMMAND(TARGET capture_DXGI POST_BUILD
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/dll"
COMMAND ${DLLTOOL_EXECUTABLE} --def libd3d11.def --output-lib "${PROJECT_BINARY_DIR}/libd3d11.dll"
VERBATIM
)
target_link_libraries(capture_DXGI
lg_common
${PROJECT_BINARY_DIR}/libd3d11.dll
dxgi
)
target_include_directories(capture_DXGI
PRIVATE
src
)

View File

@@ -0,0 +1,44 @@
LIBRARY "d3d11.dll"
EXPORTS
D3DKMTCloseAdapter
D3DKMTDestroyAllocation
D3DKMTDestroyContext
D3DKMTDestroyDevice
D3DKMTDestroySynchronizationObject
D3DKMTQueryAdapterInfo
D3DKMTSetDisplayPrivateDriverFormat
D3DKMTSignalSynchronizationObject
D3DKMTUnlock
D3DKMTWaitForSynchronizationObject
OpenAdapter10
OpenAdapter10_2
D3D11CoreCreateDevice
D3D11CoreCreateLayeredDevice
D3D11CoreGetLayeredDeviceSize
D3D11CoreRegisterLayers
D3D11CreateDevice
D3D11CreateDeviceAndSwapChain
D3DKMTCreateAllocation
D3DKMTCreateContext
D3DKMTCreateDevice
D3DKMTCreateSynchronizationObject
D3DKMTEscape
D3DKMTGetContextSchedulingPriority
D3DKMTGetDeviceState
D3DKMTGetDisplayModeList
D3DKMTGetMultisampleMethodList
D3DKMTGetRuntimeData
D3DKMTGetSharedPrimaryHandle
D3DKMTLock
D3DKMTOpenAdapterFromHdc
D3DKMTOpenResource
D3DKMTPresent
D3DKMTQueryAllocationResidency
D3DKMTQueryResourceInfo
D3DKMTRender
D3DKMTSetAllocationPriority
D3DKMTSetContextSchedulingPriority
D3DKMTSetDisplayMode
D3DKMTSetGammaRamp
D3DKMTSetVidPnSourceOwner
D3DKMTWaitForVerticalBlankEvent

View File

@@ -0,0 +1,922 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/capture.h"
#include "interface/platform.h"
#include "common/debug.h"
#include "common/windebug.h"
#include "common/option.h"
#include "common/locking.h"
#include "common/event.h"
#include <assert.h>
#include <dxgi.h>
#include <d3d11.h>
#include <d3dcommon.h>
#include "dxgi_extra.h"
#define LOCKED(x) INTERLOCKED_SECTION(this->deviceContextLock, x)
enum TextureState
{
TEXTURE_STATE_UNUSED,
TEXTURE_STATE_PENDING_MAP,
TEXTURE_STATE_MAPPED
};
typedef struct Texture
{
enum TextureState state;
ID3D11Texture2D * tex;
D3D11_MAPPED_SUBRESOURCE map;
}
Texture;
// locals
struct iface
{
bool initialized;
LARGE_INTEGER perfFreq;
LARGE_INTEGER frameTime;
bool stop;
HDESK desktop;
IDXGIFactory1 * factory;
IDXGIAdapter1 * adapter;
IDXGIOutput * output;
ID3D11Device * device;
ID3D11DeviceContext * deviceContext;
LG_Lock deviceContextLock;
bool useAcquireLock;
D3D_FEATURE_LEVEL featureLevel;
IDXGIOutputDuplication * dup;
int maxTextures;
Texture * texture;
int texRIndex;
int texWIndex;
volatile int texReady;
bool needsRelease;
CaptureGetPointerBuffer getPointerBufferFn;
CapturePostPointerBuffer postPointerBufferFn;
LGEvent * frameEvent;
unsigned int width;
unsigned int height;
unsigned int pitch;
unsigned int stride;
CaptureFormat format;
int lastPointerX, lastPointerY;
bool lastPointerVisible;
};
static bool dpiDone = false;
static struct iface * this = NULL;
// forwards
static bool dxgi_deinit();
static CaptureResult dxgi_releaseFrame();
// implementation
static const char * dxgi_getName()
{
return "DXGI";
}
static void dxgi_initOptions()
{
struct Option options[] =
{
{
.module = "dxgi",
.name = "adapter",
.description = "The name of the adapter to capture",
.type = OPTION_TYPE_STRING,
.value.x_string = NULL
},
{
.module = "dxgi",
.name = "output",
.description = "The name of the adapter's output to capture",
.type = OPTION_TYPE_STRING,
.value.x_string = NULL
},
{
.module = "dxgi",
.name = "maxTextures",
.description = "The maximum number of frames to buffer before skipping",
.type = OPTION_TYPE_INT,
.value.x_int = 3
},
{
.module = "dxgi",
.name = "useAcquireLock",
.description = "Enable locking around `AcquireFrame` (use if freezing, may lower performance)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{0}
};
option_register(options);
}
static bool dxgi_create(CaptureGetPointerBuffer getPointerBufferFn, CapturePostPointerBuffer postPointerBufferFn)
{
assert(!this);
this = calloc(sizeof(struct iface), 1);
if (!this)
{
DEBUG_ERROR("failed to allocate iface struct");
return false;
}
this->frameEvent = lgCreateEvent(true, 17); // 60Hz = 16.7ms
if (!this->frameEvent)
{
DEBUG_ERROR("failed to create the frame event");
free(this);
return false;
}
this->maxTextures = option_get_int("dxgi", "maxTextures");
if (this->maxTextures <= 0)
this->maxTextures = 1;
this->useAcquireLock = option_get_bool("dxgi", "useAcquireLock");
this->texture = calloc(sizeof(struct Texture), this->maxTextures);
this->getPointerBufferFn = getPointerBufferFn;
this->postPointerBufferFn = postPointerBufferFn;
return true;
}
static bool dxgi_init()
{
assert(this);
this->desktop = OpenInputDesktop(0, FALSE, GENERIC_READ);
if (!this->desktop)
DEBUG_WINERROR("Failed to open the desktop", GetLastError());
else
{
if (!SetThreadDesktop(this->desktop))
{
DEBUG_WINERROR("Failed to set thread desktop", GetLastError());
CloseDesktop(this->desktop);
this->desktop = NULL;
}
}
if (!this->desktop)
{
DEBUG_INFO("The above error(s) will prevent LG from being able to capture the secure desktop (UAC dialogs)");
DEBUG_INFO("This is not a failure, please do not report this as an issue.");
DEBUG_INFO("To fix this run LG using the PsExec SysInternals tool from Microsoft.");
DEBUG_INFO("https://docs.microsoft.com/en-us/sysinternals/downloads/psexec");
}
// this is required for DXGI 1.5 support to function
if (!dpiDone)
{
DECLARE_HANDLE(DPI_AWARENESS_CONTEXT);
#define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((DPI_AWARENESS_CONTEXT)-4)
typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value);
HMODULE user32 = LoadLibraryA("user32.dll");
User32_SetProcessDpiAwarenessContext fn;
fn = (User32_SetProcessDpiAwarenessContext)GetProcAddress(user32, "SetProcessDpiAwarenessContext");
if (fn)
fn(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
FreeLibrary(user32);
dpiDone = true;
}
HRESULT status;
DXGI_OUTPUT_DESC outputDesc;
this->stop = false;
this->texRIndex = 0;
this->texWIndex = 0;
this->texReady = 0;
lgResetEvent(this->frameEvent );
status = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&this->factory);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create DXGIFactory1", status);
goto fail;
}
const char * optAdapter = option_get_string("dxgi", "adapter");
const char * optOutput = option_get_string("dxgi", "output" );
for(int i = 0; IDXGIFactory1_EnumAdapters1(this->factory, i, &this->adapter) != DXGI_ERROR_NOT_FOUND; ++i)
{
if (optAdapter)
{
DXGI_ADAPTER_DESC1 adapterDesc;
status = IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to get the device description", status);
goto fail;
}
const size_t s = (wcslen(adapterDesc.Description)+1) * 2;
char * desc = malloc(s);
wcstombs(desc, adapterDesc.Description, s);
if (strstr(desc, optAdapter) == NULL)
{
DEBUG_INFO("Not using adapter: %ls", adapterDesc.Description);
free(desc);
IDXGIAdapter1_Release(this->adapter);
this->adapter = NULL;
continue;
}
free(desc);
DEBUG_INFO("Adapter matched, trying: %ls", adapterDesc.Description);
}
for(int n = 0; IDXGIAdapter1_EnumOutputs(this->adapter, n, &this->output) != DXGI_ERROR_NOT_FOUND; ++n)
{
IDXGIOutput_GetDesc(this->output, &outputDesc);
if (optOutput)
{
const size_t s = (wcslen(outputDesc.DeviceName)+1) * 2;
char * desc = malloc(s);
wcstombs(desc, outputDesc.DeviceName, s);
if (strstr(desc, optOutput) == NULL)
{
DEBUG_INFO("Not using adapter output: %ls", outputDesc.DeviceName);
free(desc);
IDXGIOutput_Release(this->output);
this->output = NULL;
continue;
}
free(desc);
DEBUG_INFO("Adapter output matched, trying: %ls", outputDesc.DeviceName);
}
if (outputDesc.AttachedToDesktop)
break;
IDXGIOutput_Release(this->output);
this->output = NULL;
}
if (this->output)
break;
IDXGIAdapter1_Release(this->adapter);
this->adapter = NULL;
}
if (!this->output)
{
DEBUG_ERROR("Failed to locate a valid output device");
goto fail;
}
static const D3D_FEATURE_LEVEL win8[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
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
};
static const D3D_FEATURE_LEVEL win10[] =
{
D3D_FEATURE_LEVEL_12_1,
D3D_FEATURE_LEVEL_12_0,
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
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
};
const D3D_FEATURE_LEVEL * featureLevels;
unsigned int featureLevelCount;
if (IsWindows8())
{
featureLevels = win8;
featureLevelCount = sizeof(win8) / sizeof(D3D_FEATURE_LEVEL);
}
else
{
featureLevels = win10;
featureLevelCount = sizeof(win10) / sizeof(D3D_FEATURE_LEVEL);
}
IDXGIAdapter * tmp;
status = IDXGIAdapter1_QueryInterface(this->adapter, &IID_IDXGIAdapter, (void **)&tmp);
if (FAILED(status))
{
DEBUG_ERROR("Failed to query IDXGIAdapter interface");
goto fail;
}
status = D3D11CreateDevice(
tmp,
D3D_DRIVER_TYPE_UNKNOWN,
NULL,
D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
featureLevels, featureLevelCount,
D3D11_SDK_VERSION,
&this->device,
&this->featureLevel,
&this->deviceContext);
LG_LOCK_INIT(this->deviceContextLock);
IDXGIAdapter_Release(tmp);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create D3D11 device", status);
goto fail;
}
DXGI_ADAPTER_DESC1 adapterDesc;
IDXGIAdapter1_GetDesc1(this->adapter, &adapterDesc);
this->width = outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left;
this->height = outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top;
DEBUG_INFO("Device Descripion: %ls" , adapterDesc.Description);
DEBUG_INFO("Device Vendor ID : 0x%x" , adapterDesc.VendorId);
DEBUG_INFO("Device Device ID : 0x%x" , adapterDesc.DeviceId);
DEBUG_INFO("Device Video Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedVideoMemory / 1048576));
DEBUG_INFO("Device Sys Mem : %u MiB" , (unsigned)(adapterDesc.DedicatedSystemMemory / 1048576));
DEBUG_INFO("Shared Sys Mem : %u MiB" , (unsigned)(adapterDesc.SharedSystemMemory / 1048576));
DEBUG_INFO("Feature Level : 0x%x" , this->featureLevel);
DEBUG_INFO("Capture Size : %u x %u", this->width, this->height);
DEBUG_INFO("AcquireLock : %s" , this->useAcquireLock ? "enabled" : "disabled");
// bump up our priority
{
IDXGIDevice * dxgi;
status = ID3D11Device_QueryInterface(this->device, &IID_IDXGIDevice, (void **)&dxgi);
if (FAILED(status))
{
DEBUG_WINERROR("failed to query DXGI interface from device", status);
goto fail;
}
IDXGIDevice_SetGPUThreadPriority(dxgi, 7);
IDXGIDevice_Release(dxgi);
}
// try to reduce the latency
{
IDXGIDevice1 * dxgi;
status = ID3D11Device_QueryInterface(this->device, &IID_IDXGIDevice1, (void **)&dxgi);
if (FAILED(status))
{
DEBUG_WINERROR("failed to query DXGI interface from device", status);
goto fail;
}
IDXGIDevice1_SetMaximumFrameLatency(dxgi, 1);
IDXGIDevice1_Release(dxgi);
}
IDXGIOutput5 * output5 = NULL;
status = IDXGIOutput_QueryInterface(this->output, &IID_IDXGIOutput5, (void **)&output5);
if (FAILED(status))
{
DEBUG_WARN("IDXGIOutput5 is not available, please update windows for improved performance!");
DEBUG_WARN("Falling back to IDXIGOutput1");
IDXGIOutput1 * output1 = NULL;
status = IDXGIOutput_QueryInterface(this->output, &IID_IDXGIOutput1, (void **)&output1);
if (FAILED(status))
{
DEBUG_ERROR("Failed to query IDXGIOutput1 from the output");
goto fail;
}
// we try this twice in case we still get an error on re-initialization
for (int i = 0; i < 2; ++i)
{
status = IDXGIOutput1_DuplicateOutput(output1, (IUnknown *)this->device, &this->dup);
if (SUCCEEDED(status))
break;
Sleep(200);
}
if (FAILED(status))
{
DEBUG_WINERROR("DuplicateOutput Failed", status);
IDXGIOutput1_Release(output1);
goto fail;
}
IDXGIOutput1_Release(output1);
}
else
{
const DXGI_FORMAT supportedFormats[] =
{
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R10G10B10A2_UNORM
};
// we try this twice in case we still get an error on re-initialization
for (int i = 0; i < 2; ++i)
{
status = IDXGIOutput5_DuplicateOutput1(
output5,
(IUnknown *)this->device,
0,
sizeof(supportedFormats) / sizeof(DXGI_FORMAT),
supportedFormats,
&this->dup);
if (SUCCEEDED(status))
break;
// if access is denied we just keep trying until it isn't
if (status == E_ACCESSDENIED)
--i;
Sleep(200);
}
if (FAILED(status))
{
DEBUG_WINERROR("DuplicateOutput1 Failed", status);
IDXGIOutput5_Release(output5);
goto fail;
}
IDXGIOutput5_Release(output5);
}
DXGI_OUTDUPL_DESC dupDesc;
IDXGIOutputDuplication_GetDesc(this->dup, &dupDesc);
DEBUG_INFO("Source Format : %s", GetDXGIFormatStr(dupDesc.ModeDesc.Format));
switch(dupDesc.ModeDesc.Format)
{
case DXGI_FORMAT_B8G8R8A8_UNORM : this->format = CAPTURE_FMT_BGRA ; break;
case DXGI_FORMAT_R8G8B8A8_UNORM : this->format = CAPTURE_FMT_RGBA ; break;
case DXGI_FORMAT_R10G10B10A2_UNORM: this->format = CAPTURE_FMT_RGBA10; break;
default:
DEBUG_ERROR("Unsupported source format");
goto fail;
}
D3D11_TEXTURE2D_DESC texDesc;
memset(&texDesc, 0, sizeof(texDesc));
texDesc.Width = this->width;
texDesc.Height = this->height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_STAGING;
texDesc.Format = dupDesc.ModeDesc.Format;
texDesc.BindFlags = 0;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
texDesc.MiscFlags = 0;
for(int i = 0; i < this->maxTextures; ++i)
{
status = ID3D11Device_CreateTexture2D(this->device, &texDesc, NULL, &this->texture[i].tex);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create texture", status);
goto fail;
}
}
// map the texture simply to get the pitch and stride
D3D11_MAPPED_SUBRESOURCE mapping;
status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource *)this->texture[0].tex, 0, D3D11_MAP_READ, 0, &mapping);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to map the texture", status);
goto fail;
}
this->pitch = mapping.RowPitch;
this->stride = mapping.RowPitch / 4;
ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource *)this->texture[0].tex, 0);
QueryPerformanceFrequency(&this->perfFreq) ;
QueryPerformanceCounter (&this->frameTime);
this->initialized = true;
return true;
fail:
dxgi_deinit();
return false;
}
static void dxgi_stop()
{
this->stop = true;
}
static bool dxgi_deinit()
{
assert(this);
for(int i = 0; i < this->maxTextures; ++i)
{
this->texture[i].state = TEXTURE_STATE_UNUSED;
if (this->texture[i].map.pData)
{
ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)this->texture[i].tex, 0);
this->texture[i].map.pData = NULL;
}
if (this->texture[i].tex)
{
ID3D11Texture2D_Release(this->texture[i].tex);
this->texture[i].tex = NULL;
}
}
if (this->dup)
{
dxgi_releaseFrame();
IDXGIOutputDuplication_Release(this->dup);
this->dup = NULL;
}
if (this->deviceContext)
{
ID3D11DeviceContext_Release(this->deviceContext);
this->deviceContext = NULL;
}
if (this->output)
{
IDXGIOutput_Release(this->output);
this->output = NULL;
}
if (this->device)
{
ID3D11Device_Release(this->device);
this->device = NULL;
}
if (this->adapter)
{
IDXGIAdapter1_Release(this->adapter);
this->adapter = NULL;
}
if (this->factory)
{
// if this doesn't free we have a memory leak
DWORD count = IDXGIFactory1_Release(this->factory);
this->factory = NULL;
if (count != 0)
{
DEBUG_ERROR("Factory release is %lu, there is a memory leak!", count);
return false;
}
}
LG_LOCK_FREE(this->deviceContextLock);
if (this->desktop)
{
CloseDesktop(this->desktop);
this->desktop = NULL;
}
this->initialized = false;
return true;
}
static void dxgi_free()
{
assert(this);
if (this->initialized)
dxgi_deinit();
free(this->texture);
free(this);
this = NULL;
}
static unsigned int dxgi_getMaxFrameSize()
{
assert(this);
assert(this->initialized);
return this->height * this->pitch;
}
static CaptureResult dxgi_hResultToCaptureResult(const HRESULT status)
{
switch(status)
{
case S_OK:
return CAPTURE_RESULT_OK;
case DXGI_ERROR_WAIT_TIMEOUT:
return CAPTURE_RESULT_TIMEOUT;
case WAIT_ABANDONED:
case DXGI_ERROR_ACCESS_LOST:
return CAPTURE_RESULT_REINIT;
default:
return CAPTURE_RESULT_ERROR;
}
}
static CaptureResult dxgi_capture()
{
assert(this);
assert(this->initialized);
Texture * tex;
CaptureResult result;
HRESULT status;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
IDXGIResource * res;
// release the prior frame
result = dxgi_releaseFrame();
if (result != CAPTURE_RESULT_OK)
return result;
if (this->useAcquireLock)
{
LOCKED({
status = IDXGIOutputDuplication_AcquireNextFrame(this->dup, 1, &frameInfo, &res);
});
}
else
status = IDXGIOutputDuplication_AcquireNextFrame(this->dup, 1000, &frameInfo, &res);
result = dxgi_hResultToCaptureResult(status);
if (result != CAPTURE_RESULT_OK)
{
if (result == CAPTURE_RESULT_ERROR)
DEBUG_WINERROR("AcquireNextFrame failed", status);
return result;
}
this->needsRelease = true;
if (frameInfo.LastPresentTime.QuadPart != 0)
{
tex = &this->texture[this->texWIndex];
// check if the texture is free, if not skip the frame to keep up
if (tex->state == TEXTURE_STATE_UNUSED)
{
ID3D11Texture2D * src;
status = IDXGIResource_QueryInterface(res, &IID_ID3D11Texture2D, (void **)&src);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to get the texture from the dxgi resource", status);
IDXGIResource_Release(res);
return CAPTURE_RESULT_ERROR;
}
LOCKED({
// issue the copy from GPU to CPU RAM and release the src
ID3D11DeviceContext_CopyResource(this->deviceContext,
(ID3D11Resource *)tex->tex, (ID3D11Resource *)src);
});
ID3D11Texture2D_Release(src);
// set the state, and signal
tex->state = TEXTURE_STATE_PENDING_MAP;
INTERLOCKED_INC(&this->texReady);
lgSignalEvent(this->frameEvent);
// advance the write index
if (++this->texWIndex == this->maxTextures)
this->texWIndex = 0;
// update the last frame time
this->frameTime.QuadPart = frameInfo.LastPresentTime.QuadPart;
}
}
IDXGIResource_Release(res);
// if the pointer has moved or changed state
bool postPointer = false;
CapturePointer pointer = { 0 };
void * pointerShape = NULL;
UINT pointerShapeSize = 0;
if (frameInfo.LastMouseUpdateTime.QuadPart)
{
/* the pointer position is only valid if the pointer is visible */
if (frameInfo.PointerPosition.Visible &&
(frameInfo.PointerPosition.Position.x != this->lastPointerX ||
frameInfo.PointerPosition.Position.y != this->lastPointerY))
{
pointer.positionUpdate = true;
pointer.x =
this->lastPointerX =
frameInfo.PointerPosition.Position.x;
pointer.y =
this->lastPointerY =
frameInfo.PointerPosition.Position.y;
postPointer = true;
}
if (this->lastPointerVisible != frameInfo.PointerPosition.Visible)
{
this->lastPointerVisible = frameInfo.PointerPosition.Visible;
postPointer = true;
}
}
// if the pointer shape has changed
if (frameInfo.PointerShapeBufferSize > 0)
{
uint32_t bufferSize;
if(!this->getPointerBufferFn(&pointerShape, &bufferSize))
DEBUG_WARN("Failed to obtain a buffer for the pointer shape");
else
{
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
LOCKED({status = IDXGIOutputDuplication_GetFramePointerShape(this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);});
result = dxgi_hResultToCaptureResult(status);
if (result != CAPTURE_RESULT_OK)
{
if (result == CAPTURE_RESULT_ERROR)
DEBUG_WINERROR("Failed to get the new pointer shape", status);
return result;
}
switch(shapeInfo.Type)
{
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : pointer.format = CAPTURE_FMT_COLOR ; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: pointer.format = CAPTURE_FMT_MASKED; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : pointer.format = CAPTURE_FMT_MONO ; break;
default:
DEBUG_ERROR("Unsupported cursor format");
return CAPTURE_RESULT_ERROR;
}
pointer.shapeUpdate = true;
pointer.width = shapeInfo.Width;
pointer.height = shapeInfo.Height;
pointer.pitch = shapeInfo.Pitch;
postPointer = true;
}
}
// post back the pointer information
if (postPointer)
{
pointer.visible = this->lastPointerVisible;
this->postPointerBufferFn(pointer);
}
return CAPTURE_RESULT_OK;
}
static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
{
assert(this);
assert(this->initialized);
// NOTE: the event may be signaled when there are no frames available
if(this->texReady == 0)
{
if (!lgWaitEvent(this->frameEvent, 1000))
return CAPTURE_RESULT_TIMEOUT;
if (this->texReady == 0)
return CAPTURE_RESULT_TIMEOUT;
}
Texture * tex = &this->texture[this->texRIndex];
// try to map the resource, but don't wait for it
HRESULT status;
LOCKED({status = ID3D11DeviceContext_Map(this->deviceContext, (ID3D11Resource*)tex->tex, 0, D3D11_MAP_READ, 0x100000L, &tex->map);});
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
return CAPTURE_RESULT_TIMEOUT;
if (FAILED(status))
{
DEBUG_WINERROR("Failed to map the texture", status);
return CAPTURE_RESULT_ERROR;
}
tex->state = TEXTURE_STATE_MAPPED;
frame->width = this->width;
frame->height = this->height;
frame->pitch = this->pitch;
frame->stride = this->stride;
frame->format = this->format;
INTERLOCKED_DEC(&this->texReady);
return CAPTURE_RESULT_OK;
}
static CaptureResult dxgi_getFrame(FrameBuffer * frame)
{
assert(this);
assert(this->initialized);
Texture * tex = &this->texture[this->texRIndex];
framebuffer_write(frame, tex->map.pData, this->pitch * this->height);
LOCKED({ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)tex->tex, 0);});
tex->state = TEXTURE_STATE_UNUSED;
if (++this->texRIndex == this->maxTextures)
this->texRIndex = 0;
return CAPTURE_RESULT_OK;
}
static CaptureResult dxgi_releaseFrame()
{
assert(this);
if (!this->needsRelease)
return CAPTURE_RESULT_OK;
HRESULT status;
LOCKED({status = IDXGIOutputDuplication_ReleaseFrame(this->dup);});
switch(status)
{
case S_OK:
break;
case DXGI_ERROR_INVALID_CALL:
DEBUG_WINERROR("Frame was already released", status);
return CAPTURE_RESULT_ERROR;
case WAIT_ABANDONED:
case DXGI_ERROR_ACCESS_LOST:
{
this->needsRelease = false;
return CAPTURE_RESULT_REINIT;
}
default:
DEBUG_WINERROR("ReleaseFrame failed", status);
return CAPTURE_RESULT_ERROR;
}
this->needsRelease = false;
return CAPTURE_RESULT_OK;
}
struct CaptureInterface Capture_DXGI =
{
.getName = dxgi_getName,
.initOptions = dxgi_initOptions,
.create = dxgi_create,
.init = dxgi_init,
.stop = dxgi_stop,
.deinit = dxgi_deinit,
.free = dxgi_free,
.getMaxFrameSize = dxgi_getMaxFrameSize,
.capture = dxgi_capture,
.waitFrame = dxgi_waitFrame,
.getFrame = dxgi_getFrame
};

View File

@@ -0,0 +1,648 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <dxgi.h>
#include <d3d11.h>
#include <d3dcommon.h>
// missing declarations in dxgi.h
HRESULT __stdcall CreateDXGIFactory1(REFIID riid, void **factory);
#define D3D_FEATURE_LEVEL_12_0 0xc000
#define D3D_FEATURE_LEVEL_12_1 0xc100
#ifndef DXGI_ERROR_ACCESS_LOST
#define DXGI_ERROR_ACCESS_LOST _HRESULT_TYPEDEF_(0x887A0026L)
#endif
#ifndef DXGI_ERROR_WAIT_TIMEOUT
#define DXGI_ERROR_WAIT_TIMEOUT _HRESULT_TYPEDEF_(0x887A0027L)
#endif
enum DXGI_OUTDUPL_POINTER_SHAPE_TYPE {
DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME = 0x1,
DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR = 0x2,
DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR = 0x4
};
typedef struct DXGI_OUTDUPL_DESC {
DXGI_MODE_DESC ModeDesc;
DXGI_MODE_ROTATION Rotation;
BOOL DesktopImageInSystemMemory;
}
DXGI_OUTDUPL_DESC;
typedef struct DXGI_OUTDUPL_POINTER_POSITION {
POINT Position;
BOOL Visible;
}
DXGI_OUTDUPL_POINTER_POSITION;
typedef struct DXGI_OUTDUPL_FRAME_INFO {
LARGE_INTEGER LastPresentTime;
LARGE_INTEGER LastMouseUpdateTime;
UINT AccumulatedFrames;
BOOL RectsCoalesced;
BOOL ProtectedContentMaskedOut;
DXGI_OUTDUPL_POINTER_POSITION PointerPosition;
UINT TotalMetadataBufferSize;
UINT PointerShapeBufferSize;
}
DXGI_OUTDUPL_FRAME_INFO;
typedef struct DXGI_OUTDUPL_MOVE_RECT {
POINT SourcePoint;
RECT DestinationRect;
}
DXGI_OUTDUPL_MOVE_RECT;
typedef struct DXGI_OUTDUPL_POINTER_SHAPE_INFO {
UINT Type;
UINT Width;
UINT Height;
UINT Pitch;
POINT HotSpot;
}
DXGI_OUTDUPL_POINTER_SHAPE_INFO;
DEFINE_GUID(IID_IDXGIOutputDuplication, 0x191cfac3, 0xa341, 0x470d, 0xb2,0x6e,0xa8,0x64,0xf4,0x28,0x31,0x9c);
typedef interface IDXGIOutputDuplication IDXGIOutputDuplication;
typedef struct IDXGIOutputDuplicationVtbl {
BEGIN_INTERFACE
/*** IUnknown methods ***/
HRESULT (STDMETHODCALLTYPE *QueryInterface)(
IDXGIOutputDuplication* This,
REFIID riid,
void **ppvObject);
ULONG (STDMETHODCALLTYPE *AddRef)(
IDXGIOutputDuplication* This);
ULONG (STDMETHODCALLTYPE *Release)(
IDXGIOutputDuplication* This);
/*** IDXGIObject methods ***/
HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
IDXGIOutputDuplication* This,
REFGUID guid,
UINT data_size,
const void *data);
HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
IDXGIOutputDuplication* This,
REFGUID guid,
const IUnknown *object);
HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
IDXGIOutputDuplication* This,
REFGUID guid,
UINT *data_size,
void *data);
HRESULT (STDMETHODCALLTYPE *GetParent)(
IDXGIOutputDuplication* This,
REFIID riid,
void **parent);
/*** IDXGIOutputDuplication methods ***/
void (STDMETHODCALLTYPE *GetDesc)(
IDXGIOutputDuplication* This,
DXGI_OUTDUPL_DESC *pDesc);
HRESULT (STDMETHODCALLTYPE *AcquireNextFrame)(
IDXGIOutputDuplication* This,
UINT TimeoutInMilliseconds,
DXGI_OUTDUPL_FRAME_INFO *pFrameInfo,
IDXGIResource **ppDesktopResource);
HRESULT (STDMETHODCALLTYPE *GetFrameDirtyRects)(
IDXGIOutputDuplication* This,
UINT DirtyRectsBufferSize,
RECT *pDirtyRectsBuffer,
UINT *pDirtyRectsBufferSizeRequired);
HRESULT (STDMETHODCALLTYPE *GetFrameMoveRects)(
IDXGIOutputDuplication* This,
UINT MoveRectsBufferSize,
DXGI_OUTDUPL_MOVE_RECT *pMoveRectBuffer,
UINT *pMoveRectsBufferSizeRequired);
HRESULT (STDMETHODCALLTYPE *GetFramePointerShape)(
IDXGIOutputDuplication* This,
UINT PointerShapeBufferSize,
void *pPointerShapeBuffer,
UINT *pPointerShapeBufferSizeRequired,
DXGI_OUTDUPL_POINTER_SHAPE_INFO *pPointerShapeInfo);
HRESULT (STDMETHODCALLTYPE *MapDesktopSurface)(
IDXGIOutputDuplication* This,
DXGI_MAPPED_RECT *pLockedRect);
HRESULT (STDMETHODCALLTYPE *UnMapDesktopSurface)(
IDXGIOutputDuplication* This);
HRESULT (STDMETHODCALLTYPE *ReleaseFrame)(
IDXGIOutputDuplication* This);
END_INTERFACE
}
IDXGIOutputDuplicationVtbl;
interface IDXGIOutputDuplication {
CONST_VTBL IDXGIOutputDuplicationVtbl* lpVtbl;
};
#define IDXGIOutputDuplication_Release(This) (This)->lpVtbl->Release(This)
#define IDXGIOutputDuplication_GetDesc(This, pDesc) (This)->lpVtbl->GetDesc(This, pDesc)
#define IDXGIOutputDuplication_AcquireNextFrame(This, TimeoutInMilliseconds, pFrameInfo, ppDesktopResource) (This)->lpVtbl->AcquireNextFrame(This, TimeoutInMilliseconds, pFrameInfo, ppDesktopResource)
#define IDXGIOutputDuplication_GetFrameDirtyRects(This, DirtyRectsBufferSize, pDirectyRectsBuffer, pDirtyRectsBufferSizeRequired) (This)->lpVtbl->GetFrameDirtyRects(This, DirtyRectsBufferSize, pDirectyRectsBuffer, pDirtyRectsBufferSizeRequired)
#define IDXGIOutputDuplication_GetFrameMoveRects(This, MoveRectsBufferSize, pDirtyRectsBuffer, pDirtyRectsBufferSizeRequired) (This)->lpVtbl->GetFrameMoveRects(This, MoveRectsBufferSize, pDirtyRectsBuffer, pDirtyRectsBufferSizeRequired)
#define IDXGIOutputDuplication_GetFramePointerShape(This, PointerShapeBufferSize, pPointerShapeBuffer, pPointerShapeBufferSizeRequired, pPointerShapeInfo) (This)->lpVtbl->GetFramePointerShape(This, PointerShapeBufferSize, pPointerShapeBuffer, pPointerShapeBufferSizeRequired, pPointerShapeInfo)
#define IDXGIOutputDuplication_MapDesktopSurface(This, pLockedRect) (This)->lpVtbl->MapDesktopSurface(This, pLockedRect)
#define IDXGIOutputDuplication_UnMapDesktopSurface(This) (This)->lpVtbl->UnMapDesktopSurface(This)
#define IDXGIOutputDuplication_ReleaseFrame(This) (This)->lpVtbl->ReleaseFrame(This)
typedef struct DXGI_MODE_DESC1
{
UINT Width;
UINT Height;
DXGI_RATIONAL RefreshRate;
DXGI_FORMAT Format;
DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;
DXGI_MODE_SCALING Scaling;
BOOL Stereo;
}
DXGI_MODE_DESC1;
#ifndef __dxgicommon_h__
typedef enum DXGI_COLOR_SPACE_TYPE {
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0,
DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1,
DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2,
DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3,
DXGI_COLOR_SPACE_RESERVED = 4,
DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6,
DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8,
DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10,
DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11,
DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13,
DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 14,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16,
DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17,
DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020 = 18,
DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020 = 19,
DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709 = 20,
DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020 = 21,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709 = 22,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020 = 23,
DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020 = 24,
DXGI_COLOR_SPACE_CUSTOM = 0xFFFFFFFF
} DXGI_COLOR_SPACE_TYPE;
#endif
DEFINE_GUID(IID_IDXGIOutput1, 0x00cddea8, 0x939b, 0x4b83, 0xa3,0x40,0xa6,0x85,0x22,0x66,0x66,0xcc);
typedef struct IDXGIOutput1 IDXGIOutput1;
typedef struct IDXGIOutput1Vtbl {
BEGIN_INTERFACE
/*** IUnknown methods ***/
HRESULT (STDMETHODCALLTYPE *QueryInterface)(
IDXGIOutput1* This,
REFIID riid,
void **ppvObject);
ULONG (STDMETHODCALLTYPE *AddRef)(
IDXGIOutput1* This);
ULONG (STDMETHODCALLTYPE *Release)(
IDXGIOutput1* This);
/*** IDXGIObject methods ***/
HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
IDXGIOutput1* This,
REFGUID guid,
UINT data_size,
const void *data);
HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
IDXGIOutput1* This,
REFGUID guid,
const IUnknown *object);
HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
IDXGIOutput1* This,
REFGUID guid,
UINT *data_size,
void *data);
HRESULT (STDMETHODCALLTYPE *GetParent)(
IDXGIOutput1* This,
REFIID riid,
void **parent);
/*** IDXGIOutput methods ***/
HRESULT (STDMETHODCALLTYPE *GetDesc)(
IDXGIOutput1* This,
DXGI_OUTPUT_DESC *desc);
HRESULT (STDMETHODCALLTYPE *GetDisplayModeList)(
IDXGIOutput1* This,
DXGI_FORMAT format,
UINT flags,
UINT *mode_count,
DXGI_MODE_DESC *desc);
HRESULT (STDMETHODCALLTYPE *FindClosestMatchingMode)(
IDXGIOutput1* This,
const DXGI_MODE_DESC *mode,
DXGI_MODE_DESC *closest_match,
IUnknown *device);
HRESULT (STDMETHODCALLTYPE *WaitForVBlank)(
IDXGIOutput1* This);
HRESULT (STDMETHODCALLTYPE *TakeOwnership)(
IDXGIOutput1* This,
IUnknown *device,
WINBOOL exclusive);
void (STDMETHODCALLTYPE *ReleaseOwnership)(
IDXGIOutput1* This);
HRESULT (STDMETHODCALLTYPE *GetGammaControlCapabilities)(
IDXGIOutput1* This,
DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps);
HRESULT (STDMETHODCALLTYPE *SetGammaControl)(
IDXGIOutput1* This,
const DXGI_GAMMA_CONTROL *gamma_control);
HRESULT (STDMETHODCALLTYPE *GetGammaControl)(
IDXGIOutput1* This,
DXGI_GAMMA_CONTROL *gamma_control);
HRESULT (STDMETHODCALLTYPE *SetDisplaySurface)(
IDXGIOutput1* This,
IDXGISurface *surface);
HRESULT (STDMETHODCALLTYPE *GetDisplaySurfaceData)(
IDXGIOutput1* This,
IDXGISurface *surface);
HRESULT (STDMETHODCALLTYPE *GetFrameStatistics)(
IDXGIOutput1* This,
DXGI_FRAME_STATISTICS *stats);
/*** IDXGIOutput1 methods ***/
HRESULT (STDMETHODCALLTYPE *GetDisplayModeList1)(
IDXGIOutput1* This,
DXGI_FORMAT EnumFormat,
UINT Flags,
UINT *pNumModes,
DXGI_MODE_DESC1 *pDesc);
HRESULT (STDMETHODCALLTYPE *FindClosestMatchingMode1)(
IDXGIOutput1* This,
const DXGI_MODE_DESC1 *pModeToMatch,
DXGI_MODE_DESC1 *pClosestMatch,
IUnknown *pConcernedDevice);
HRESULT (STDMETHODCALLTYPE *GetDisplaySurfaceData1)(
IDXGIOutput1* This,
IDXGIResource *pDestination);
HRESULT (STDMETHODCALLTYPE *DuplicateOutput)(
IDXGIOutput1* This,
IUnknown *pDevice,
IDXGIOutputDuplication **ppOutputDuplication);
END_INTERFACE
}
IDXGIOutput1Vtbl;
interface IDXGIOutput1 {
CONST_VTBL IDXGIOutput1Vtbl* lpVtbl;
};
#define IDXGIOutput1_DuplicateOutput(This,pDevice,ppOutputDuplication) (This)->lpVtbl->DuplicateOutput(This,pDevice,ppOutputDuplication)
#define IDXGIOutput1_Release(This) (This)->lpVtbl->Release(This);
DEFINE_GUID(IID_IDXGIOutput5, 0x80a07424, 0xab52, 0x42eb, 0x83,0x3c,0x0c,0x42,0xfd,0x28,0x2d,0x98);
typedef struct IDXGIOutput5 IDXGIOutput5;
typedef struct IDXGIOutput5Vtbl {
BEGIN_INTERFACE
/*** IUnknown methods ***/
HRESULT (STDMETHODCALLTYPE *QueryInterface)(
IDXGIOutput5* This,
REFIID riid,
void **ppvObject);
ULONG (STDMETHODCALLTYPE *AddRef)(
IDXGIOutput5* This);
ULONG (STDMETHODCALLTYPE *Release)(
IDXGIOutput5* This);
/*** IDXGIObject methods ***/
HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
IDXGIOutput5* This,
REFGUID guid,
UINT data_size,
const void *data);
HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
IDXGIOutput5* This,
REFGUID guid,
const IUnknown *object);
HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
IDXGIOutput5* This,
REFGUID guid,
UINT *data_size,
void *data);
HRESULT (STDMETHODCALLTYPE *GetParent)(
IDXGIOutput5* This,
REFIID riid,
void **parent);
/*** IDXGIOutput methods ***/
HRESULT (STDMETHODCALLTYPE *GetDesc)(
IDXGIOutput5* This,
DXGI_OUTPUT_DESC *desc);
HRESULT (STDMETHODCALLTYPE *GetDisplayModeList)(
IDXGIOutput5* This,
DXGI_FORMAT format,
UINT flags,
UINT *mode_count,
DXGI_MODE_DESC *desc);
HRESULT (STDMETHODCALLTYPE *FindClosestMatchingMode)(
IDXGIOutput5* This,
const DXGI_MODE_DESC *mode,
DXGI_MODE_DESC *closest_match,
IUnknown *device);
HRESULT (STDMETHODCALLTYPE *WaitForVBlank)(
IDXGIOutput5* This);
HRESULT (STDMETHODCALLTYPE *TakeOwnership)(
IDXGIOutput5* This,
IUnknown *device,
WINBOOL exclusive);
void (STDMETHODCALLTYPE *ReleaseOwnership)(
IDXGIOutput5* This);
HRESULT (STDMETHODCALLTYPE *GetGammaControlCapabilities)(
IDXGIOutput5* This,
DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps);
HRESULT (STDMETHODCALLTYPE *SetGammaControl)(
IDXGIOutput5* This,
const DXGI_GAMMA_CONTROL *gamma_control);
HRESULT (STDMETHODCALLTYPE *GetGammaControl)(
IDXGIOutput5* This,
DXGI_GAMMA_CONTROL *gamma_control);
HRESULT (STDMETHODCALLTYPE *SetDisplaySurface)(
IDXGIOutput5* This,
IDXGISurface *surface);
HRESULT (STDMETHODCALLTYPE *GetDisplaySurfaceData)(
IDXGIOutput5* This,
IDXGISurface *surface);
HRESULT (STDMETHODCALLTYPE *GetFrameStatistics)(
IDXGIOutput5* This,
DXGI_FRAME_STATISTICS *stats);
/*** IDXGIOutput1 methods ***/
HRESULT (STDMETHODCALLTYPE *GetDisplayModeList1)(
IDXGIOutput5* This,
DXGI_FORMAT EnumFormat,
UINT Flags,
UINT *pNumModes,
DXGI_MODE_DESC1 *pDesc);
HRESULT (STDMETHODCALLTYPE *FindClosestMatchingMode1)(
IDXGIOutput5* This,
const DXGI_MODE_DESC1 *pModeToMatch,
DXGI_MODE_DESC1 *pClosestMatch,
IUnknown *pConcernedDevice);
HRESULT (STDMETHODCALLTYPE *GetDisplaySurfaceData1)(
IDXGIOutput5* This,
IDXGIResource *pDestination);
HRESULT (STDMETHODCALLTYPE *DuplicateOutput)(
IDXGIOutput5* This,
IUnknown *pDevice,
IDXGIOutputDuplication **ppOutputDuplication);
/*** IDXGIOutput2 methods ***/
BOOL (STDMETHODCALLTYPE *SupportsOverlays)(
IDXGIOutput5* This);
/*** IDXGIOutput3 methods ***/
HRESULT (STDMETHODCALLTYPE *CheckOverlaySupport)(
IDXGIOutput5* This,
DXGI_FORMAT EnumFormat,
IUnknown *pConcernedDevice,
UINT *pFlags);
/*** IDXGIOutput4 methods ***/
HRESULT (STDMETHODCALLTYPE *CheckOverlayColorSpaceSupport)(
IDXGIOutput5* This,
DXGI_FORMAT Format,
DXGI_COLOR_SPACE_TYPE ColorSpace,
IUnknown *pConcernedDevice,
UINT *pFlags);
/*** IDXGIOutput5 methods ***/
HRESULT (STDMETHODCALLTYPE *DuplicateOutput1)(
IDXGIOutput5* This,
IUnknown *pDevice,
UINT Flags,
UINT SupportedFormatsCount,
const DXGI_FORMAT *pSupportedFormats,
IDXGIOutputDuplication **ppOutputDuplication);
END_INTERFACE
}
IDXGIOutput5Vtbl;
interface IDXGIOutput5 {
CONST_VTBL IDXGIOutput5Vtbl* lpVtbl;
};
#define IDXGIOutput5_DuplicateOutput1(This,pDevice,Flags,SupportedForamtsCount,pSupportedFormats,ppOutputDuplication) (This)->lpVtbl->DuplicateOutput1(This,pDevice,Flags,SupportedForamtsCount,pSupportedFormats,ppOutputDuplication)
#define IDXGIOutput5_Release(This) (This)->lpVtbl->Release(This);
static const char * DXGI_FORMAT_STR[] = {
"DXGI_FORMAT_UNKNOWN",
"DXGI_FORMAT_R32G32B32A32_TYPELESS",
"DXGI_FORMAT_R32G32B32A32_FLOAT",
"DXGI_FORMAT_R32G32B32A32_UINT",
"DXGI_FORMAT_R32G32B32A32_SINT",
"DXGI_FORMAT_R32G32B32_TYPELESS",
"DXGI_FORMAT_R32G32B32_FLOAT",
"DXGI_FORMAT_R32G32B32_UINT",
"DXGI_FORMAT_R32G32B32_SINT",
"DXGI_FORMAT_R16G16B16A16_TYPELESS",
"DXGI_FORMAT_R16G16B16A16_FLOAT",
"DXGI_FORMAT_R16G16B16A16_UNORM",
"DXGI_FORMAT_R16G16B16A16_UINT",
"DXGI_FORMAT_R16G16B16A16_SNORM",
"DXGI_FORMAT_R16G16B16A16_SINT",
"DXGI_FORMAT_R32G32_TYPELESS",
"DXGI_FORMAT_R32G32_FLOAT",
"DXGI_FORMAT_R32G32_UINT",
"DXGI_FORMAT_R32G32_SINT",
"DXGI_FORMAT_R32G8X24_TYPELESS",
"DXGI_FORMAT_D32_FLOAT_S8X24_UINT",
"DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS",
"DXGI_FORMAT_X32_TYPELESS_G8X24_UINT",
"DXGI_FORMAT_R10G10B10A2_TYPELESS",
"DXGI_FORMAT_R10G10B10A2_UNORM",
"DXGI_FORMAT_R10G10B10A2_UINT",
"DXGI_FORMAT_R11G11B10_FLOAT",
"DXGI_FORMAT_R8G8B8A8_TYPELESS",
"DXGI_FORMAT_R8G8B8A8_UNORM",
"DXGI_FORMAT_R8G8B8A8_UNORM_SRGB",
"DXGI_FORMAT_R8G8B8A8_UINT",
"DXGI_FORMAT_R8G8B8A8_SNORM",
"DXGI_FORMAT_R8G8B8A8_SINT",
"DXGI_FORMAT_R16G16_TYPELESS",
"DXGI_FORMAT_R16G16_FLOAT",
"DXGI_FORMAT_R16G16_UNORM",
"DXGI_FORMAT_R16G16_UINT",
"DXGI_FORMAT_R16G16_SNORM",
"DXGI_FORMAT_R16G16_SINT",
"DXGI_FORMAT_R32_TYPELESS",
"DXGI_FORMAT_D32_FLOAT",
"DXGI_FORMAT_R32_FLOAT",
"DXGI_FORMAT_R32_UINT",
"DXGI_FORMAT_R32_SINT",
"DXGI_FORMAT_R24G8_TYPELESS",
"DXGI_FORMAT_D24_UNORM_S8_UINT",
"DXGI_FORMAT_R24_UNORM_X8_TYPELESS",
"DXGI_FORMAT_X24_TYPELESS_G8_UINT",
"DXGI_FORMAT_R8G8_TYPELESS",
"DXGI_FORMAT_R8G8_UNORM",
"DXGI_FORMAT_R8G8_UINT",
"DXGI_FORMAT_R8G8_SNORM",
"DXGI_FORMAT_R8G8_SINT",
"DXGI_FORMAT_R16_TYPELESS",
"DXGI_FORMAT_R16_FLOAT",
"DXGI_FORMAT_D16_UNORM",
"DXGI_FORMAT_R16_UNORM",
"DXGI_FORMAT_R16_UINT",
"DXGI_FORMAT_R16_SNORM",
"DXGI_FORMAT_R16_SINT",
"DXGI_FORMAT_R8_TYPELESS",
"DXGI_FORMAT_R8_UNORM",
"DXGI_FORMAT_R8_UINT",
"DXGI_FORMAT_R8_SNORM",
"DXGI_FORMAT_R8_SINT",
"DXGI_FORMAT_A8_UNORM",
"DXGI_FORMAT_R1_UNORM",
"DXGI_FORMAT_R9G9B9E5_SHAREDEXP",
"DXGI_FORMAT_R8G8_B8G8_UNORM",
"DXGI_FORMAT_G8R8_G8B8_UNORM",
"DXGI_FORMAT_BC1_TYPELESS",
"DXGI_FORMAT_BC1_UNORM",
"DXGI_FORMAT_BC1_UNORM_SRGB",
"DXGI_FORMAT_BC2_TYPELESS",
"DXGI_FORMAT_BC2_UNORM",
"DXGI_FORMAT_BC2_UNORM_SRGB",
"DXGI_FORMAT_BC3_TYPELESS",
"DXGI_FORMAT_BC3_UNORM",
"DXGI_FORMAT_BC3_UNORM_SRGB",
"DXGI_FORMAT_BC4_TYPELESS",
"DXGI_FORMAT_BC4_UNORM",
"DXGI_FORMAT_BC4_SNORM",
"DXGI_FORMAT_BC5_TYPELESS",
"DXGI_FORMAT_BC5_UNORM",
"DXGI_FORMAT_BC5_SNORM",
"DXGI_FORMAT_B5G6R5_UNORM",
"DXGI_FORMAT_B5G5R5A1_UNORM",
"DXGI_FORMAT_B8G8R8A8_UNORM",
"DXGI_FORMAT_B8G8R8X8_UNORM",
"DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM",
"DXGI_FORMAT_B8G8R8A8_TYPELESS",
"DXGI_FORMAT_B8G8R8A8_UNORM_SRGB",
"DXGI_FORMAT_B8G8R8X8_TYPELESS",
"DXGI_FORMAT_B8G8R8X8_UNORM_SRGB",
"DXGI_FORMAT_BC6H_TYPELESS",
"DXGI_FORMAT_BC6H_UF16",
"DXGI_FORMAT_BC6H_SF16",
"DXGI_FORMAT_BC7_TYPELESS",
"DXGI_FORMAT_BC7_UNORM",
"DXGI_FORMAT_BC7_UNORM_SRGB",
"DXGI_FORMAT_AYUV",
"DXGI_FORMAT_Y410",
"DXGI_FORMAT_Y416",
"DXGI_FORMAT_NV12",
"DXGI_FORMAT_P010",
"DXGI_FORMAT_P016",
"DXGI_FORMAT_420_OPAQUE",
"DXGI_FORMAT_YUY2",
"DXGI_FORMAT_Y210",
"DXGI_FORMAT_Y216",
"DXGI_FORMAT_NV11",
"DXGI_FORMAT_AI44",
"DXGI_FORMAT_IA44",
"DXGI_FORMAT_P8",
"DXGI_FORMAT_A8P8",
"DXGI_FORMAT_B4G4R4A4_UNORM",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
"DXGI_FORMAT_P208",
"DXGI_FORMAT_V208",
"DXGI_FORMAT_V408"
};
static const char * GetDXGIFormatStr(DXGI_FORMAT format)
{
if (format > sizeof(DXGI_FORMAT_STR) / sizeof(const char *))
return DXGI_FORMAT_STR[0];
return DXGI_FORMAT_STR[format];
}

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.0)
project(capture_NVFBC LANGUAGES C CXX)
add_library(capture_NVFBC STATIC
src/nvfbc.c
src/wrapper.cpp
)
file(TO_CMAKE_PATH "${NVFBC_SDK}" nvfbc_sdk)
include_directories(file, "${nvfbc_sdk}/inc")
target_include_directories(capture_NVFBC
PRIVATE
src
)
target_link_libraries(capture_NVFBC
platform_Windows
)

View File

@@ -0,0 +1,387 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2020 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "interface/capture.h"
#include "interface/platform.h"
#include "common/windebug.h"
#include "windows/mousehook.h"
#include "common/option.h"
#include "common/framebuffer.h"
#include "common/event.h"
#include "common/thread.h"
#include <assert.h>
#include <stdlib.h>
#include <windows.h>
#include <NvFBC/nvFBC.h>
#include "wrapper.h"
struct iface
{
bool stop;
NvFBCHandle nvfbc;
bool seperateCursor;
CaptureGetPointerBuffer getPointerBufferFn;
CapturePostPointerBuffer postPointerBufferFn;
LGThread * pointerThread;
unsigned int maxWidth, maxHeight;
unsigned int width , height;
uint8_t * frameBuffer;
uint8_t * diffMap;
NvFBCFrameGrabInfo grabInfo;
LGEvent * frameEvent;
LGEvent * cursorEvents[2];
int mouseX, mouseY, mouseHotX, mouseHotY;
bool mouseVisible;
};
static struct iface * this = NULL;
static void nvfbc_free();
static int pointerThread(void * unused);
static void getDesktopSize(unsigned int * width, unsigned int * height)
{
HMONITOR monitor = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTOPRIMARY);
MONITORINFO monitorInfo = {
.cbSize = sizeof(MONITORINFO)
};
GetMonitorInfo(monitor, &monitorInfo);
CloseHandle(monitor);
*width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
*height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
}
static void on_mouseMove(int x, int y)
{
this->mouseX = x;
this->mouseY = y;
lgSignalEvent(this->cursorEvents[0]);
}
static const char * nvfbc_getName()
{
return "NVFBC (NVidia Frame Buffer Capture)";
};
static void nvfbc_initOptions()
{
struct Option options[] =
{
{
.module = "nvfbc",
.name = "decoupleCursor",
.description = "Capture the cursor seperately",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{0}
};
option_register(options);
}
static bool nvfbc_create(
CaptureGetPointerBuffer getPointerBufferFn,
CapturePostPointerBuffer postPointerBufferFn)
{
if (!NvFBCInit())
return false;
int bufferLen = GetEnvironmentVariable("NVFBC_PRIV_DATA", NULL, 0);
uint8_t * privData = NULL;
int privDataLen = 0;
if(bufferLen)
{
char * buffer = malloc(bufferLen);
GetEnvironmentVariable("NVFBC_PRIV_DATA", buffer, bufferLen);
privDataLen = (bufferLen - 1) / 2;
privData = (uint8_t *)malloc(privDataLen);
char hex[3] = {0};
for(int i = 0; i < privDataLen; ++i)
{
memcpy(hex, &buffer[i*2], 2);
privData[i] = (uint8_t)strtoul(hex, NULL, 16);
}
free(buffer);
}
this = (struct iface *)calloc(sizeof(struct iface), 1);
if (!NvFBCToSysCreate(privData, privDataLen, &this->nvfbc, &this->maxWidth, &this->maxHeight))
{
free(privData);
nvfbc_free();
return false;
}
free(privData);
this->frameEvent = lgCreateEvent(true, 17);
if (!this->frameEvent)
{
DEBUG_ERROR("failed to create the frame event");
nvfbc_free();
return false;
}
this->seperateCursor = option_get_bool("nvfbc", "decoupleCursor");
this->getPointerBufferFn = getPointerBufferFn;
this->postPointerBufferFn = postPointerBufferFn;
return true;
}
static bool nvfbc_init()
{
this->stop = false;
getDesktopSize(&this->width, &this->height);
lgResetEvent(this->frameEvent);
HANDLE event;
if (!NvFBCToSysSetup(
this->nvfbc,
BUFFER_FMT_ARGB,
!this->seperateCursor,
this->seperateCursor,
true,
DIFFMAP_BLOCKSIZE_128X128,
(void **)&this->frameBuffer,
(void **)&this->diffMap,
&event
))
{
return false;
}
this->cursorEvents[0] = lgCreateEvent(true, 10);
mouseHook_install(on_mouseMove);
if (this->seperateCursor)
this->cursorEvents[1] = lgWrapEvent(event);
DEBUG_INFO("Cursor mode : %s", this->seperateCursor ? "decoupled" : "integrated");
Sleep(100);
if (!lgCreateThread("NvFBCPointer", pointerThread, NULL, &this->pointerThread))
{
DEBUG_ERROR("Failed to create the NvFBCPointer thread");
return false;
}
return true;
}
static void nvfbc_stop()
{
this->stop = true;
lgSignalEvent(this->cursorEvents[0]);
lgSignalEvent(this->frameEvent);
if (this->pointerThread)
{
lgJoinThread(this->pointerThread, NULL);
this->pointerThread = NULL;
}
}
static bool nvfbc_deinit()
{
mouseHook_remove();
if (this->cursorEvents[0])
{
lgFreeEvent(this->cursorEvents[0]);
this->cursorEvents[0] = NULL;
}
return true;
}
static void nvfbc_free()
{
NvFBCToSysRelease(&this->nvfbc);
if (this->frameEvent)
lgFreeEvent(this->frameEvent);
free(this);
this = NULL;
NvFBCFree();
}
static unsigned int nvfbc_getMaxFrameSize()
{
return this->maxWidth * this->maxHeight * 4;
}
static CaptureResult nvfbc_capture()
{
getDesktopSize(&this->width, &this->height);
NvFBCFrameGrabInfo grabInfo;
CaptureResult result = NvFBCToSysCapture(
this->nvfbc,
1000,
0, 0,
this->width,
this->height,
&grabInfo
);
if (result != CAPTURE_RESULT_OK)
return result;
bool changed = false;
const unsigned int h = (this->height + 127) / 128;
const unsigned int w = (this->width + 127) / 128;
for(unsigned int y = 0; y < h; ++y)
for(unsigned int x = 0; x < w; ++x)
if (this->diffMap[(y*w)+x])
{
changed = true;
break;
}
if (!changed)
return CAPTURE_RESULT_TIMEOUT;
memcpy(&this->grabInfo, &grabInfo, sizeof(grabInfo));
lgSignalEvent(this->frameEvent);
return CAPTURE_RESULT_OK;
}
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame)
{
if (!lgWaitEvent(this->frameEvent, 1000))
return CAPTURE_RESULT_TIMEOUT;
if (this->stop)
return CAPTURE_RESULT_REINIT;
frame->width = this->grabInfo.dwWidth;
frame->height = this->grabInfo.dwHeight;
frame->pitch = this->grabInfo.dwBufferWidth * 4;
frame->stride = this->grabInfo.dwBufferWidth;
#if 0
//NvFBC never sets bIsHDR so instead we check for any data in the alpha channel
//If there is data, it's HDR. This is clearly suboptimal
if (!this->grabInfo.bIsHDR)
for(int y = 0; y < frame->height; ++y)
for(int x = 0; x < frame->width; ++x)
{
int offset = (y * frame->pitch) + (x * 4);
if (this->frameBuffer[offset + 3])
{
this->grabInfo.bIsHDR = 1;
break;
}
}
#endif
frame->format = this->grabInfo.bIsHDR ? CAPTURE_FMT_RGBA10 : CAPTURE_FMT_BGRA;
return CAPTURE_RESULT_OK;
}
static CaptureResult nvfbc_getFrame(FrameBuffer * frame)
{
framebuffer_write(
frame,
this->frameBuffer,
this->grabInfo.dwHeight * this->grabInfo.dwBufferWidth * 4
);
return CAPTURE_RESULT_OK;
}
static int pointerThread(void * unused)
{
while(!this->stop)
{
LGEvent * events[2];
memcpy(&events, &this->cursorEvents, sizeof(LGEvent *) * 2);
if (!lgWaitEvents(events, this->seperateCursor ? 2 : 1, false, 1000))
continue;
if (this->stop)
break;
CaptureResult result;
CapturePointer pointer = { 0 };
if (this->seperateCursor && events[1])
{
void * data;
uint32_t size;
if (!this->getPointerBufferFn(&data, &size))
{
DEBUG_WARN("failed to get a pointer buffer");
continue;
}
result = NvFBCToSysGetCursor(this->nvfbc, &pointer, data, size);
if (result != CAPTURE_RESULT_OK)
{
DEBUG_WARN("NvFBCToSysGetCursor failed");
continue;
}
this->mouseVisible = pointer.visible;
this->mouseHotX = pointer.x;
this->mouseHotY = pointer.y;
}
if (events[0])
{
pointer.positionUpdate = true;
pointer.visible = this->mouseVisible;
pointer.x = this->mouseX - this->mouseHotX;
pointer.y = this->mouseY - this->mouseHotY;
}
this->postPointerBufferFn(pointer);
}
return 0;
}
struct CaptureInterface Capture_NVFBC =
{
.getName = nvfbc_getName,
.initOptions = nvfbc_initOptions,
.create = nvfbc_create,
.init = nvfbc_init,
.stop = nvfbc_stop,
.deinit = nvfbc_deinit,
.free = nvfbc_free,
.getMaxFrameSize = nvfbc_getMaxFrameSize,
.capture = nvfbc_capture,
.waitFrame = nvfbc_waitFrame,
.getFrame = nvfbc_getFrame
};

View File

@@ -0,0 +1,330 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2020 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wrapper.h"
#include "common/windebug.h"
#include <windows.h>
#include <NvFBC/nvFBCToSys.h>
#ifdef _WIN64
#define NVFBC_DLL "NvFBC64.dll"
#else
#define NVFBC_DLL "NvFBC.dll"
#endif
struct NVAPI
{
bool initialized;
HMODULE dll;
NvFBC_CreateFunctionExType createEx;
NvFBC_SetGlobalFlagsType setGlobalFlags;
NvFBC_GetStatusExFunctionType getStatusEx;
NvFBC_EnableFunctionType enable;
NvFBC_GetSDKVersionFunctionType getVersion;
};
struct stNvFBCHandle
{
NvFBCToSys * nvfbc;
HANDLE cursorEvent;
int retry;
};
static NVAPI nvapi;
bool NvFBCInit()
{
if (nvapi.initialized)
return true;
nvapi.dll = LoadLibraryA(NVFBC_DLL);
if (!nvapi.dll)
{
DEBUG_WINERROR("Failed to load " NVFBC_DLL, GetLastError());
return false;
}
nvapi.createEx = (NvFBC_CreateFunctionExType )GetProcAddress(nvapi.dll, "NvFBC_CreateEx" );
nvapi.setGlobalFlags = (NvFBC_SetGlobalFlagsType )GetProcAddress(nvapi.dll, "NvFBC_SetGlobalFlags");
nvapi.getStatusEx = (NvFBC_GetStatusExFunctionType )GetProcAddress(nvapi.dll, "NvFBC_GetStatusEx" );
nvapi.enable = (NvFBC_EnableFunctionType )GetProcAddress(nvapi.dll, "NvFBC_Enable" );
nvapi.getVersion = (NvFBC_GetSDKVersionFunctionType)GetProcAddress(nvapi.dll, "NvFBC_GetSDKVersion" );
if (
!nvapi.createEx ||
!nvapi.setGlobalFlags ||
!nvapi.getStatusEx ||
!nvapi.enable ||
!nvapi.getVersion)
{
DEBUG_ERROR("Failed to get the required proc addresses");
return false;
}
NvU32 version;
if (nvapi.getVersion(&version) != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to get the NvFBC SDK version");
return false;
}
DEBUG_INFO("NvFBC SDK Version: %lu", version);
if (nvapi.enable(NVFBC_STATE_ENABLE) != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to enable the NvFBC interface");
return false;
}
nvapi.initialized = true;
return true;
}
void NvFBCFree()
{
if (!nvapi.initialized)
return;
FreeLibrary(nvapi.dll);
nvapi.initialized = false;
}
bool NvFBCToSysCreate(
void * privData,
unsigned int privDataSize,
NvFBCHandle * handle,
unsigned int * maxWidth,
unsigned int * maxHeight
)
{
NvFBCCreateParams params = {0};
params.dwVersion = NVFBC_CREATE_PARAMS_VER;
params.dwInterfaceType = NVFBC_TO_SYS;
params.pDevice = NULL;
params.dwAdapterIdx = 0;
params.dwPrivateDataSize = privDataSize;
params.pPrivateData = privData;
if (nvapi.createEx(&params) != NVFBC_SUCCESS)
{
*handle = NULL;
return false;
}
*handle = (NvFBCHandle)calloc(sizeof(struct stNvFBCHandle), 1);
(*handle)->nvfbc = static_cast<NvFBCToSys *>(params.pNvFBC);
if (maxWidth)
*maxWidth = params.dwMaxDisplayWidth;
if (maxHeight)
*maxHeight = params.dwMaxDisplayHeight;
return true;
}
void NvFBCToSysRelease(NvFBCHandle * handle)
{
if (!*handle)
return;
(*handle)->nvfbc->NvFBCToSysRelease();
free(*handle);
*handle = NULL;
}
bool NvFBCToSysSetup(
NvFBCHandle handle,
enum BufferFormat format,
bool hwCursor,
bool seperateCursorCapture,
bool useDiffMap,
enum DiffMapBlockSize diffMapBlockSize,
void ** frameBuffer,
void ** diffMap,
HANDLE * cursorEvent
)
{
NVFBC_TOSYS_SETUP_PARAMS params = {0};
params.dwVersion = NVFBC_TOSYS_SETUP_PARAMS_VER;
switch(format)
{
case BUFFER_FMT_ARGB : params.eMode = NVFBC_TOSYS_ARGB ; break;
case BUFFER_FMT_RGB : params.eMode = NVFBC_TOSYS_RGB ; break;
case BUFFER_FMT_YYYYUV420p: params.eMode = NVFBC_TOSYS_YYYYUV420p; break;
case BUFFER_FMT_RGB_PLANAR: params.eMode = NVFBC_TOSYS_RGB_PLANAR; break;
case BUFFER_FMT_XOR : params.eMode = NVFBC_TOSYS_XOR ; break;
case BUFFER_FMT_YUV444p : params.eMode = NVFBC_TOSYS_YUV444p ; break;
case BUFFER_FMT_ARGB10 :
params.eMode = NVFBC_TOSYS_ARGB10;
params.bHDRRequest = TRUE;
break;
default:
DEBUG_INFO("Invalid format");
return false;
}
params.bWithHWCursor = hwCursor ? TRUE : FALSE;
params.bEnableSeparateCursorCapture = seperateCursorCapture ? TRUE : FALSE;
params.bDiffMap = useDiffMap ? TRUE : FALSE;
switch(diffMapBlockSize)
{
case DIFFMAP_BLOCKSIZE_128X128: params.eDiffMapBlockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_128X128; break;
case DIFFMAP_BLOCKSIZE_16X16 : params.eDiffMapBlockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_16X16 ; break;
case DIFFMAP_BLOCKSIZE_32X32 : params.eDiffMapBlockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_32X32 ; break;
case DIFFMAP_BLOCKSIZE_64X64 : params.eDiffMapBlockSize = NVFBC_TOSYS_DIFFMAP_BLOCKSIZE_64X64 ; break;
default:
DEBUG_ERROR("Invalid diffMapBlockSize");
return false;
}
params.ppBuffer = frameBuffer;
params.ppDiffMap = diffMap;
NVFBCRESULT status = handle->nvfbc->NvFBCToSysSetUp(&params);
if (status != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to setup NVFBCToSys");
return false;
}
if (cursorEvent)
*cursorEvent = params.hCursorCaptureEvent;
return true;
}
CaptureResult NvFBCToSysCapture(
NvFBCHandle handle,
const unsigned int waitTime,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height,
NvFBCFrameGrabInfo * grabInfo
)
{
NVFBC_TOSYS_GRAB_FRAME_PARAMS params = {0};
params.dwVersion = NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER;
params.dwFlags = NVFBC_TOSYS_WAIT_WITH_TIMEOUT;
params.dwWaitTime = waitTime;
params.eGMode = NVFBC_TOSYS_SOURCEMODE_CROP;
params.dwStartX = x;
params.dwStartY = y;
params.dwTargetWidth = width;
params.dwTargetHeight = height;
params.pNvFBCFrameGrabInfo = grabInfo;
grabInfo->bMustRecreate = FALSE;
NVFBCRESULT status = handle->nvfbc->NvFBCToSysGrabFrame(&params);
if (grabInfo->bMustRecreate)
{
DEBUG_INFO("NvFBC reported recreation is required");
return CAPTURE_RESULT_REINIT;
}
switch(status)
{
case NVFBC_SUCCESS:
handle->retry = 0;
break;
case NVFBC_ERROR_INVALID_PARAM:
if (handle->retry < 2)
{
Sleep(100);
++handle->retry;
return CAPTURE_RESULT_TIMEOUT;
}
return CAPTURE_RESULT_ERROR;
case NVFBC_ERROR_DYNAMIC_DISABLE:
DEBUG_ERROR("NvFBC was disabled by someone else");
return CAPTURE_RESULT_ERROR;
case NVFBC_ERROR_INVALIDATED_SESSION:
DEBUG_WARN("Session was invalidated, attempting to restart");
return CAPTURE_RESULT_REINIT;
default:
DEBUG_ERROR("Unknown NVFBCRESULT failure 0x%x", status);
return CAPTURE_RESULT_ERROR;
}
return CAPTURE_RESULT_OK;
}
CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer, void * buffer, unsigned int size)
{
NVFBC_CURSOR_CAPTURE_PARAMS params;
params.dwVersion = NVFBC_CURSOR_CAPTURE_PARAMS_VER;
if (handle->nvfbc->NvFBCToSysCursorCapture(&params) != NVFBC_SUCCESS)
{
DEBUG_ERROR("Failed to get the cursor");
return CAPTURE_RESULT_ERROR;
}
pointer->x = params.dwXHotSpot;
pointer->y = params.dwYHotSpot;
pointer->width = params.dwWidth;
pointer->height = params.dwHeight;
pointer->pitch = params.dwPitch;
pointer->visible = params.bIsHwCursor;
pointer->shapeUpdate = params.bIsHwCursor;
if (!params.bIsHwCursor)
return CAPTURE_RESULT_OK;
switch(params.dwPointerFlags & 0x7)
{
case 0x1:
pointer->format = CAPTURE_FMT_MONO;
pointer->height *= 2;
break;
case 0x2:
pointer->format = CAPTURE_FMT_COLOR;
break;
case 0x4:
pointer->format = CAPTURE_FMT_MASKED;
break;
default:
DEBUG_ERROR("Invalid/unknown pointer data format");
return CAPTURE_RESULT_ERROR;
}
if (params.dwBufferSize > size)
{
DEBUG_WARN("Cursor data larger then provided buffer");
params.dwBufferSize = size;
}
memcpy(buffer, params.pBits, params.dwBufferSize);
return CAPTURE_RESULT_OK;
}

View File

@@ -0,0 +1,88 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdbool.h>
#include <NvFBC/nvFBC.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "interface/capture.h"
typedef struct stNvFBCHandle * NvFBCHandle;
enum BufferFormat
{
BUFFER_FMT_ARGB,
BUFFER_FMT_RGB,
BUFFER_FMT_YYYYUV420p,
BUFFER_FMT_RGB_PLANAR,
BUFFER_FMT_XOR,
BUFFER_FMT_YUV444p,
BUFFER_FMT_ARGB10
};
enum DiffMapBlockSize
{
DIFFMAP_BLOCKSIZE_128X128 = 0,
DIFFMAP_BLOCKSIZE_16X16,
DIFFMAP_BLOCKSIZE_32X32,
DIFFMAP_BLOCKSIZE_64X64
};
bool NvFBCInit();
void NvFBCFree();
bool NvFBCToSysCreate(
void * privData,
unsigned int privDataSize,
NvFBCHandle * handle,
unsigned int * maxWidth,
unsigned int * maxHeight
);
void NvFBCToSysRelease(NvFBCHandle * handle);
bool NvFBCToSysSetup(
NvFBCHandle handle,
enum BufferFormat format,
bool hwCursor,
bool seperateCursorCapture,
bool useDiffMap,
enum DiffMapBlockSize diffMapBlockSize,
void ** frameBuffer,
void ** diffMap,
HANDLE * cursorEvent
);
CaptureResult NvFBCToSysCapture(
NvFBCHandle handle,
const unsigned int waitTime,
const unsigned int x,
const unsigned int y,
const unsigned int width,
const unsigned int height,
NvFBCFrameGrabInfo * grabInfo
);
CaptureResult NvFBCToSysGetCursor(NvFBCHandle handle, CapturePointer * pointer, void * buffer, unsigned int size);
#ifdef __cplusplus
}
#endif