looking-glass/host/platform/Windows/capture/D12/backend/dd.c

875 lines
22 KiB
C

/**
* Looking Glass
* Copyright © 2017-2024 The Looking Glass Authors
* https://looking-glass.io
*
* 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 "backend.h"
#include "d12.h"
#include "com_ref.h"
#include "common/debug.h"
#include "common/windebug.h"
#include "common/array.h"
#include <d3d11.h>
#include <d3d11_4.h>
#include <dxgi.h>
#include <dxgi1_2.h>
#include <dxgi1_3.h>
#include <dxgi1_5.h>
#include <dxgi1_6.h>
#define CACHE_SIZE 10
typedef struct DDCacheInfo
{
D3D11_TEXTURE2D_DESC format;
/* this value is likely released, only used to check if the texture supplied
by DD is different, do not rely on it pointing to valid memory! */
ID3D11Texture2D * srcTex;
ID3D12Resource ** d12Res;
ID3D11Fence ** fence;
ID3D12Fence ** d12Fence;
UINT64 fenceValue;
bool ready;
RECT dirtyRects[D12_MAX_DIRTY_RECTS];
unsigned nbDirtyRects;
}
DDCacheInfo;
typedef struct DDInstance
{
D12Backend base;
HDESK desktop;
ID3D12Device3 ** d12device;
ID3D11Device5 ** device;
ID3D11DeviceContext4 ** context;
IDXGIOutputDuplication ** dup;
CaptureRotation rotation;
DXGI_COLOR_SPACE_TYPE colorSpace;
bool release;
DDCacheInfo cache[CACHE_SIZE];
DDCacheInfo * current;
bool lastPosValid;
DXGI_OUTDUPL_POINTER_POSITION lastPos;
void * shapeBuffer;
unsigned shapeBufferSize;
}
DDInstance;
static void d12_dd_openDesktop(DDInstance * this);
static bool d12_dd_handleFrameUpdate(DDInstance * this, IDXGIResource * res);
static void d12_dd_handlePointerMovement(DDInstance * this,
DXGI_OUTDUPL_POINTER_POSITION * pos, CapturePointer * pointer, bool * changed);
static void d12_dd_handlePointerShape(DDInstance * this,
CapturePointer * pointer, size_t size, bool * changed);
static bool d12_dd_getCache(DDInstance * this,
ID3D11Texture2D * srcTex, DDCacheInfo ** result);
static bool d12_dd_convertResource(DDInstance * this,
ID3D11Texture2D * srcTex, DDCacheInfo * cache);
static bool d12_dd_create(D12Backend ** instance, unsigned frameBuffers)
{
DDInstance * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("out of memory");
return false;
}
*instance = &this->base;
return true;
}
static bool d12_dd_init(
D12Backend * instance,
bool debug,
ID3D12Device3 * device,
IDXGIAdapter1 * adapter,
IDXGIOutput * output)
{
DDInstance * this = UPCAST(DDInstance, instance);
bool result = false;
HRESULT hr;
comRef_scopePush(10);
// try to open the desktop so we can capture the secure desktop
d12_dd_openDesktop(this);
comRef_defineLocal(IDXGIAdapter, _adapter);
hr = IDXGIAdapter1_QueryInterface(
adapter, &IID_IDXGIAdapter, (void **)_adapter);
if (FAILED(hr))
{
DEBUG_ERROR("Failed to get the IDXGIAdapter interface");
goto exit;
}
static const D3D_FEATURE_LEVEL featureLevels[] =
{
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
};
D3D_FEATURE_LEVEL featureLevel;
// create a DirectX11 context
comRef_defineLocal(ID3D11Device , d11device);
comRef_defineLocal(ID3D11DeviceContext, d11context);
hr = D3D11CreateDevice(
*_adapter,
D3D_DRIVER_TYPE_UNKNOWN,
NULL,
D3D11_CREATE_DEVICE_VIDEO_SUPPORT |
(debug ? D3D11_CREATE_DEVICE_DEBUG : 0),
featureLevels,
ARRAY_LENGTH(featureLevels),
D3D11_SDK_VERSION,
d11device,
&featureLevel,
d11context);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the D3D11Device", hr);
goto exit;
}
// get the updated interfaces
comRef_defineLocal(ID3D11DeviceContext4, d11context4);
hr = ID3D11DeviceContext_QueryInterface(
*d11context, &IID_ID3D11DeviceContext4, (void **)d11context4);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to get the ID3D11Context4 interface", hr);
goto exit;
}
comRef_defineLocal(ID3D11Device5, d11device5);
hr = ID3D11Device_QueryInterface(
*d11device, &IID_ID3D11Device5, (void **)d11device5);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to get the ID3D11Device5 interface", hr);
goto exit;
}
// try to reduce the latency
comRef_defineLocal(IDXGIDevice1, dxgi1);
hr = ID3D11Device_QueryInterface(
*d11device, &IID_IDXGIDevice1, (void **)dxgi1);
if (FAILED(hr))
{
DEBUG_WINERROR("failed to query the DXGI interface from the device", hr);
goto exit;
}
IDXGIDevice1_SetMaximumFrameLatency(*dxgi1, 1);
// duplicate the output
comRef_defineLocal(IDXGIOutput5 , output5);
comRef_defineLocal(IDXGIOutputDuplication, dup );
hr = IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput5, (void **)output5);
if (FAILED(hr))
{
DEBUG_WARN("IDXGIOutput5 is not available, "
"please update windows for improved performance!");
DEBUG_WARN("Falling back to IDXGIOutput1");
comRef_defineLocal(IDXGIOutput1, output1);
hr = IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput1, (void **)output1);
if (FAILED(hr))
{
DEBUG_ERROR("Failed to query IDXGIOutput1 from the output");
goto exit;
}
// we try this twice in case we still get an error on re-initialization
for (int i = 0; i < 2; ++i)
{
hr = IDXGIOutput1_DuplicateOutput(*output1, *(IUnknown **)d11device, dup);
if (SUCCEEDED(hr))
break;
Sleep(200);
}
}
else
{
static const DXGI_FORMAT supportedFormats[] =
{
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_R16G16B16A16_FLOAT
};
// we try this twice in case we still get an error on re-initialization
for (int i = 0; i < 2; ++i)
{
hr = IDXGIOutput5_DuplicateOutput1(
*output5,
*(IUnknown **)d11device,
0,
ARRAY_LENGTH(supportedFormats),
supportedFormats,
dup);
if (SUCCEEDED(hr))
break;
// if access is denied we just keep trying until it isn't
if (hr == E_ACCESSDENIED)
--i;
Sleep(200);
}
}
if (FAILED(hr))
{
DEBUG_WINERROR("DuplicateOutput Failed", hr);
goto exit;
}
DXGI_OUTDUPL_DESC dupDesc;
IDXGIOutputDuplication_GetDesc(*dup, &dupDesc);
switch(dupDesc.Rotation)
{
case DXGI_MODE_ROTATION_UNSPECIFIED:
case DXGI_MODE_ROTATION_IDENTITY:
this->rotation = CAPTURE_ROT_0;
break;
case DXGI_MODE_ROTATION_ROTATE90:
this->rotation = CAPTURE_ROT_90;
break;
case DXGI_MODE_ROTATION_ROTATE180:
this->rotation = CAPTURE_ROT_180;
break;
case DXGI_MODE_ROTATION_ROTATE270:
this->rotation = CAPTURE_ROT_270;
break;
}
comRef_defineLocal(IDXGIOutput6, output6);
hr = IDXGIOutput_QueryInterface(output, &IID_IDXGIOutput6, (void **)output6);
if (FAILED(hr))
this->colorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
else
{
DXGI_OUTPUT_DESC1 desc1;
IDXGIOutput6_GetDesc1(*output6, &desc1);
this->colorSpace = desc1.ColorSpace;
}
ID3D12Device3_AddRef(device);
comRef_toGlobal(this->d12device, &device );
comRef_toGlobal(this->device , d11device5 );
comRef_toGlobal(this->context , d11context4);
comRef_toGlobal(this->dup , dup );
result = true;
exit:
comRef_scopePop();
return result;
}
static bool d12_dd_deinit(D12Backend * instance)
{
DDInstance * this = UPCAST(DDInstance, instance);
if (this->release)
{
IDXGIOutputDuplication_ReleaseFrame(*this->dup);
this->release = false;
}
if (this->desktop)
{
CloseDesktop(this->desktop);
this->desktop = NULL;
}
memset(this->cache, 0, sizeof(this->cache));
return true;
}
static void d12_dd_free(D12Backend ** instance)
{
DDInstance * this = UPCAST(DDInstance, *instance);
free(this->shapeBuffer);
free(this);
*instance = NULL;
}
static CaptureResult d12_dd_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:
case DXGI_ERROR_INVALID_CALL:
return CAPTURE_RESULT_REINIT;
default:
return CAPTURE_RESULT_ERROR;
}
}
static CaptureResult d12_dd_capture(D12Backend * instance,
unsigned frameBufferIndex)
{
DDInstance * this = UPCAST(DDInstance, instance);
HRESULT hr;
CaptureResult result = CAPTURE_RESULT_ERROR;
comRef_scopePush(10);
DXGI_OUTDUPL_FRAME_INFO frameInfo = {0};
comRef_defineLocal(IDXGIResource, res);
retry:
if (this->release)
{
IDXGIOutputDuplication_ReleaseFrame(*this->dup);
this->release = false;
}
hr = IDXGIOutputDuplication_AcquireNextFrame(
*this->dup, 1000, &frameInfo, res);
result = d12_dd_hResultToCaptureResult(hr);
if (result != CAPTURE_RESULT_OK)
{
if (result == CAPTURE_RESULT_ERROR)
DEBUG_WINERROR("AcquireNextFrame failed", hr);
if (hr == DXGI_ERROR_ACCESS_LOST)
{
hr = ID3D11Device5_GetDeviceRemovedReason(*this->device);
if (FAILED(hr))
{
DEBUG_WINERROR("Device Removed", hr);
result = CAPTURE_RESULT_ERROR;
}
}
goto exit;
}
this->release = true;
// if we have a new frame
if (frameInfo.LastPresentTime.QuadPart != 0)
if (!d12_dd_handleFrameUpdate(this, *res))
{
result = CAPTURE_RESULT_ERROR;
goto exit;
}
bool postPointer = false;
CapturePointer pointer = {0};
// if the pointer has moved
if (frameInfo.LastMouseUpdateTime.QuadPart != 0)
d12_dd_handlePointerMovement(this,
&frameInfo.PointerPosition, &pointer, &postPointer);
// if the pointer shape has changed
if (frameInfo.PointerShapeBufferSize > 0)
d12_dd_handlePointerShape(this,
&pointer, frameInfo.PointerShapeBufferSize, &postPointer);
if (postPointer)
d12_updatePointer(&pointer, this->shapeBuffer, this->shapeBufferSize);
// if this was not a frame update, go back and try again
if (frameInfo.LastPresentTime.QuadPart == 0)
{
comRef_release(res);
goto retry;
}
exit:
comRef_scopePop();
return result;
}
static CaptureResult d12_dd_sync(D12Backend * instance,
ID3D12CommandQueue * commandQueue)
{
DDInstance * this = UPCAST(DDInstance, instance);
if (!this->current)
return CAPTURE_RESULT_TIMEOUT;
DDCacheInfo * cache = this->current;
if (ID3D11Fence_GetCompletedValue(*cache->fence) < cache->fenceValue)
ID3D12CommandQueue_Wait(commandQueue, *cache->d12Fence, cache->fenceValue);
return CAPTURE_RESULT_OK;
}
static ID3D12Resource * d12_dd_fetch(D12Backend * instance,
unsigned frameBufferIndex, D12FrameDesc * desc)
{
DDInstance * this = UPCAST(DDInstance, instance);
if (!this->current)
return NULL;
desc->dirtyRects = this->current->dirtyRects;
desc->nbDirtyRects = this->current->nbDirtyRects;
desc->rotation = this->rotation;
desc->colorSpace = this->colorSpace;
ID3D12Resource_AddRef(*this->current->d12Res);
return *this->current->d12Res;
}
static void d12_dd_openDesktop(DDInstance * 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 the 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, install and run the Looking Glass host as a "
"service.");
DEBUG_INFO("looking-glass-host.exe InstallService");
}
}
static bool d12_dd_handleFrameUpdate(DDInstance * this, IDXGIResource * res)
{
bool result = false;
comRef_scopePush(1);
comRef_defineLocal(ID3D11Texture2D, srcTex);
HRESULT hr = IDXGIResource_QueryInterface(
res, &IID_ID3D11Texture2D, (void **)srcTex);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to obtain the D3D11Texture2D interface", hr);
goto exit;
}
if (!d12_dd_getCache(this, *srcTex, &this->current))
goto exit;
/**
* Even though we have not performed any copy/draw operations we still need to
* use a fence. Because we share this texture with DirectX12 it is able to
* read from it before the desktop duplication API has finished updating it.*/
++this->current->fenceValue;
ID3D11DeviceContext4_Signal(
*this->context, *this->current->fence, this->current->fenceValue);
// handle damage tracking
this->current->nbDirtyRects = 0;
if (this->base.trackDamage)
{
/* Get the frame damage, if there is too many damage rects, we disable
* damage tracking for the frame and assume full frame damage */
UINT requiredSize;
hr = IDXGIOutputDuplication_GetFrameDirtyRects(*this->dup,
sizeof(this->current->dirtyRects),
this->current->dirtyRects,
&requiredSize);
if (FAILED(hr))
{
if (hr != DXGI_ERROR_MORE_DATA)
{
DEBUG_WINERROR("GetFrameDirtyRects failed", hr);
goto exit;
}
}
else
{
unsigned nbDirtyRects = requiredSize / sizeof(*this->current->dirtyRects);
// if there is only one damage rect and it covers the entire frame
if (nbDirtyRects == 1 &&
this->current->dirtyRects[0].left == 0 &&
this->current->dirtyRects[0].top == 0 &&
this->current->dirtyRects[0].right == this->current->format.Width &&
this->current->dirtyRects[0].bottom == this->current->format.Height)
goto fullDamage;
this->current->nbDirtyRects = nbDirtyRects;
}
DXGI_OUTDUPL_MOVE_RECT moveRects[
(ARRAY_LENGTH(this->current->dirtyRects) - this->current->nbDirtyRects) / 2
];
hr = IDXGIOutputDuplication_GetFrameMoveRects(*this->dup,
sizeof(moveRects), moveRects, &requiredSize);
if (FAILED(hr))
{
this->current->nbDirtyRects = 0;
if (hr != DXGI_ERROR_MORE_DATA)
{
DEBUG_WINERROR("GetFrameMoveRects failed", hr);
goto exit;
}
}
/* Move rects are seemingly not generated on Windows 10, but incase it
* becomes a thing in the future we still need to implement this */
const unsigned moveRectCount = requiredSize / sizeof(*moveRects);
for(DXGI_OUTDUPL_MOVE_RECT *moveRect = moveRects; moveRect < moveRects +
moveRectCount; ++moveRect)
{
/* According to WebRTC source comments, the DirectX capture API may
* randomly return unmoved rects, which should be skipped to avoid
* unnecessary work */
if (moveRect->SourcePoint.x == moveRect->DestinationRect.left &&
moveRect->SourcePoint.y == moveRect->DestinationRect.top)
continue;
/* Add the source rect to the dirty array */
this->current->dirtyRects[this->current->nbDirtyRects++] = (RECT)
{
.left = moveRect->SourcePoint.x,
.top = moveRect->SourcePoint.y,
.right = moveRect->SourcePoint.x +
(moveRect->DestinationRect.right - moveRect->DestinationRect.left),
.bottom = moveRect->SourcePoint.y +
(moveRect->DestinationRect.bottom - moveRect->DestinationRect.top)
};
/* Add the destination rect to the dirty array */
this->current->dirtyRects[this->current->nbDirtyRects++] =
moveRect->DestinationRect;
}
}
fullDamage:
result = true;
exit:
comRef_scopePop();
return result;
}
static void d12_dd_handlePointerMovement(DDInstance * this,
DXGI_OUTDUPL_POINTER_POSITION * pos, CapturePointer * pointer, bool * changed)
{
bool setPos = false;
// if the last position is valid, check against it for changes
if (this->lastPosValid)
{
// update the position only if the pointer is visible and it has moved
if (pos->Visible && (
pos->Position.x != this->lastPos.Position.x ||
pos->Position.y != this->lastPos.Position.y))
setPos = true;
// if the visibillity has changed
if (pos->Visible != this->lastPos.Visible)
*changed = true;
}
else
{
// update the position only if the pointer is visible
setPos = pos->Visible;
// this is the first update, we need to send it
*changed = true;
}
pointer->visible = pos->Visible;
if (setPos)
{
pointer->positionUpdate = true;
pointer->x = pos->Position.x;
pointer->y = pos->Position.y;
*changed = true;
}
memcpy(&this->lastPos, pos, sizeof(*pos));
this->lastPosValid = true;
}
static void d12_dd_handlePointerShape(DDInstance * this,
CapturePointer * pointer, size_t size, bool * changed)
{
HRESULT hr;
DXGI_OUTDUPL_POINTER_SHAPE_INFO info;
retry:
if (this->shapeBufferSize < size)
{
free(this->shapeBuffer);
this->shapeBuffer = malloc(size);
if (!this->shapeBuffer)
{
DEBUG_ERROR("out of memory");
this->shapeBufferSize = 0;
return;
}
this->shapeBufferSize = size;
}
UINT s;
hr = IDXGIOutputDuplication_GetFramePointerShape(
*this->dup,
this->shapeBufferSize,
this->shapeBuffer,
&s,
&info);
if (FAILED(hr))
{
if (hr == DXGI_ERROR_MORE_DATA)
{
size = s;
goto retry;
}
DEBUG_WINERROR("Failed to get the pointer shape", hr);
return;
}
switch(info.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("Unsupporter cursor format");
return;
}
pointer->shapeUpdate = true;
pointer->width = info.Width;
pointer->height = info.Height;
pointer->pitch = info.Pitch;
pointer->hx = info.HotSpot.x;
pointer->hy = info.HotSpot.y;
*changed = true;
}
static bool d12_dd_getCache(DDInstance * this,
ID3D11Texture2D * srcTex, DDCacheInfo ** result)
{
*result = NULL;
D3D11_TEXTURE2D_DESC srcDesc;
ID3D11Texture2D_GetDesc(srcTex, &srcDesc);
unsigned freeSlot = CACHE_SIZE;
for(unsigned i = 0; i < CACHE_SIZE; ++i)
{
DDCacheInfo * cache = &this->cache[i];
if (!cache->ready)
{
freeSlot = min(freeSlot, i);
continue;
}
// check for a resource match
if (cache->srcTex != srcTex)
continue;
// check if the match is not valid
if (cache->format.Width != srcDesc.Width ||
cache->format.Height != srcDesc.Height ||
cache->format.Format != srcDesc.Format)
{
// break out and allow this entry to be rebuilt
cache->ready = false;
freeSlot = i;
break;
}
// found, so return it
*result = cache;
return true;
}
// cache is full
if (freeSlot == CACHE_SIZE)
return false;
// convert the resource
if (!d12_dd_convertResource(this, srcTex, &this->cache[freeSlot]))
return false;
// return the new cache entry
*result = &this->cache[freeSlot];
return true;
}
static bool d12_dd_convertResource(DDInstance * this,
ID3D11Texture2D * srcTex, DDCacheInfo * cache)
{
bool result = false;
HRESULT hr;
comRef_scopePush(10);
D3D11_TEXTURE2D_DESC srcDesc;
ID3D11Texture2D_GetDesc(srcTex, &srcDesc);
// get the DXGI resource interface so we can create the shared handle
comRef_defineLocal(IDXGIResource1, dxgiRes);
hr = ID3D11Texture2D_QueryInterface(
srcTex, &IID_IDXGIResource1, (void **)dxgiRes);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to obtain the shared ID3D11Resource1 interface", hr);
goto exit;
}
// create the shared handle
HANDLE sharedHandle;
hr = IDXGIResource1_CreateSharedHandle(
*dxgiRes, NULL, DXGI_SHARED_RESOURCE_READ, NULL, &sharedHandle);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the shared handle", hr);
goto exit;
}
// open the resource as a DirectX12 resource
comRef_defineLocal(ID3D12Resource, dst);
hr = ID3D12Device3_OpenSharedHandle(
*this->d12device, sharedHandle, &IID_ID3D12Resource, (void **)dst);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to open the D3D12Resource from the handle", hr);
CloseHandle(sharedHandle);
goto exit;
}
// close the shared handle
CloseHandle(sharedHandle);
// create the sync fence
comRef_defineLocal(ID3D11Fence, fence);
hr = ID3D11Device5_CreateFence(
*this->device, 0, D3D11_FENCE_FLAG_SHARED, &IID_ID3D11Fence, (void **)fence);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the fence", hr);
goto exit;
}
// create the fence shared handle
hr = ID3D11Fence_CreateSharedHandle(
*fence, NULL, GENERIC_ALL, NULL, &sharedHandle);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the fence shared handle", hr);
goto exit;
}
// open the fence as a DirectX12 fence
comRef_defineLocal(ID3D12Fence, d12Fence);
hr = ID3D12Device3_OpenSharedHandle(
*this->d12device, sharedHandle, &IID_ID3D12Fence, (void **)d12Fence);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to open the D3D12Fence from the handle", hr);
CloseHandle(sharedHandle);
goto exit;
}
// close the shared handle
CloseHandle(sharedHandle);
// store the details
cache->srcTex = srcTex;
comRef_toGlobal(cache->d12Res , dst );
comRef_toGlobal(cache->fence , fence );
comRef_toGlobal(cache->d12Fence, d12Fence);
memcpy(&cache->format, &srcDesc, sizeof(srcDesc));
cache->fenceValue = 0;
cache->ready = true;
result = true;
exit:
comRef_scopePop();
return result;
}
const D12Backend D12Backend_DD =
{
.name = "Desktop Duplication",
.codeName = "DD",
.create = d12_dd_create,
.init = d12_dd_init,
.deinit = d12_dd_deinit,
.free = d12_dd_free,
.capture = d12_dd_capture,
.sync = d12_dd_sync,
.fetch = d12_dd_fetch
};