[host] dxgi: decouple backends from the DXGI main struct

This commit is contained in:
Geoffrey McRae 2023-11-07 20:40:05 +11:00
parent eb2796d40b
commit 54bd08c3cb
6 changed files with 361 additions and 232 deletions

View File

@ -35,23 +35,37 @@ struct DXGICopyBackend
const char * code; const char * code;
// create the copy backend // create the copy backend
bool (*create)(struct DXGIInterface * intf); bool (*create)(unsigned textures);
// configure the copy backend with the specified format // configure the copy backend with the specified format
bool (*configure)(unsigned width, unsigned height, bool (*configure)(
DXGI_FORMAT format, unsigned * pitch); unsigned width,
unsigned height,
DXGI_FORMAT format,
unsigned bpp,
unsigned * pitch);
// free the copy backend // free the copy backend
void (*free)(void); void (*free)(void);
// called each captured frame after post processing to copy the frame // called just before the copy starts
bool (*copyFrame)(struct Texture * tex, ID3D11Texture2D * src); bool (*preCopy)(ID3D11Texture2D * src, unsigned textureIndex);
// called to copy the full frame
bool (*copyFull)(ID3D11Texture2D * src, unsigned textureIndex);
// called for each damage rect that needs to be copied
bool (*copyRect)(ID3D11Texture2D * src, unsigned textureIndex,
FrameDamageRect * rect);
// called just after the copy has finished
bool (*postCopy)(ID3D11Texture2D * src, unsigned textureIndex);
// maps the copied frame into memory // maps the copied frame into memory
CaptureResult (*mapTexture)(struct Texture * tex); CaptureResult (*mapTexture)(unsigned textureIndex, void ** map);
// unmaps the copied frame from memory // unmaps the copied frame from memory
void (*unmapTexture)(struct Texture * tex); void (*unmapTexture)(unsigned textureIndex);
// called just before the frame is released by the frontend // called just before the frame is released by the frontend
void (*preRelease)(void); void (*preRelease)(void);

View File

