mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-25 14:57:20 +00:00
[host] dxgi: refactor to support additional copy backends
This commit is contained in:
parent
36892839f3
commit
137171a8a2
@ -3,6 +3,8 @@ project(capture_DXGI LANGUAGES C)
|
|||||||
|
|
||||||
add_library(capture_DXGI STATIC
|
add_library(capture_DXGI STATIC
|
||||||
src/dxgi.c
|
src/dxgi.c
|
||||||
|
src/d3d11.c
|
||||||
|
src/util.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions("-DCOBJMACROS -DINITGUID")
|
add_definitions("-DCOBJMACROS -DINITGUID")
|
||||||
|
198
host/platform/Windows/capture/DXGI/src/d3d11.c
Normal file
198
host/platform/Windows/capture/DXGI/src/d3d11.c
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2021 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 "dxgi_capture.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/runningavg.h"
|
||||||
|
#include "common/windebug.h"
|
||||||
|
|
||||||
|
struct D3D11Backend
|
||||||
|
{
|
||||||
|
RunningAvg avgMapTime;
|
||||||
|
uint64_t usleepMapTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct DXGIInterface * dxgi = NULL;
|
||||||
|
static struct D3D11Backend * this = NULL;
|
||||||
|
|
||||||
|
static void d3d11_free(void);
|
||||||
|
|
||||||
|
static bool d3d11_create(struct DXGIInterface * intf)
|
||||||
|
{
|
||||||
|
HRESULT status;
|
||||||
|
dxgi = intf;
|
||||||
|
|
||||||
|
assert(!this);
|
||||||
|
this = calloc(1, sizeof(struct D3D11Backend));
|
||||||
|
if (!this)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("failed to allocate D3D11Backend struct");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->avgMapTime = runningavg_new(10);
|
||||||
|
|
||||||
|
D3D11_TEXTURE2D_DESC texDesc;
|
||||||
|
memset(&texDesc, 0, sizeof(texDesc));
|
||||||
|
texDesc.Width = dxgi->width;
|
||||||
|
texDesc.Height = dxgi->height;
|
||||||
|
texDesc.MipLevels = 1;
|
||||||
|
texDesc.ArraySize = 1;
|
||||||
|
texDesc.SampleDesc.Count = 1;
|
||||||
|
texDesc.SampleDesc.Quality = 0;
|
||||||
|
texDesc.Usage = D3D11_USAGE_STAGING;
|
||||||
|
texDesc.Format = dxgi->dxgiFormat;
|
||||||
|
texDesc.BindFlags = 0;
|
||||||
|
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||||
|
texDesc.MiscFlags = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < dxgi->maxTextures; ++i)
|
||||||
|
{
|
||||||
|
status = ID3D11Device_CreateTexture2D(dxgi->device, &texDesc, NULL, (ID3D11Texture2D **)&dxgi->texture[i].impl);
|
||||||
|
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(dxgi->deviceContext, (ID3D11Resource *)dxgi->texture[0].impl, 0, D3D11_MAP_READ, 0, &mapping);
|
||||||
|
if (FAILED(status))
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("Failed to map the texture", status);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
dxgi->pitch = mapping.RowPitch;
|
||||||
|
dxgi->stride = mapping.RowPitch / dxgi->bpp;
|
||||||
|
ID3D11DeviceContext_Unmap(dxgi->deviceContext, (ID3D11Resource *)dxgi->texture[0].impl, 0);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
d3d11_free();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void d3d11_free(void)
|
||||||
|
{
|
||||||
|
assert(this);
|
||||||
|
|
||||||
|
for (int i = 0; i < dxgi->maxTextures; ++i)
|
||||||
|
if (dxgi->texture[i].impl)
|
||||||
|
ID3D11Texture2D_Release((ID3D11Texture2D *) dxgi->texture[i].impl);
|
||||||
|
|
||||||
|
runningavg_free(&this->avgMapTime);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool d3d11_copyFrame(Texture * tex, ID3D11Texture2D * src)
|
||||||
|
{
|
||||||
|
INTERLOCKED_SECTION(dxgi->deviceContextLock,
|
||||||
|
{
|
||||||
|
tex->copyTime = microtime();
|
||||||
|
|
||||||
|
if (tex->texDamageCount < 0)
|
||||||
|
ID3D11DeviceContext_CopyResource(dxgi->deviceContext,
|
||||||
|
(ID3D11Resource *)tex->impl, (ID3D11Resource *)src);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tex->texDamageCount; ++i)
|
||||||
|
{
|
||||||
|
FrameDamageRect * rect = tex->texDamageRects + i;
|
||||||
|
D3D11_BOX box = {
|
||||||
|
.left = rect->x, .top = rect->y, .front = 0, .back = 1,
|
||||||
|
.right = rect->x + rect->width, .bottom = rect->y + rect->height,
|
||||||
|
};
|
||||||
|
ID3D11DeviceContext_CopySubresourceRegion(dxgi->deviceContext,
|
||||||
|
(ID3D11Resource *)tex->impl, 0, rect->x, rect->y, 0,
|
||||||
|
(ID3D11Resource *)src, 0, &box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D11DeviceContext_Flush(dxgi->deviceContext);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CaptureResult d3d11_mapTexture(Texture * tex)
|
||||||
|
{
|
||||||
|
D3D11_MAPPED_SUBRESOURCE map;
|
||||||
|
|
||||||
|
// sleep until it's close to time to map
|
||||||
|
const uint64_t delta = microtime() - tex->copyTime;
|
||||||
|
if (delta < this->usleepMapTime)
|
||||||
|
usleep(this->usleepMapTime - delta);
|
||||||
|
|
||||||
|
// try to map the resource, but don't wait for it
|
||||||
|
for (int i = 0; ; ++i)
|
||||||
|
{
|
||||||
|
HRESULT status;
|
||||||
|
|
||||||
|
INTERLOCKED_SECTION(dxgi->deviceContextLock, {
|
||||||
|
status = ID3D11DeviceContext_Map(dxgi->deviceContext, (ID3D11Resource *) tex->impl,
|
||||||
|
0, D3D11_MAP_READ, 0x100000L, &map);
|
||||||
|
});
|
||||||
|
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
|
||||||
|
{
|
||||||
|
if (i == 100)
|
||||||
|
return CAPTURE_RESULT_TIMEOUT;
|
||||||
|
|
||||||
|
usleep(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(status))
|
||||||
|
{
|
||||||
|
DEBUG_WINERROR("Failed to map the texture", status);
|
||||||
|
return CAPTURE_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tex->map = map.pData;
|
||||||
|
|
||||||
|
// update the sleep average and sleep for 80% of the average on the next call
|
||||||
|
runningavg_push(this->avgMapTime, microtime() - tex->copyTime);
|
||||||
|
this->usleepMapTime = (uint64_t)(runningavg_calc(this->avgMapTime) * 0.8);
|
||||||
|
return CAPTURE_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void d3d11_unmapTexture(Texture * tex)
|
||||||
|
{
|
||||||
|
INTERLOCKED_SECTION(dxgi->deviceContextLock, {
|
||||||
|
ID3D11DeviceContext_Unmap(dxgi->deviceContext, (ID3D11Resource *) tex->impl, 0);
|
||||||
|
});
|
||||||
|
tex->map = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DXGICopyBackend copyBackendD3D11 = {
|
||||||
|
.name = "Direct3D 11",
|
||||||
|
.create = d3d11_create,
|
||||||
|
.free = d3d11_free,
|
||||||
|
.copyFrame = d3d11_copyFrame,
|
||||||
|
.mapTexture = d3d11_mapTexture,
|
||||||
|
.unmapTexture = d3d11_unmapTexture,
|
||||||
|
};
|
@ -36,89 +36,23 @@
|
|||||||
#include <dxgi1_2.h>
|
#include <dxgi1_2.h>
|
||||||
#include <dxgi1_5.h>
|
#include <dxgi1_5.h>
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
|
#include <d3d12.h>
|
||||||
#include <d3dcommon.h>
|
#include <d3dcommon.h>
|
||||||
#include <versionhelpers.h>
|
#include <versionhelpers.h>
|
||||||
#include <dwmapi.h>
|
#include <dwmapi.h>
|
||||||
|
|
||||||
#include "dxgi_extra.h"
|
#include "dxgi_capture.h"
|
||||||
|
|
||||||
#define LOCKED(...) INTERLOCKED_SECTION(this->deviceContextLock, __VA_ARGS__)
|
#define LOCKED(...) INTERLOCKED_SECTION(this->deviceContextLock, __VA_ARGS__)
|
||||||
|
|
||||||
enum TextureState
|
|
||||||
{
|
|
||||||
TEXTURE_STATE_UNUSED,
|
|
||||||
TEXTURE_STATE_PENDING_MAP,
|
|
||||||
TEXTURE_STATE_MAPPED
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct Texture
|
|
||||||
{
|
|
||||||
unsigned int formatVer;
|
|
||||||
volatile enum TextureState state;
|
|
||||||
ID3D11Texture2D * tex;
|
|
||||||
D3D11_MAPPED_SUBRESOURCE map;
|
|
||||||
uint64_t copyTime;
|
|
||||||
uint32_t damageRectsCount;
|
|
||||||
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
|
|
||||||
int32_t texDamageCount;
|
|
||||||
FrameDamageRect texDamageRects[KVMFR_MAX_DAMAGE_RECTS];
|
|
||||||
}
|
|
||||||
Texture;
|
|
||||||
|
|
||||||
struct FrameDamage
|
|
||||||
{
|
|
||||||
int count;
|
|
||||||
FrameDamageRect rects[KVMFR_MAX_DAMAGE_RECTS];
|
|
||||||
};
|
|
||||||
|
|
||||||
// locals
|
// locals
|
||||||
struct iface
|
static struct DXGIInterface * this = NULL;
|
||||||
{
|
|
||||||
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;
|
|
||||||
bool dwmFlush;
|
|
||||||
D3D_FEATURE_LEVEL featureLevel;
|
|
||||||
IDXGIOutputDuplication * dup;
|
|
||||||
int maxTextures;
|
|
||||||
Texture * texture;
|
|
||||||
int texRIndex;
|
|
||||||
int texWIndex;
|
|
||||||
atomic_int texReady;
|
|
||||||
bool needsRelease;
|
|
||||||
|
|
||||||
RunningAvg avgMapTime;
|
extern struct DXGICopyBackend copyBackendD3D11;
|
||||||
uint64_t usleepMapTime;
|
static struct DXGICopyBackend * backends[] = {
|
||||||
|
©BackendD3D11,
|
||||||
CaptureGetPointerBuffer getPointerBufferFn;
|
|
||||||
CapturePostPointerBuffer postPointerBufferFn;
|
|
||||||
LGEvent * frameEvent;
|
|
||||||
|
|
||||||
unsigned int formatVer;
|
|
||||||
unsigned int width;
|
|
||||||
unsigned int height;
|
|
||||||
unsigned int pitch;
|
|
||||||
unsigned int stride;
|
|
||||||
CaptureFormat format;
|
|
||||||
CaptureRotation rotation;
|
|
||||||
|
|
||||||
int lastPointerX, lastPointerY;
|
|
||||||
bool lastPointerVisible;
|
|
||||||
|
|
||||||
struct FrameDamage frameDamage[LGMP_Q_FRAME_LEN];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct iface * this = NULL;
|
|
||||||
|
|
||||||
// forwards
|
// forwards
|
||||||
|
|
||||||
static bool dxgi_deinit();
|
static bool dxgi_deinit();
|
||||||
@ -182,7 +116,7 @@ static bool dxgi_create(CaptureGetPointerBuffer getPointerBufferFn, CapturePostP
|
|||||||
this = calloc(1, sizeof(*this));
|
this = calloc(1, sizeof(*this));
|
||||||
if (!this)
|
if (!this)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("failed to allocate iface struct");
|
DEBUG_ERROR("failed to allocate DXGIInterface struct");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +137,6 @@ static bool dxgi_create(CaptureGetPointerBuffer getPointerBufferFn, CapturePostP
|
|||||||
this->texture = calloc(this->maxTextures, sizeof(*this->texture));
|
this->texture = calloc(this->maxTextures, sizeof(*this->texture));
|
||||||
this->getPointerBufferFn = getPointerBufferFn;
|
this->getPointerBufferFn = getPointerBufferFn;
|
||||||
this->postPointerBufferFn = postPointerBufferFn;
|
this->postPointerBufferFn = postPointerBufferFn;
|
||||||
this->avgMapTime = runningavg_new(10);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,9 +173,6 @@ static bool dxgi_init(void)
|
|||||||
this->texWIndex = 0;
|
this->texWIndex = 0;
|
||||||
atomic_store(&this->texReady, 0);
|
atomic_store(&this->texReady, 0);
|
||||||
|
|
||||||
runningavg_reset(this->avgMapTime);
|
|
||||||
this->usleepMapTime = 0;
|
|
||||||
|
|
||||||
lgResetEvent(this->frameEvent);
|
lgResetEvent(this->frameEvent);
|
||||||
|
|
||||||
status = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&this->factory);
|
status = CreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&this->factory);
|
||||||
@ -534,9 +464,11 @@ static bool dxgi_init(void)
|
|||||||
|
|
||||||
DXGI_OUTDUPL_DESC dupDesc;
|
DXGI_OUTDUPL_DESC dupDesc;
|
||||||
IDXGIOutputDuplication_GetDesc(this->dup, &dupDesc);
|
IDXGIOutputDuplication_GetDesc(this->dup, &dupDesc);
|
||||||
DEBUG_INFO("Source Format : %s", GetDXGIFormatStr(dupDesc.ModeDesc.Format));
|
|
||||||
|
|
||||||
uint8_t bpp = 4;
|
this->dxgiFormat = dupDesc.ModeDesc.Format;
|
||||||
|
DEBUG_INFO("Source Format : %s", GetDXGIFormatStr(this->dxgiFormat));
|
||||||
|
|
||||||
|
this->bpp = 4;
|
||||||
switch(dupDesc.ModeDesc.Format)
|
switch(dupDesc.ModeDesc.Format)
|
||||||
{
|
{
|
||||||
case DXGI_FORMAT_B8G8R8A8_UNORM : this->format = CAPTURE_FMT_BGRA ; break;
|
case DXGI_FORMAT_B8G8R8A8_UNORM : this->format = CAPTURE_FMT_BGRA ; break;
|
||||||
@ -545,7 +477,7 @@ static bool dxgi_init(void)
|
|||||||
|
|
||||||
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
case DXGI_FORMAT_R16G16B16A16_FLOAT:
|
||||||
this->format = CAPTURE_FMT_RGBA16F;
|
this->format = CAPTURE_FMT_RGBA16F;
|
||||||
bpp = 8;
|
this->bpp = 8;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -553,42 +485,26 @@ static bool dxgi_init(void)
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
D3D11_TEXTURE2D_DESC texDesc;
|
for (int i = 0; i < ARRAY_LENGTH(backends); ++i)
|
||||||
memset(&texDesc, 0, sizeof(texDesc));
|
{
|
||||||
texDesc.Width = this->width;
|
if (backends[i]->create(this))
|
||||||
texDesc.Height = this->height;
|
{
|
||||||
texDesc.MipLevels = 1;
|
this->backend = backends[i];
|
||||||
texDesc.ArraySize = 1;
|
break;
|
||||||
texDesc.SampleDesc.Count = 1;
|
}
|
||||||
texDesc.SampleDesc.Quality = 0;
|
}
|
||||||
texDesc.Usage = D3D11_USAGE_STAGING;
|
|
||||||
texDesc.Format = dupDesc.ModeDesc.Format;
|
if (!this->backend)
|
||||||
texDesc.BindFlags = 0;
|
{
|
||||||
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
DEBUG_ERROR("Could not find a usable copy backend");
|
||||||
texDesc.MiscFlags = 0;
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO("Copy backend : %s", this->backend->name);
|
||||||
|
DEBUG_INFO("AcquireLock : %s", this->useAcquireLock ? "enabled" : "disabled");
|
||||||
|
|
||||||
for (int i = 0; i < this->maxTextures; ++i)
|
for (int i = 0; i < this->maxTextures; ++i)
|
||||||
{
|
|
||||||
this->texture[i].texDamageCount = -1;
|
this->texture[i].texDamageCount = -1;
|
||||||
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 / bpp;
|
|
||||||
ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource *)this->texture[0].tex, 0);
|
|
||||||
|
|
||||||
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||||
this->frameDamage[i].count = -1;
|
this->frameDamage[i].count = -1;
|
||||||
@ -612,21 +528,25 @@ static bool dxgi_deinit(void)
|
|||||||
{
|
{
|
||||||
DEBUG_ASSERT(this);
|
DEBUG_ASSERT(this);
|
||||||
|
|
||||||
|
for (int i = 0; i < this->maxTextures; ++i)
|
||||||
|
{
|
||||||
|
if (this->texture[i].map)
|
||||||
|
{
|
||||||
|
this->backend->unmapTexture(this->texture + i);
|
||||||
|
this->texture[i].map = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->backend)
|
||||||
|
{
|
||||||
|
this->backend->free();
|
||||||
|
this->backend = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < this->maxTextures; ++i)
|
for (int i = 0; i < this->maxTextures; ++i)
|
||||||
{
|
{
|
||||||
this->texture[i].state = TEXTURE_STATE_UNUSED;
|
this->texture[i].state = TEXTURE_STATE_UNUSED;
|
||||||
|
this->texture[i].impl = NULL;
|
||||||
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)
|
if (this->dup)
|
||||||
@ -692,8 +612,6 @@ static void dxgi_free(void)
|
|||||||
dxgi_deinit();
|
dxgi_deinit();
|
||||||
|
|
||||||
free(this->texture);
|
free(this->texture);
|
||||||
|
|
||||||
runningavg_free(&this->avgMapTime);
|
|
||||||
free(this);
|
free(this);
|
||||||
this = NULL;
|
this = NULL;
|
||||||
}
|
}
|
||||||
@ -792,6 +710,20 @@ static void computeFrameDamage(Texture * tex)
|
|||||||
tex->damageRectsCount = dirtyRectsCount + actuallyMovedRectsCount;
|
tex->damageRectsCount = dirtyRectsCount + actuallyMovedRectsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void computeTexDamage(Texture * tex)
|
||||||
|
{
|
||||||
|
if (tex->texDamageCount < 0 || tex->damageRectsCount == 0 ||
|
||||||
|
tex->texDamageCount + tex->damageRectsCount > KVMFR_MAX_DAMAGE_RECTS)
|
||||||
|
tex->texDamageCount = -1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(tex->texDamageRects + tex->texDamageCount, tex->damageRects,
|
||||||
|
tex->damageRectsCount * sizeof(FrameDamageRect));
|
||||||
|
tex->texDamageCount += tex->damageRectsCount;
|
||||||
|
tex->texDamageCount = rectsMergeOverlapping(tex->texDamageRects, tex->texDamageCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static CaptureResult dxgi_capture(void)
|
static CaptureResult dxgi_capture(void)
|
||||||
{
|
{
|
||||||
DEBUG_ASSERT(this);
|
DEBUG_ASSERT(this);
|
||||||
@ -880,58 +812,23 @@ static CaptureResult dxgi_capture(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (copyFrame || copyPointer)
|
if (copyFrame || copyPointer)
|
||||||
{
|
|
||||||
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
|
|
||||||
LOCKED(
|
|
||||||
{
|
{
|
||||||
if (copyFrame)
|
if (copyFrame)
|
||||||
{
|
{
|
||||||
|
if (this->useAcquireLock)
|
||||||
|
{
|
||||||
|
LOCKED({ computeFrameDamage(tex); });
|
||||||
|
}
|
||||||
|
else
|
||||||
computeFrameDamage(tex);
|
computeFrameDamage(tex);
|
||||||
|
computeTexDamage(tex);
|
||||||
|
|
||||||
if (tex->texDamageCount < 0 || tex->damageRectsCount == 0 ||
|
if (!this->backend->copyFrame(tex, src))
|
||||||
tex->texDamageCount + tex->damageRectsCount > KVMFR_MAX_DAMAGE_RECTS)
|
|
||||||
tex->texDamageCount = -1;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
memcpy(tex->texDamageRects + tex->texDamageCount, tex->damageRects,
|
ID3D11Texture2D_Release(src);
|
||||||
tex->damageRectsCount * sizeof(*tex->damageRects));
|
return CAPTURE_RESULT_ERROR;
|
||||||
tex->texDamageCount += tex->damageRectsCount;
|
|
||||||
tex->texDamageCount = rectsMergeOverlapping(tex->texDamageRects, tex->texDamageCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// issue the copy from GPU to CPU RAM
|
|
||||||
tex->copyTime = microtime();
|
|
||||||
if (tex->texDamageCount < 0)
|
|
||||||
ID3D11DeviceContext_CopyResource(this->deviceContext,
|
|
||||||
(ID3D11Resource *)tex->tex, (ID3D11Resource *)src);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (int i = 0; i < tex->texDamageCount; ++i)
|
|
||||||
{
|
|
||||||
FrameDamageRect * rect = tex->texDamageRects + i;
|
|
||||||
D3D11_BOX box = {
|
|
||||||
.left = rect->x, .top = rect->y, .front = 0, .back = 1,
|
|
||||||
.right = rect->x + rect->width, .bottom = rect->y + rect->height,
|
|
||||||
};
|
|
||||||
ID3D11DeviceContext_CopySubresourceRegion(this->deviceContext,
|
|
||||||
(ID3D11Resource *)tex->tex, 0, rect->x, rect->y, 0,
|
|
||||||
(ID3D11Resource *)src, 0, &box);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copyPointer)
|
|
||||||
{
|
|
||||||
// grab the pointer shape
|
|
||||||
status = IDXGIOutputDuplication_GetFramePointerShape(
|
|
||||||
this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3D11DeviceContext_Flush(this->deviceContext);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (copyFrame)
|
|
||||||
{
|
|
||||||
ID3D11Texture2D_Release(src);
|
ID3D11Texture2D_Release(src);
|
||||||
|
|
||||||
for (int i = 0; i < this->maxTextures; ++i)
|
for (int i = 0; i < this->maxTextures; ++i)
|
||||||
@ -966,6 +863,18 @@ static CaptureResult dxgi_capture(void)
|
|||||||
|
|
||||||
if (copyPointer)
|
if (copyPointer)
|
||||||
{
|
{
|
||||||
|
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
|
||||||
|
if (this->useAcquireLock)
|
||||||
|
{
|
||||||
|
LOCKED({
|
||||||
|
status = IDXGIOutputDuplication_GetFramePointerShape(
|
||||||
|
this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
status = IDXGIOutputDuplication_GetFramePointerShape(
|
||||||
|
this->dup, bufferSize, pointerShape, &pointerShapeSize, &shapeInfo);
|
||||||
|
|
||||||
result = dxgi_hResultToCaptureResult(status);
|
result = dxgi_hResultToCaptureResult(status);
|
||||||
if (result != CAPTURE_RESULT_OK)
|
if (result != CAPTURE_RESULT_OK)
|
||||||
{
|
{
|
||||||
@ -1046,37 +955,9 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameS
|
|||||||
|
|
||||||
Texture * tex = &this->texture[this->texRIndex];
|
Texture * tex = &this->texture[this->texRIndex];
|
||||||
|
|
||||||
// sleep until it's close to time to map
|
CaptureResult result = this->backend->mapTexture(tex);
|
||||||
const uint64_t delta = microtime() - tex->copyTime;
|
if (result != CAPTURE_RESULT_OK)
|
||||||
if (delta < this->usleepMapTime)
|
return result;
|
||||||
usleep(this->usleepMapTime - delta);
|
|
||||||
|
|
||||||
// try to map the resource, but don't wait for it
|
|
||||||
for (int i = 0; ; ++i)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (i == 100)
|
|
||||||
return CAPTURE_RESULT_TIMEOUT;
|
|
||||||
|
|
||||||
usleep(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(status))
|
|
||||||
{
|
|
||||||
DEBUG_WINERROR("Failed to map the texture", status);
|
|
||||||
return CAPTURE_RESULT_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the sleep average and sleep for 80% of the average on the next call
|
|
||||||
runningavg_push(this->avgMapTime, microtime() - tex->copyTime);
|
|
||||||
this->usleepMapTime = (uint64_t)(runningavg_calc(this->avgMapTime) * 0.8);
|
|
||||||
|
|
||||||
tex->state = TEXTURE_STATE_MAPPED;
|
tex->state = TEXTURE_STATE_MAPPED;
|
||||||
|
|
||||||
@ -1112,14 +993,14 @@ static CaptureResult dxgi_getFrame(FrameBuffer * frame,
|
|||||||
damage->count + tex->damageRectsCount > KVMFR_MAX_DAMAGE_RECTS;
|
damage->count + tex->damageRectsCount > KVMFR_MAX_DAMAGE_RECTS;
|
||||||
|
|
||||||
if (damageAll)
|
if (damageAll)
|
||||||
framebuffer_write(frame, tex->map.pData, this->pitch * height);
|
framebuffer_write(frame, tex->map, this->pitch * height);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy(damage->rects + damage->count, tex->damageRects,
|
memcpy(damage->rects + damage->count, tex->damageRects,
|
||||||
tex->damageRectsCount * sizeof(*tex->damageRects));
|
tex->damageRectsCount * sizeof(*tex->damageRects));
|
||||||
damage->count += tex->damageRectsCount;
|
damage->count += tex->damageRectsCount;
|
||||||
rectsBufferToFramebuffer(damage->rects, damage->count, frame, this->pitch,
|
rectsBufferToFramebuffer(damage->rects, damage->count, frame, this->pitch,
|
||||||
height, tex->map.pData, this->pitch);
|
height, tex->map, this->pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||||
@ -1138,7 +1019,7 @@ static CaptureResult dxgi_getFrame(FrameBuffer * frame,
|
|||||||
damage->count = -1;
|
damage->count = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOCKED({ID3D11DeviceContext_Unmap(this->deviceContext, (ID3D11Resource*)tex->tex, 0);});
|
this->backend->unmapTexture(tex);
|
||||||
tex->state = TEXTURE_STATE_UNUSED;
|
tex->state = TEXTURE_STATE_UNUSED;
|
||||||
|
|
||||||
if (++this->texRIndex == this->maxTextures)
|
if (++this->texRIndex == this->maxTextures)
|
||||||
|
118
host/platform/Windows/capture/DXGI/src/dxgi_capture.h
Normal file
118
host/platform/Windows/capture/DXGI/src/dxgi_capture.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2021 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 <stdint.h>
|
||||||
|
#include <dxgi.h>
|
||||||
|
#include <dxgi1_2.h>
|
||||||
|
#include <d3d11.h>
|
||||||
|
#include <d3dcommon.h>
|
||||||
|
|
||||||
|
#include "common/KVMFR.h"
|
||||||
|
#include "common/event.h"
|
||||||
|
#include "common/locking.h"
|
||||||
|
#include "common/types.h"
|
||||||
|
#include "interface/capture.h"
|
||||||
|
|
||||||
|
enum TextureState
|
||||||
|
{
|
||||||
|
TEXTURE_STATE_UNUSED,
|
||||||
|
TEXTURE_STATE_PENDING_MAP,
|
||||||
|
TEXTURE_STATE_MAPPED
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Texture
|
||||||
|
{
|
||||||
|
unsigned int formatVer;
|
||||||
|
volatile enum TextureState state;
|
||||||
|
void * map;
|
||||||
|
uint64_t copyTime;
|
||||||
|
uint32_t damageRectsCount;
|
||||||
|
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
|
||||||
|
int32_t texDamageCount;
|
||||||
|
FrameDamageRect texDamageRects[KVMFR_MAX_DAMAGE_RECTS];
|
||||||
|
|
||||||
|
void * impl;
|
||||||
|
}
|
||||||
|
Texture;
|
||||||
|
|
||||||
|
struct FrameDamage
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
FrameDamageRect rects[KVMFR_MAX_DAMAGE_RECTS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DXGICopyBackend;
|
||||||
|
|
||||||
|
struct DXGIInterface
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
bool dwmFlush;
|
||||||
|
D3D_FEATURE_LEVEL featureLevel;
|
||||||
|
IDXGIOutputDuplication * dup;
|
||||||
|
int maxTextures;
|
||||||
|
Texture * texture;
|
||||||
|
int texRIndex;
|
||||||
|
int texWIndex;
|
||||||
|
atomic_int texReady;
|
||||||
|
bool needsRelease;
|
||||||
|
DXGI_FORMAT dxgiFormat;
|
||||||
|
struct DXGICopyBackend * backend;
|
||||||
|
|
||||||
|
CaptureGetPointerBuffer getPointerBufferFn;
|
||||||
|
CapturePostPointerBuffer postPointerBufferFn;
|
||||||
|
LGEvent * frameEvent;
|
||||||
|
|
||||||
|
unsigned int formatVer;
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
unsigned int pitch;
|
||||||
|
unsigned int stride;
|
||||||
|
unsigned int bpp;
|
||||||
|
CaptureFormat format;
|
||||||
|
CaptureRotation rotation;
|
||||||
|
|
||||||
|
int lastPointerX, lastPointerY;
|
||||||
|
bool lastPointerVisible;
|
||||||
|
|
||||||
|
struct FrameDamage frameDamage[LGMP_Q_FRAME_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DXGICopyBackend
|
||||||
|
{
|
||||||
|
const char * name;
|
||||||
|
bool (*create)(struct DXGIInterface * intf);
|
||||||
|
void (*free)(void);
|
||||||
|
bool (*copyFrame)(Texture * tex, ID3D11Texture2D * src);
|
||||||
|
CaptureResult (*mapTexture)(Texture * tex);
|
||||||
|
void (*unmapTexture)(Texture * tex);
|
||||||
|
};
|
||||||
|
|
||||||
|
const char * GetDXGIFormatStr(DXGI_FORMAT format);
|
@ -18,9 +18,7 @@
|
|||||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <dxgi.h>
|
#include "dxgi_capture.h"
|
||||||
#include <d3d11.h>
|
|
||||||
#include <d3dcommon.h>
|
|
||||||
|
|
||||||
static const char * DXGI_FORMAT_STR[] = {
|
static const char * DXGI_FORMAT_STR[] = {
|
||||||
"DXGI_FORMAT_UNKNOWN",
|
"DXGI_FORMAT_UNKNOWN",
|
||||||
@ -147,7 +145,7 @@ static const char * DXGI_FORMAT_STR[] = {
|
|||||||
"DXGI_FORMAT_V408"
|
"DXGI_FORMAT_V408"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char * GetDXGIFormatStr(DXGI_FORMAT format)
|
const char * GetDXGIFormatStr(DXGI_FORMAT format)
|
||||||
{
|
{
|
||||||
if (format > sizeof(DXGI_FORMAT_STR) / sizeof(const char *))
|
if (format > sizeof(DXGI_FORMAT_STR) / sizeof(const char *))
|
||||||
return DXGI_FORMAT_STR[0];
|
return DXGI_FORMAT_STR[0];
|
Loading…
Reference in New Issue
Block a user