@ -31,26 +31,29 @@ struct D3D11Backend
{ {
RunningAvg avgMapTime; RunningAvg avgMapTime;
uint64_t usleepMapTime; uint64_t usleepMapTime;
};
struct D3D11TexImpl unsigned textures;
{ struct
ID3D11Texture2D ** cpu; {
uint64_t copyTime;
ID3D11Texture2D ** cpu;
}
texture[0];
}; };
#define TEXIMPL(x) ((struct D3D11TexImpl *)(x).impl) #define TEXIMPL(x) ((struct D3D11TexImpl *)(x).impl)
static struct DXGIInterface * dxgi = NULL;
static struct D3D11Backend * this = NULL; static struct D3D11Backend * this = NULL;
static void d3d11_free(void); static void d3d11_free(void);
static bool d3d11_create(struct DXGIInterface * intf) static bool d3d11_create(unsigned textures)
{ {
dxgi = intf;
DEBUG_ASSERT(!this); DEBUG_ASSERT(!this);
this = calloc(1, sizeof(struct D3D11Backend)); this = calloc(1,
sizeof(struct D3D11Backend) +
sizeof(*this->texture) + textures);
if (!this) if (!this)
{ {
DEBUG_ERROR("failed to allocate D3D11Backend struct"); DEBUG_ERROR("failed to allocate D3D11Backend struct");
@ -58,11 +61,16 @@ static bool d3d11_create(struct DXGIInterface * intf)
} }
this->avgMapTime = runningavg_new(10); this->avgMapTime = runningavg_new(10);
this->textures = textures;
return true; return true;
} }
static bool d3d11_configure(unsigned width, unsigned height, DXGI_FORMAT format, static bool d3d11_configure(
unsigned * pitch) unsigned width,
unsigned height,
DXGI_FORMAT format,
unsigned bpp,
unsigned * pitch)
{ {
HRESULT status; HRESULT status;
@ -81,32 +89,22 @@ static bool d3d11_configure(unsigned width, unsigned height, DXGI_FORMAT format,
.MiscFlags = 0 .MiscFlags = 0
}; };
for (int i = 0; i < dxgi->maxTextures; ++i) comRef_defineLocal(ID3D11Texture2D, cpu);
for(int i = 0; i < this->textures; ++i)
{ {
if (!(dxgi->texture[i].impl = status = ID3D11Device_CreateTexture2D(dxgi_getDevice(), &cpuTexDesc, NULL, cpu);
(struct D3D11TexImpl *)calloc(sizeof(struct D3D11TexImpl), 1)))
{
DEBUG_ERROR("Failed to allocate D3D11TexImpl struct");
goto fail;
}
struct D3D11TexImpl * teximpl = TEXIMPL(dxgi->texture[i]);
status = ID3D11Device_CreateTexture2D(*dxgi->device, &cpuTexDesc, NULL,
(ID3D11Texture2D **)comRef_newGlobal(&teximpl->cpu));
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to create CPU texture", status); DEBUG_WINERROR("Failed to create CPU texture", status);
goto fail; goto fail;
} }
comRef_toGlobal(this->texture[i].cpu, cpu);
} }
// map the texture simply to get the pitch and stride // map the texture simply to get the pitch and stride
D3D11_MAPPED_SUBRESOURCE mapping; D3D11_MAPPED_SUBRESOURCE mapping;
status = ID3D11DeviceContext_Map(*dxgi->deviceContext, status = ID3D11DeviceContext_Map(dxgi_getContext(),
*(ID3D11Resource **)TEXIMPL(dxgi->texture[0])->cpu, 0, *(ID3D11Resource **)this->texture[0].cpu, 0, D3D11_MAP_READ, 0, &mapping);
D3D11_MAP_READ, 0, &mapping);
if (FAILED(status)) if (FAILED(status))
{ {
@ -114,8 +112,8 @@ static bool d3d11_configure(unsigned width, unsigned height, DXGI_FORMAT format,
goto fail; goto fail;
} }
ID3D11DeviceContext_Unmap(*dxgi->deviceContext, ID3D11DeviceContext_Unmap(dxgi_getContext(),
*(ID3D11Resource **)TEXIMPL(dxgi->texture[0])->cpu, 0); *(ID3D11Resource **)this->texture[0].cpu, 0);
*pitch = mapping.RowPitch; *pitch = mapping.RowPitch;
return true; return true;
@ -129,81 +127,79 @@ static void d3d11_free(void)
if (!this) if (!this)
return; return;
for (int i = 0; i < dxgi->maxTextures; ++i)
{
struct D3D11TexImpl * teximpl = TEXIMPL(dxgi->texture[i]);
free(teximpl);
teximpl = NULL;
}
runningavg_free(&this->avgMapTime); runningavg_free(&this->avgMapTime);
free(this); free(this);
this = NULL; this = NULL;
} }
static bool d3d11_copyFrame(Texture * tex, ID3D11Texture2D * src) static bool d3d11_preCopy(ID3D11Texture2D * src, unsigned textureIndex)
{ {
struct D3D11TexImpl * teximpl = TEXIMPL(*tex); dxgi_contextLock();
ID3D11Texture2D * dst = *teximpl->cpu; this->texture[textureIndex].copyTime = microtime();
INTERLOCKED_SECTION(dxgi->deviceContextLock,
{
tex->copyTime = microtime();
if (tex->texDamageCount < 0)
ID3D11DeviceContext_CopyResource(*dxgi->deviceContext,
(ID3D11Resource *)dst, (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,
};
if (dxgi->outputFormat == CAPTURE_FMT_BGR)
{
box.left = box.left * 3 / 4;
box.right = box.right * 3 / 4;
}
ID3D11DeviceContext_CopySubresourceRegion(*dxgi->deviceContext,
(ID3D11Resource *)dst, 0, box.left, box.top, 0,
(ID3D11Resource *)src, 0, &box);
}
}
ID3D11DeviceContext_Flush(*dxgi->deviceContext);
});
return true; return true;
} }
static CaptureResult d3d11_mapTexture(Texture * tex) static bool d3d11_copyFull(ID3D11Texture2D * src, unsigned textureIndex)
{ {
struct D3D11TexImpl * teximpl = TEXIMPL(*tex); ID3D11Texture2D * dst = *this->texture[textureIndex].cpu;
D3D11_MAPPED_SUBRESOURCE map;
ID3D11DeviceContext_CopyResource(dxgi_getContext(),
(ID3D11Resource *)dst, (ID3D11Resource *)src);
return true;
}
static bool d3d11_copyRect(ID3D11Texture2D * src, unsigned textureIndex,
FrameDamageRect * rect)
{
ID3D11Texture2D * dst = *this->texture[textureIndex].cpu;
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_getContext(),
(ID3D11Resource *)dst, 0, box.left, box.top, 0,
(ID3D11Resource *)src, 0, &box);
return true;
}
static bool d3d11_postCopy(ID3D11Texture2D * src, unsigned textureIndex)
{
ID3D11DeviceContext_Flush(dxgi_getContext());
dxgi_contextUnlock();
return true;
}
static CaptureResult d3d11_mapTexture(unsigned textureIndex, void ** map)
{
ID3D11Resource * cpu = *(ID3D11Resource **)this->texture[textureIndex].cpu;
// sleep until it's close to time to map // sleep until it's close to time to map
const uint64_t delta = microtime() - tex->copyTime; const uint64_t delta = microtime() - this->texture[textureIndex].copyTime;
if (delta < this->usleepMapTime) if (delta < this->usleepMapTime)
usleep(this->usleepMapTime - delta); usleep(this->usleepMapTime - delta);
D3D11_MAPPED_SUBRESOURCE mappedRes;
// try to map the resource, but don't wait for it // try to map the resource, but don't wait for it
for (int i = 0; ; ++i) for (int i = 0; ; ++i)
{ {
HRESULT status; HRESULT status;
INTERLOCKED_SECTION(dxgi->deviceContextLock, { dxgi_contextLock();
status = ID3D11DeviceContext_Map(*dxgi->deviceContext, status = ID3D11DeviceContext_Map(dxgi_getContext(), cpu, 0, D3D11_MAP_READ,
(ID3D11Resource *)*teximpl->cpu, 0, D3D11_MAP_READ, 0x100000L, &map); 0x100000L, &mappedRes);
}); dxgi_contextUnlock();
if (status == DXGI_ERROR_WAS_STILL_DRAWING) if (status == DXGI_ERROR_WAS_STILL_DRAWING)
{ {
if (i == 100) if (i == 100)
@ -222,23 +218,23 @@ static CaptureResult d3d11_mapTexture(Texture * tex)
break; break;
} }
tex->map = map.pData; *map = mappedRes.pData;
// update the sleep average and sleep for 80% of the average on the next call // update the sleep average and sleep for 80% of the average on the next call
runningavg_push(this->avgMapTime, microtime() - tex->copyTime); runningavg_push(this->avgMapTime,
microtime() - this->texture[textureIndex].copyTime);
this->usleepMapTime = (uint64_t)(runningavg_calc(this->avgMapTime) * 0.8); this->usleepMapTime = (uint64_t)(runningavg_calc(this->avgMapTime) * 0.8);
return CAPTURE_RESULT_OK; return CAPTURE_RESULT_OK;
} }
static void d3d11_unmapTexture(Texture * tex) static void d3d11_unmapTexture(unsigned textureIndex)
{ {
struct D3D11TexImpl * teximpl = TEXIMPL(*tex); ID3D11Resource * cpu = *(ID3D11Resource **)this->texture[textureIndex].cpu;
INTERLOCKED_SECTION(dxgi->deviceContextLock, { dxgi_contextLock();
ID3D11DeviceContext_Unmap(*dxgi->deviceContext, ID3D11DeviceContext_Unmap(dxgi_getContext(), cpu, 0);
(ID3D11Resource *)*teximpl->cpu, 0); dxgi_contextUnlock();
});
tex->map = NULL;
} }
static void d3d11_preRelease(void) static void d3d11_preRelease(void)
@ -252,7 +248,10 @@ struct DXGICopyBackend copyBackendD3D11 = {
.create = d3d11_create, .create = d3d11_create,
.configure = d3d11_configure, .configure = d3d11_configure,
.free = d3d11_free, .free = d3d11_free,
.copyFrame = d3d11_copyFrame, .preCopy = d3d11_preCopy,
.copyFull = d3d11_copyFull,
.copyRect = d3d11_copyRect,
.postCopy = d3d11_postCopy,
.mapTexture = d3d11_mapTexture, .mapTexture = d3d11_mapTexture,
.unmapTexture = d3d11_unmapTexture, .unmapTexture = d3d11_unmapTexture,
.preRelease = d3d11_preRelease, .preRelease = d3d11_preRelease,

View File

@ -1,5 +1,4 @@
/** /**
* Looking Glass
* Copyright © 2017-2023 The Looking Glass Authors * Copyright © 2017-2023 The Looking Glass Authors
* https://looking-glass.io * https://looking-glass.io
* *
@ -58,7 +57,6 @@ struct D3D12Backend
float copySleep; float copySleep;
ID3D12Device ** device; ID3D12Device ** device;
ID3D12CommandQueue ** commandQueue; ID3D12CommandQueue ** commandQueue;
struct D3D12Texture * texture;
UINT64 fenceValue; UINT64 fenceValue;
ID3D12Fence ** fence; ID3D12Fence ** fence;
HANDLE event; HANDLE event;
@ -66,10 +64,13 @@ struct D3D12Backend
// shared handle cache // shared handle cache
struct SharedCache sharedCache[10]; struct SharedCache sharedCache[10];
int sharedCacheCount; int sharedCacheCount;
ID3D12Resource * d12src;
unsigned textures;
struct D3D12Texture texture[0];
}; };
static struct DXGIInterface * dxgi = NULL; static struct D3D12Backend * this = NULL;
static struct D3D12Backend * this = NULL;
typedef HRESULT (*D3D12CreateDevice_t)( typedef HRESULT (*D3D12CreateDevice_t)(
IUnknown *pAdapter, IUnknown *pAdapter,
@ -85,18 +86,17 @@ typedef HRESULT (*D3D12GetDebugInterface_t)(
static void d3d12_free(void); static void d3d12_free(void);
static bool d3d12_create(struct DXGIInterface * intf) static bool d3d12_create(unsigned textures)
{ {
DEBUG_ASSERT(!this); DEBUG_ASSERT(!this);
HRESULT status; HRESULT status;
dxgi = intf;
HMODULE d3d12 = LoadLibrary("d3d12.dll"); HMODULE d3d12 = LoadLibrary("d3d12.dll");
if (!d3d12) if (!d3d12)
return false; return false;
if (dxgi->debug) if (dxgi_debug())
{ {
D3D12GetDebugInterface_t D3D12GetDebugInterface = (D3D12GetDebugInterface_t) D3D12GetDebugInterface_t D3D12GetDebugInterface = (D3D12GetDebugInterface_t)
GetProcAddress(d3d12, "D3D12GetDebugInterface"); GetProcAddress(d3d12, "D3D12GetDebugInterface");
@ -118,20 +118,17 @@ static bool d3d12_create(struct DXGIInterface * intf)
if (!D3D12CreateDevice) if (!D3D12CreateDevice)
return false; return false;
this = calloc(1, sizeof(struct D3D12Backend)); this = calloc(1,
sizeof(struct D3D12Backend) +
sizeof(*this->texture) * textures);
if (!this) if (!this)
{ {
DEBUG_ERROR("failed to allocate D3D12Backend struct"); DEBUG_ERROR("failed to allocate D3D12Backend struct");
return false; return false;
} }
this->texture = calloc(dxgi->maxTextures, sizeof(struct D3D12Texture)); this->textures = textures;
if (!this->texture)
{
DEBUG_ERROR("Failed to allocate memory");
return false;
}
this->copySleep = option_get_float("dxgi", "d3d12CopySleep"); this->copySleep = option_get_float("dxgi", "d3d12CopySleep");
DEBUG_INFO("Sleep before copy : %f ms", this->copySleep); DEBUG_INFO("Sleep before copy : %f ms", this->copySleep);
@ -139,8 +136,8 @@ static bool d3d12_create(struct DXGIInterface * intf)
comRef_scopePush(); comRef_scopePush();
comRef_defineLocal(ID3D12Device, device); comRef_defineLocal(ID3D12Device, device);
status = D3D12CreateDevice(*(IUnknown **)dxgi->adapter, D3D_FEATURE_LEVEL_11_0, status = D3D12CreateDevice((IUnknown *)dxgi_getAdapter(),
&IID_ID3D12Device, (void **)device); D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)device);
if (FAILED(status)) if (FAILED(status))
{ {
@ -200,8 +197,12 @@ exit:
return result; return result;
} }
static bool d3d12_configure(unsigned width, unsigned height, DXGI_FORMAT format, static bool d3d12_configure(
unsigned * pitch) unsigned width,
unsigned height,
DXGI_FORMAT format,
unsigned bpp,
unsigned * pitch)
{ {
bool result = false; bool result = false;
HRESULT status; HRESULT status;
@ -210,7 +211,7 @@ static bool d3d12_configure(unsigned width, unsigned height, DXGI_FORMAT format,
this->width = width; this->width = width;
this->height = height; this->height = height;
this->format = format; this->format = format;
this->pitch = ALIGN_TO(width * dxgi->bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); this->pitch = ALIGN_TO(width * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
D3D12_HEAP_PROPERTIES readbackHeapProperties = D3D12_HEAP_PROPERTIES readbackHeapProperties =
{ {
@ -237,7 +238,7 @@ static bool d3d12_configure(unsigned width, unsigned height, DXGI_FORMAT format,
comRef_defineLocal(ID3D12CommandAllocator , commandAllocator ); comRef_defineLocal(ID3D12CommandAllocator , commandAllocator );
comRef_defineLocal(ID3D12GraphicsCommandList, graphicsCommandList); comRef_defineLocal(ID3D12GraphicsCommandList, graphicsCommandList);
comRef_defineLocal(ID3D12CommandList , commandList ); comRef_defineLocal(ID3D12CommandList , commandList );
for (int i = 0; i < dxgi->maxTextures; ++i) for (int i = 0; i < this->textures; ++i)
{ {
status = ID3D12Device_CreateCommittedResource(*this->device, status = ID3D12Device_CreateCommittedResource(*this->device,
&readbackHeapProperties, &readbackHeapProperties,
@ -306,8 +307,6 @@ static bool d3d12_configure(unsigned width, unsigned height, DXGI_FORMAT format,
goto exit; goto exit;
} }
dxgi->texture[i].impl = this->texture + i;
comRef_toGlobal(this->texture[i].tex , texture ); comRef_toGlobal(this->texture[i].tex , texture );
comRef_toGlobal(this->texture[i].fence , fence ); comRef_toGlobal(this->texture[i].fence , fence );
comRef_toGlobal(this->texture[i].commandAllocator , commandAllocator ); comRef_toGlobal(this->texture[i].commandAllocator , commandAllocator );
@ -329,13 +328,9 @@ static void d3d12_free(void)
if (!this) if (!this)
return; return;
if (this->texture) for (int i = 0; i < this->textures; ++i)
{ if (this->texture[i].event)
for (int i = 0; i < dxgi->maxTextures; ++i) CloseHandle(this->texture[i].event);
if (this->texture[i].event)
CloseHandle(this->texture[i].event);
free(this->texture);
}
if (this->event) if (this->event)
CloseHandle(this->event); CloseHandle(this->event);
@ -344,86 +339,97 @@ static void d3d12_free(void)
this = NULL; this = NULL;
} }
static bool d3d12_copyFrame(Texture * parent, ID3D11Texture2D * src) static bool d3d12_preCopy(ID3D11Texture2D * src, unsigned textureIndex)
{ {
// we need to flush the DX11 context explicity or we get tons of lag
ID3D11DeviceContext_Flush(*dxgi->deviceContext);
comRef_scopePush(); comRef_scopePush();
bool result = false;
struct D3D12Texture * tex = parent->impl; // we need to flush the DX11 context explicity or we get tons of lag
bool fail = true; dxgi_contextLock();
comRef_defineLocal(IDXGIResource1, res1); ID3D11DeviceContext_Flush(dxgi_getContext());
ID3D12Resource * d12src = NULL; dxgi_contextUnlock();
HRESULT status;
if (this->copySleep > 0) if (this->copySleep > 0)
nsleep((uint64_t)(this->copySleep * 1000000)); nsleep((uint64_t)(this->copySleep * 1000000));
this->d12src = NULL;
if (this->sharedCacheCount > -1) if (this->sharedCacheCount > -1)
{ {
// see if there is a cached handle already available for this texture // see if there is a cached handle already available for this texture
for(int i = 0; i < this->sharedCacheCount; ++i) for(int i = 0; i < this->sharedCacheCount; ++i)
if (this->sharedCache[i].tex == src) if (this->sharedCache[i].tex == src)
{ {
d12src = *this->sharedCache[i].d12src; this->d12src = *this->sharedCache[i].d12src;
break; result = true;
goto exit;
} }
} }
if (!d12src) comRef_defineLocal(IDXGIResource1, res1);
HRESULT status = ID3D11Texture2D_QueryInterface(src,
&IID_IDXGIResource1, (void **)res1);
if (FAILED(status))
{ {
status = ID3D11Texture2D_QueryInterface(src, DEBUG_WINERROR("Failed to get IDXGIResource1 from texture", status);
&IID_IDXGIResource1, (void **)res1); goto exit;
}
if (FAILED(status)) HANDLE handle;
{ status = IDXGIResource1_CreateSharedHandle(*res1,
DEBUG_WINERROR("Failed to get IDXGIResource1 from texture", status); NULL, DXGI_SHARED_RESOURCE_READ, NULL, &handle);
goto cleanup;
}
HANDLE handle; if (FAILED(status))
status = IDXGIResource1_CreateSharedHandle(*res1, {
NULL, DXGI_SHARED_RESOURCE_READ, NULL, &handle); DEBUG_WINERROR("Failed to get create shared handle for texture", status);
goto exit;
}
if (FAILED(status)) status = ID3D12Device_OpenSharedHandle(*this->device,
{ handle, &IID_ID3D12Resource, (void **)&this->d12src);
DEBUG_WINERROR("Failed to get create shared handle for texture", status);
goto cleanup;
}
status = ID3D12Device_OpenSharedHandle(*this->device, CloseHandle(handle);
handle, &IID_ID3D12Resource, (void **)&d12src);
CloseHandle(handle);
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to open the shared handle for texture", status); DEBUG_WINERROR("Failed to open the shared handle for texture", status);
goto cleanup; goto exit;
} }
// store the texture for later use // store the texture for later use
if (this->sharedCacheCount < ARRAY_LENGTH(this->sharedCache)) if (this->sharedCacheCount < ARRAY_LENGTH(this->sharedCache))
{
struct SharedCache *cache = &this->sharedCache[this->sharedCacheCount++];
cache->tex = src;
*comRef_newGlobal(&cache->d12src) = (IUnknown *)this->d12src;
}
else
{
// too many handles to cache, disable the cache entirely
for(int i = 0; i < this->sharedCacheCount; ++i)
{ {
struct SharedCache *cache = &this->sharedCache[this->sharedCacheCount++]; struct SharedCache *cache = &this->sharedCache[this->sharedCacheCount++];
cache->tex = src; comRef_release(cache->d12src);
*comRef_newGlobal(&cache->d12src) = (IUnknown *)d12src;
}
else
{
// too many handles to cache, disable the cache entirely
for(int i = 0; i < this->sharedCacheCount; ++i)
{
struct SharedCache *cache = &this->sharedCache[this->sharedCacheCount++];
comRef_release(cache->d12src);
}
this->sharedCacheCount = -1;
} }
this->sharedCacheCount = -1;
} }
result = true;
exit:
if (!result)
comRef_scopePop();
return result;
}
static bool d3d12_copyFull(ID3D11Texture2D * src, unsigned textureIndex)
{
struct D3D12Texture * tex = &this->texture[textureIndex];
D3D12_TEXTURE_COPY_LOCATION srcLoc = D3D12_TEXTURE_COPY_LOCATION srcLoc =
{ {
.pResource = d12src, .pResource = this->d12src,
.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
.SubresourceIndex = 0 .SubresourceIndex = 0
}; };
@ -446,42 +452,69 @@ static bool d3d12_copyFrame(Texture * parent, ID3D11Texture2D * src)
} }
}; };
if (parent->texDamageCount < 0) ID3D12GraphicsCommandList_CopyTextureRegion(*tex->graphicsCommandList,
&destLoc, 0, 0, 0, &srcLoc, NULL);
return true;
}
static bool d3d12_copyRect(ID3D11Texture2D * src, unsigned textureIndex,
FrameDamageRect * rect)
{
struct D3D12Texture * tex = &this->texture[textureIndex];
D3D12_TEXTURE_COPY_LOCATION srcLoc =
{ {
ID3D12GraphicsCommandList_CopyTextureRegion(*tex->graphicsCommandList, .pResource = this->d12src,
&destLoc, 0, 0, 0, &srcLoc, NULL); .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
} .SubresourceIndex = 0
else };
D3D12_TEXTURE_COPY_LOCATION destLoc =
{ {
for (int i = 0; i < parent->texDamageCount; ++i) .pResource = *tex->tex,
.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
.PlacedFootprint =
{ {
FrameDamageRect * rect = parent->texDamageRects + i; .Offset = 0,
D3D12_BOX box = .Footprint =
{ {
.left = rect->x, .Format = this->format,
.top = rect->y, .Width = this->width,
.front = 0, .Height = this->height,
.back = 1, .Depth = 1,
.right = rect->x + rect->width, .RowPitch = this->pitch,
.bottom = rect->y + rect->height,
};
if (dxgi->outputFormat == CAPTURE_FMT_BGR)
{
box.left = box.left * 3 / 4;
box.right = box.right * 3 / 4;
} }
ID3D12GraphicsCommandList_CopyTextureRegion(*tex->graphicsCommandList,
&destLoc, box.left, box.top, 0, &srcLoc, &box);
} }
} };
status = ID3D12GraphicsCommandList_Close(*tex->graphicsCommandList); D3D12_BOX box =
{
.left = rect->x,
.top = rect->y,
.front = 0,
.back = 1,
.right = rect->x + rect->width,
.bottom = rect->y + rect->height,
};
ID3D12GraphicsCommandList_CopyTextureRegion(*tex->graphicsCommandList,
&destLoc, box.left, box.top, 0, &srcLoc, &box);
return true;
}
static bool d3d12_postCopy(ID3D11Texture2D * src, unsigned textureIndex)
{
bool result = false;
struct D3D12Texture * tex = &this->texture[textureIndex];
HRESULT status = ID3D12GraphicsCommandList_Close(*tex->graphicsCommandList);
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to close command list", status); DEBUG_WINERROR("Failed to close command list", status);
goto cleanup; goto exit;
} }
ID3D12CommandQueue_ExecuteCommandLists(*this->commandQueue, ID3D12CommandQueue_ExecuteCommandLists(*this->commandQueue,
@ -492,7 +525,7 @@ static bool d3d12_copyFrame(Texture * parent, ID3D11Texture2D * src)
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to signal capture fence", status); DEBUG_WINERROR("Failed to signal capture fence", status);
goto cleanup; goto exit;
} }
ResetEvent(this->event); ResetEvent(this->event);
@ -501,7 +534,7 @@ static bool d3d12_copyFrame(Texture * parent, ID3D11Texture2D * src)
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to signal capture fence event", status); DEBUG_WINERROR("Failed to signal capture fence event", status);
goto cleanup; goto exit;
} }
status = ID3D12CommandQueue_Signal(*this->commandQueue, status = ID3D12CommandQueue_Signal(*this->commandQueue,
@ -509,7 +542,7 @@ static bool d3d12_copyFrame(Texture * parent, ID3D11Texture2D * src)
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to signal texture fence", status); DEBUG_WINERROR("Failed to signal texture fence", status);
goto cleanup; goto exit;
} }
status = ID3D12Fence_SetEventOnCompletion(*tex->fence, status = ID3D12Fence_SetEventOnCompletion(*tex->fence,
@ -517,18 +550,19 @@ static bool d3d12_copyFrame(Texture * parent, ID3D11Texture2D * src)
if (FAILED(status)) if (FAILED(status))
{ {
DEBUG_WINERROR("Failed to signal texture fence event", status); DEBUG_WINERROR("Failed to signal texture fence event", status);
goto cleanup; goto exit;
} }
fail = false; result = true;
cleanup:
comRef_scopePop(); exit:
return !fail; comRef_scopePop(); //push is in pre-copy
return result;
} }
static CaptureResult d3d12_mapTexture(Texture * parent) static CaptureResult d3d12_mapTexture(unsigned textureIndex, void ** map)
{ {
struct D3D12Texture * tex = parent->impl; struct D3D12Texture * tex = &this->texture[textureIndex];
HRESULT status; HRESULT status;
WaitForSingleObject(tex->event, INFINITE); WaitForSingleObject(tex->event, INFINITE);
@ -553,7 +587,7 @@ static CaptureResult d3d12_mapTexture(Texture * parent)
.Begin = 0, .Begin = 0,
.End = this->pitch * this->height .End = this->pitch * this->height
}; };
status = ID3D12Resource_Map(*tex->tex, 0, &range, &parent->map); status = ID3D12Resource_Map(*tex->tex, 0, &range, map);
if (FAILED(status)) if (FAILED(status))
{ {
@ -564,9 +598,9 @@ static CaptureResult d3d12_mapTexture(Texture * parent)
return CAPTURE_RESULT_OK; return CAPTURE_RESULT_OK;
} }
static void d3d12_unmapTexture(Texture * parent) static void d3d12_unmapTexture(unsigned textureIndex)
{ {
struct D3D12Texture * tex = parent->impl; struct D3D12Texture * tex = &this->texture[textureIndex];
D3D12_RANGE range = D3D12_RANGE range =
{ {
@ -574,7 +608,6 @@ static void d3d12_unmapTexture(Texture * parent)
.End = 0 .End = 0
}; };
ID3D12Resource_Unmap(*tex->tex, 0, &range); ID3D12Resource_Unmap(*tex->tex, 0, &range);
parent->map = NULL;
} }
static void d3d12_preRelease(void) static void d3d12_preRelease(void)
@ -589,7 +622,10 @@ struct DXGICopyBackend copyBackendD3D12 =
.create = d3d12_create, .create = d3d12_create,
.configure = d3d12_configure, .configure = d3d12_configure,
.free = d3d12_free, .free = d3d12_free,
.copyFrame = d3d12_copyFrame, .preCopy = d3d12_preCopy,
.copyFull = d3d12_copyFull,
.copyRect = d3d12_copyRect,
.postCopy = d3d12_postCopy,
.mapTexture = d3d12_mapTexture, .mapTexture = d3d12_mapTexture,
.unmapTexture = d3d12_unmapTexture, .unmapTexture = d3d12_unmapTexture,
.preRelease = d3d12_preRelease, .preRelease = d3d12_preRelease,

View File

@ -708,7 +708,7 @@ static bool dxgi_init(void)
{ {
if (!strcasecmp(copyBackend, backends[i]->code)) if (!strcasecmp(copyBackend, backends[i]->code))
{ {
if (!backends[i]->create(this)) if (!backends[i]->create(this->maxTextures))
{ {
DEBUG_ERROR("Failed to initialize selected capture backend: %s", backends[i]->name); DEBUG_ERROR("Failed to initialize selected capture backend: %s", backends[i]->name);
backends[i]->free(); backends[i]->free();
@ -795,7 +795,7 @@ static bool dxgi_deinit(void)
Texture * tex = &this->texture[i]; Texture * tex = &this->texture[i];
if (!tex->map) if (!tex->map)
continue; continue;
this->backend->unmapTexture(tex); this->backend->unmapTexture(i);
tex->map = NULL; tex->map = NULL;
} }
@ -1099,7 +1099,12 @@ static CaptureResult dxgi_capture(void)
unsigned pitch = 0; unsigned pitch = 0;
LG_LOCK(this->deviceContextLock); LG_LOCK(this->deviceContextLock);
if (!this->backend->configure(cols, rows, this->dxgiFormat, &pitch)) if (!this->backend->configure(
cols,
rows,
this->dxgiFormat,
this->bpp,
&pitch))
{ {
LG_UNLOCK(this->deviceContextLock); LG_UNLOCK(this->deviceContextLock);
DEBUG_ERROR("Failed to configure the copy backend"); DEBUG_ERROR("Failed to configure the copy backend");
@ -1132,7 +1137,44 @@ static CaptureResult dxgi_capture(void)
computeFrameDamage(tex); computeFrameDamage(tex);
computeTexDamage(tex); computeTexDamage(tex);
if (!this->backend->copyFrame(tex, dst)) if (!this->backend->preCopy(dst, this->texWIndex))
{
result = CAPTURE_RESULT_ERROR;
goto exit;
}
if (tex->texDamageCount <= 0)
{
if (!this->backend->copyFull(dst, this->texWIndex))
{
// call this so the backend can cleanup
this->backend->postCopy(dst, this->texWIndex);
result = CAPTURE_RESULT_ERROR;
goto exit;
}
}
else
{
for (int i = 0; i < tex->texDamageCount; ++i)
{
FrameDamageRect * rect = &tex->texDamageRects[i];
// correct the damage rect for BGR packed data
if (this->outputFormat == CAPTURE_FMT_BGR)
{
rect->x = (rect->x * 3 ) / 4; // round down
rect->width = (rect->width * 3 + 3) / 4; // round up
}
if (!this->backend->copyRect(dst, this->texWIndex, rect))
{
result = CAPTURE_RESULT_ERROR;
goto exit;
}
}
}
if (!this->backend->postCopy(dst, this->texWIndex))
{ {
result = CAPTURE_RESULT_ERROR; result = CAPTURE_RESULT_ERROR;
goto exit; goto exit;
@ -1266,7 +1308,7 @@ static CaptureResult dxgi_waitFrame(CaptureFrame * frame, const size_t maxFrameS
Texture * tex = &this->texture[this->texRIndex]; Texture * tex = &this->texture[this->texRIndex];
CaptureResult result = this->backend->mapTexture(tex); CaptureResult result = this->backend->mapTexture(this->texRIndex, &tex->map);
if (result != CAPTURE_RESULT_OK) if (result != CAPTURE_RESULT_OK)
return result; return result;
@ -1355,7 +1397,8 @@ static CaptureResult dxgi_getFrame(FrameBuffer * frame, int frameIndex)
damage->count = -1; damage->count = -1;
} }
this->backend->unmapTexture(tex); this->backend->unmapTexture(this->texRIndex);
tex->map = NULL;
tex->state = TEXTURE_STATE_UNUSED; tex->state = TEXTURE_STATE_UNUSED;
if (++this->texRIndex == this->maxTextures) if (++this->texRIndex == this->maxTextures)
@ -1549,6 +1592,36 @@ static void ppFreeAll(void)
} }
} }
IDXGIAdapter1 * dxgi_getAdapter(void)
{
return *this->adapter;
}
ID3D11Device * dxgi_getDevice(void)
{
return *this->device;
}
ID3D11DeviceContext * dxgi_getContext(void)
{
return *this->deviceContext;
}
void dxgi_contextLock(void)
{
LG_LOCK(this->deviceContextLock);
};
void dxgi_contextUnlock(void)
{
LG_UNLOCK(this->deviceContextLock);
};
bool dxgi_debug(void)
{
return this->debug;
}
struct CaptureInterface Capture_DXGI = struct CaptureInterface Capture_DXGI =
{ {
.shortName = "DXGI", .shortName = "DXGI",

View File

@ -50,7 +50,6 @@ typedef struct Texture
unsigned int formatVer; unsigned int formatVer;
volatile enum TextureState state; volatile enum TextureState state;
void * map; void * map;
uint64_t copyTime;
uint32_t damageRectsCount; uint32_t damageRectsCount;
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS]; FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
int texDamageCount; int texDamageCount;
@ -63,11 +62,12 @@ typedef struct Texture
} }
Texture; Texture;
struct FrameDamage typedef struct FrameDamage
{ {
int count; int count;
FrameDamageRect rects[KVMFR_MAX_DAMAGE_RECTS]; FrameDamageRect rects[KVMFR_MAX_DAMAGE_RECTS];
}; }
FrameDamage;
struct DXGIInterface struct DXGIInterface
{ {
@ -122,4 +122,11 @@ struct DXGIInterface
struct FrameDamage frameDamage[LGMP_Q_FRAME_LEN]; struct FrameDamage frameDamage[LGMP_Q_FRAME_LEN];
}; };
IDXGIAdapter1 * dxgi_getAdapter(void);
ID3D11Device * dxgi_getDevice(void);
ID3D11DeviceContext * dxgi_getContext(void);
void dxgi_contextLock(void);
void dxgi_contextUnlock(void);
bool dxgi_debug(void);
#endif #endif

View File

@ -78,7 +78,7 @@ static bool rgb24_configure(void * opaque,
if (!this.pshader) if (!this.pshader)
{ {
this.width = *cols * 3 / 4; this.width = (*cols * 3 + 3) / 4;
this.height = *rows; this.height = *rows;
char sOutputWidth[6], sOutputHeight[6]; char sOutputWidth[6], sOutputHeight[6];