LookingGlass/host/platform/Windows/capture/D12/d12.c
Geoffrey McRae 90b27ae1f7 [host] d12: revert ivshmem heap order change
Moving this prevented the RX580 crash early in init, but later
presents during the capture. As we want to ensure fallback to DXGI
if this happens we need to catch this fault during init. This moves
the order back so that we fail back into a working state.
2024-03-09 12:11:54 +11:00

1014 lines
27 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 "d12.h"
#include "interface/capture.h"
#include "common/array.h"
#include "common/debug.h"
#include "common/windebug.h"
#include "common/option.h"
#include "common/rects.h"
#include "common/vector.h"
#include "common/display.h"
#include "com_ref.h"
#include "backend.h"
#include "effects.h"
#include "command_group.h"
#include <dxgi.h>
#include <dxgi1_3.h>
#include <dxgi1_6.h>
#include <d3dcommon.h>
// definitions
struct D12Interface
{
HMODULE d3d12;
IDXGIFactory2 ** factory;
ID3D12Device3 ** device;
DISPLAYCONFIG_PATH_INFO displayPathInfo;
ID3D12CommandQueue ** copyQueue;
ID3D12CommandQueue ** computeQueue;
D12CommandGroup copyCommand;
D12CommandGroup computeCommand;
void * ivshmemBase;
ID3D12Heap ** ivshmemHeap;
CaptureGetPointerBuffer getPointerBufferFn;
CapturePostPointerBuffer postPointerBufferFn;
D12Backend * backend;
Vector effects;
bool effectsActive;
// capture format tracking
D12FrameFormat captureFormat;
unsigned formatVer;
unsigned pitch;
// output format tracking
D12FrameFormat dstFormat;
// prior frame dirty rects
RECT dirtyRects[D12_MAX_DIRTY_RECTS];
unsigned nbDirtyRects;
// options
bool debug;
bool trackDamage;
unsigned frameBufferCount;
// must be last
struct
{
// the size of the frame buffer
unsigned size;
// the frame buffer it itself
FrameBuffer * frameBuffer;
// the resource backed by the framebuffer
ID3D12Resource ** resource;
}
frameBuffers[0];
};
// gloabls
struct DX12 DX12 = {0};
ComScope * d12_comScope = NULL;
// defines
// locals
static struct D12Interface * this = NULL;
// forwards
static bool d12_enumerateDevices(
IDXGIFactory2 ** factory,
IDXGIAdapter1 ** adapter,
IDXGIOutput ** output);
static ID3D12Resource * d12_frameBufferToResource(
unsigned frameBufferIndex,
FrameBuffer * frameBuffer,
unsigned size);
// implementation
static const char * d12_getName(void)
{
return "D12";
}
static void d12_initOptions(void)
{
struct Option options[] =
{
{
.module = "d12",
.name = "adapter",
.description = "The name of the adapter to capture",
.type = OPTION_TYPE_STRING,
.value.x_string = NULL
},
{
.module = "d12",
.name = "output",
.description = "The name of the adapter's output to capture",
.type = OPTION_TYPE_STRING,
.value.x_string = NULL
},
{
.module = "d12",
.name = "trackDamage",
.description = "Perform damage-aware copies (saves bandwidth)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "d12",
.name = "debug",
.description = "Enable DirectX12 debugging and validation (SLOW!)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{0}
};
option_register(options);
for(const D12Effect ** effect = D12Effects; *effect; ++effect)
d12_effectInitOptions(*effect);
}
static bool d12_create(
CaptureGetPointerBuffer getPointerBufferFn,
CapturePostPointerBuffer postPointerBufferFn,
unsigned frameBuffers)
{
this = calloc(1, offsetof(struct D12Interface, frameBuffers) +
sizeof(this->frameBuffers[0]) * frameBuffers);
if (!this)
{
DEBUG_ERROR("failed to allocate D12Interface struct");
return false;
}
this->debug = option_get_bool("d12", "debug" );
this->trackDamage = option_get_bool("d12", "trackDamage" );
DEBUG_INFO(
"debug:%d trackDamage:%d",
this->debug, this->trackDamage);
this->d3d12 = LoadLibrary("d3d12.dll");
if (!this->d3d12)
{
DEBUG_ERROR("failed to load d3d12.dll");
free(this);
return false;
}
DX12.D3D12CreateDevice = (typeof(DX12.D3D12CreateDevice))
GetProcAddress(this->d3d12, "D3D12CreateDevice");
DX12.D3D12GetDebugInterface = (typeof(DX12.D3D12GetDebugInterface))
GetProcAddress(this->d3d12, "D3D12GetDebugInterface");
DX12.D3D12SerializeVersionedRootSignature =
(typeof(DX12.D3D12SerializeVersionedRootSignature))
GetProcAddress(this->d3d12, "D3D12SerializeVersionedRootSignature");
this->getPointerBufferFn = getPointerBufferFn;
this->postPointerBufferFn = postPointerBufferFn;
if (!d12_backendCreate(&D12Backend_DD, &this->backend, frameBuffers))
{
DEBUG_ERROR("backend \"%s\" failed to create", this->backend->codeName);
CloseHandle(this->d3d12);
free(this);
return false;
}
this->frameBufferCount = frameBuffers;
return true;
}
static bool d12_init(void * ivshmemBase, unsigned * alignSize)
{
bool result = false;
comRef_initGlobalScope(100, d12_comScope);
comRef_scopePush(10);
// create a DXGI factory
comRef_defineLocal(IDXGIFactory2, factory);
HRESULT hr = CreateDXGIFactory2(
this->debug ? DXGI_CREATE_FACTORY_DEBUG : 0,
&IID_IDXGIFactory2,
(void **)factory);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the DXGI factory", hr);
goto exit;
}
// find the adapter and output we want to use
comRef_defineLocal(IDXGIAdapter1, adapter);
comRef_defineLocal(IDXGIOutput , output );
if (!d12_enumerateDevices(factory, adapter, output))
goto exit;
if (this->debug)
{
comRef_defineLocal(ID3D12Debug1, debug);
hr = DX12.D3D12GetDebugInterface(&IID_ID3D12Debug1, (void **)debug);
if (FAILED(hr))
{
DEBUG_WINERROR("D3D12GetDebugInterface", hr);
goto exit;
}
ID3D12Debug1_EnableDebugLayer(*debug);
ID3D12Debug1_SetEnableGPUBasedValidation(*debug, TRUE);
ID3D12Debug1_SetEnableSynchronizedCommandQueueValidation(*debug, TRUE);
}
// get the display path info
comRef_defineLocal(IDXGIOutput6, output6);
hr = IDXGIOutput_QueryInterface(*output, &IID_IDXGIOutput6, (void **)output6);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to obtain the IDXGIOutput6 interface", hr);
goto exit;
}
DXGI_OUTPUT_DESC1 desc1;
IDXGIOutput6_GetDesc1(*output6, &desc1);
if (!display_getPathInfo(desc1.Monitor, &this->displayPathInfo))
{
DEBUG_ERROR("Failed to get the display path info");
goto exit;
}
// create the D3D12 device
comRef_defineLocal(ID3D12Device3, device);
hr = DX12.D3D12CreateDevice(
(IUnknown *)*adapter,
D3D_FEATURE_LEVEL_12_0,
&IID_ID3D12Device3,
(void **)device);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the DirectX12 device", hr);
goto exit;
}
D3D12_COMMAND_QUEUE_DESC copyQueueDesc =
{
.Type = D3D12_COMMAND_LIST_TYPE_COPY,
.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH,
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE
};
comRef_defineLocal(ID3D12CommandQueue, copyQueue);
hr = ID3D12Device3_CreateCommandQueue(
*device, &copyQueueDesc, &IID_ID3D12CommandQueue, (void **)copyQueue);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create ID3D12CommandQueue (copy)", hr);
goto exit;
}
ID3D12CommandQueue_SetName(*copyQueue, L"Copy");
// create the compute queue
D3D12_COMMAND_QUEUE_DESC computeQueueDesc =
{
.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE,
.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH,
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE
};
comRef_defineLocal(ID3D12CommandQueue, computeQueue);
hr = ID3D12Device3_CreateCommandQueue(
*device, &computeQueueDesc, &IID_ID3D12CommandQueue, (void **)computeQueue);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the ID3D12CommandQueue (compute)", hr);
goto exit;
}
ID3D12CommandQueue_SetName(*computeQueue, L"Compute");
if (!d12_commandGroupCreate(
*device, D3D12_COMMAND_LIST_TYPE_COPY, &this->copyCommand, L"Copy"))
goto exit;
if (!d12_commandGroupCreate(
*device, D3D12_COMMAND_LIST_TYPE_COMPUTE, &this->computeCommand, L"Compute"))
goto exit;
// Create the IVSHMEM heap
this->ivshmemBase = ivshmemBase;
comRef_defineLocal(ID3D12Heap, ivshmemHeap);
hr = ID3D12Device3_OpenExistingHeapFromAddress(
*device, ivshmemBase, &IID_ID3D12Heap, (void **)ivshmemHeap);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to open the framebuffer as a D3D12Heap", hr);
goto exit;
}
// Adjust the alignSize based on the required heap alignment
D3D12_HEAP_DESC heapDesc = ID3D12Heap_GetDesc(*ivshmemHeap);
*alignSize = heapDesc.Alignment;
// initialize the backend
if (!d12_backendInit(this->backend, this->debug, *device, *adapter, *output,
this->trackDamage))
goto exit;
// create the vector of effects
vector_create(&this->effects, sizeof(D12Effect *), 0);
// create all the effects
for(const D12Effect ** effect = D12Effects; *effect; ++effect)
{
D12Effect * instance;
switch(d12_effectCreate(*effect, &instance, *device, &this->displayPathInfo))
{
case D12_EFFECT_STATUS_OK:
DEBUG_INFO("D12 Created Effect: %s", (*effect)->name);
vector_push(&this->effects, &instance);
break;
case D12_EFFECT_STATUS_BYPASS:
continue;
case D12_EFFECT_STATUS_ERROR:
DEBUG_ERROR("Failed to create effect: %s", (*effect)->name);
goto exit;
}
}
comRef_toGlobal(this->factory , factory );
comRef_toGlobal(this->device , device );
comRef_toGlobal(this->copyQueue , copyQueue );
comRef_toGlobal(this->computeQueue, computeQueue );
comRef_toGlobal(this->ivshmemHeap , ivshmemHeap );
result = true;
exit:
comRef_scopePop();
if (!result)
{
D12Effect * effect;
vector_forEach(effect, &this->effects)
d12_effectFree(&effect);
vector_destroy(&this->effects);
comRef_freeScope(&d12_comScope);
}
return result;
}
static void d12_stop(void)
{
}
static bool d12_deinit(void)
{
bool result = true;
D12Effect * effect;
vector_forEach(effect, &this->effects)
d12_effectFree(&effect);
vector_destroy(&this->effects);
if (!d12_backendDeinit(this->backend))
result = false;
d12_commandGroupFree(&this->copyCommand );
d12_commandGroupFree(&this->computeCommand);
IDXGIFactory2 * factory = *this->factory;
IDXGIFactory2_AddRef(factory);
comRef_freeScope(&d12_comScope);
if (IDXGIFactory2_Release(factory) != 0)
DEBUG_WARN("MEMORY LEAK");
// zero the framebuffers
memset(this->frameBuffers, 0,
sizeof(*this->frameBuffers) * this->frameBufferCount);
/* zero the formats so we properly reinit otherwise we wont detect the format
change and setup the effect chain */
memset(&this->captureFormat, 0, sizeof(this->captureFormat));
memset(&this->dstFormat , 0, sizeof(this->dstFormat ));
/* dirty rect history is no longer valid */
this->nbDirtyRects = 0;
return result;
}
static void d12_free(void)
{
d12_backendFree(&this->backend);
FreeLibrary(this->d3d12);
free(this);
this = NULL;
}
static CaptureResult d12_capture(
unsigned frameBufferIndex, FrameBuffer * frameBuffer)
{
return d12_backendCapture(this->backend, frameBufferIndex);
}
static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
CaptureFrame * frame, const size_t maxFrameSize)
{
CaptureResult result = CAPTURE_RESULT_ERROR;
comRef_scopePush(1);
D12FrameDesc desc;
comRef_defineLocal(ID3D12Resource, src);
*src = d12_backendFetch(this->backend, frameBufferIndex, &desc);
if (!*src)
{
DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
frameBufferIndex);
goto exit;
}
D12FrameFormat srcFormat =
{
.desc = ID3D12Resource_GetDesc(*src),
.colorSpace = desc.colorSpace,
.width = srcFormat.desc.Width,
.height = srcFormat.desc.Height
};
switch(srcFormat.desc.Format)
{
case DXGI_FORMAT_B8G8R8A8_UNORM:
srcFormat.format = CAPTURE_FMT_BGRA;
break;
case DXGI_FORMAT_R8G8B8A8_UNORM:
srcFormat.format = CAPTURE_FMT_RGBA;
break;
case DXGI_FORMAT_R10G10B10A2_UNORM:
srcFormat.format = CAPTURE_FMT_RGBA10;
break;
case DXGI_FORMAT_R16G16B16A16_FLOAT:
srcFormat.format = CAPTURE_FMT_RGBA16F;
break;
default:
DEBUG_ERROR("Unsupported source format");
goto exit;
}
// if the input format changed, reconfigure the effects
if (srcFormat.desc.Width == 0 ||
srcFormat.desc.Width != this->captureFormat.desc.Width ||
srcFormat.desc.Height != this->captureFormat.desc.Height ||
srcFormat.desc.Format != this->captureFormat.desc.Format ||
srcFormat.colorSpace != this->captureFormat.colorSpace)
{
D12FrameFormat dstFormat = this->dstFormat;
this->captureFormat = srcFormat;
this->effectsActive = false;
D12Effect * effect;
D12FrameFormat curFormat = srcFormat;
vector_forEach(effect, &this->effects)
{
dstFormat = curFormat;
switch(d12_effectSetFormat(effect, *this->device, &curFormat, &dstFormat))
{
case D12_EFFECT_STATUS_OK:
this->effectsActive = true;
curFormat = dstFormat;
effect->enabled = true;
DEBUG_INFO("D12 Effect Active: %s", effect->name);
break;
case D12_EFFECT_STATUS_ERROR:
DEBUG_ERROR("Failed to set the effect input format");
goto exit;
case D12_EFFECT_STATUS_BYPASS:
effect->enabled = false;
break;
}
}
// if the output format changed
if (dstFormat.desc.Width != this->dstFormat.desc.Width ||
dstFormat.desc.Height != this->dstFormat.desc.Height ||
dstFormat.desc.Format != this->dstFormat.desc.Format ||
dstFormat.colorSpace != this->dstFormat.colorSpace ||
dstFormat.width != this->dstFormat.width ||
dstFormat.height != this->dstFormat.height ||
dstFormat.format != this->dstFormat.format)
{
++this->formatVer;
this->dstFormat = dstFormat;
}
}
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
ID3D12Device3_GetCopyableFootprints(*this->device,
&this->dstFormat.desc,
0 , // FirstSubresource
1 , // NumSubresources
0 , // BaseOffset,
&layout , // pLayouts
NULL , // pNumRows,
NULL , // pRowSizeInBytes,
NULL); // pTotalBytes
this->pitch = layout.Footprint.RowPitch;
const unsigned maxRows = maxFrameSize / layout.Footprint.RowPitch;
const unsigned bpp = this->dstFormat.format == CAPTURE_FMT_RGBA16F ? 8 : 4;
frame->formatVer = this->formatVer;
frame->screenWidth = srcFormat.width;
frame->screenHeight = srcFormat.height;
frame->dataWidth = this->dstFormat.desc.Width;
frame->dataHeight = min(maxRows, this->dstFormat.desc.Height);
frame->frameWidth = this->dstFormat.width;
frame->frameHeight = this->dstFormat.height;
frame->truncated = maxRows < this->dstFormat.desc.Height;
frame->pitch = this->pitch;
frame->stride = this->pitch / bpp;
frame->format = this->dstFormat.format;
frame->hdr = this->dstFormat.colorSpace ==
DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
frame->hdrPQ = false;
frame->rotation = desc.rotation;
D12Effect * effect;
vector_forEach(effect, &this->effects)
if (effect->enabled)
d12_effectAdjustDamage(effect, desc.dirtyRects, &desc.nbDirtyRects);
{
// create a clean list of rects
FrameDamageRect allRects[desc.nbDirtyRects];
unsigned count = 0;
for(const RECT * rect = desc.dirtyRects;
rect < desc.dirtyRects + desc.nbDirtyRects; ++rect)
allRects[count++] = (FrameDamageRect){
.x = rect->left,
.y = rect->top,
.width = (rect->right - rect->left),
.height = (rect->bottom - rect->top)
};
count = rectsMergeOverlapping(allRects, count);
// if there are too many rects
if (unlikely(count > ARRAY_LENGTH(frame->damageRects)))
frame->damageRectsCount = 0;
else
{
// send the list of dirty rects for this frame
frame->damageRectsCount = count;
memcpy(frame->damageRects, allRects, sizeof(*allRects) * count);
}
}
result = CAPTURE_RESULT_OK;
exit:
comRef_scopePop();
return result;
}
static CaptureResult d12_getFrame(unsigned frameBufferIndex,
FrameBuffer * frameBuffer, const size_t maxFrameSize)
{
CaptureResult result = CAPTURE_RESULT_ERROR;
comRef_scopePush(3);
D12FrameDesc desc;
comRef_defineLocal(ID3D12Resource, src);
*src = d12_backendFetch(this->backend, frameBufferIndex, &desc);
if (!*src)
{
DEBUG_ERROR("D12 backend failed to produce an expected frame: %u",
frameBufferIndex);
goto exit;
}
comRef_defineLocal(ID3D12Resource, dst)
*dst = d12_frameBufferToResource(frameBufferIndex, frameBuffer, maxFrameSize);
if (!*dst)
goto exit;
// place a fence into the queue
result = d12_backendSync(this->backend,
this->effectsActive ? *this->computeQueue : *this->copyQueue);
if (result != CAPTURE_RESULT_OK)
goto exit;
ID3D12Resource * next = *src;
D12Effect * effect;
vector_forEach(effect, &this->effects)
{
if (!effect->enabled)
continue;
next = d12_effectRun(effect,
*this->device,
*this->computeCommand.gfxList,
next,
desc.dirtyRects,
&desc.nbDirtyRects);
}
// copy into the framebuffer resource
D3D12_TEXTURE_COPY_LOCATION srcLoc =
{
.pResource = next,
.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
.SubresourceIndex = 0
};
D3D12_TEXTURE_COPY_LOCATION dstLoc =
{
.pResource = *dst,
.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
.PlacedFootprint =
{
.Offset = 0,
.Footprint =
{
.Format = this->dstFormat.desc.Format,
.Width = this->dstFormat.desc.Width,
.Height = this->dstFormat.desc.Height,
.Depth = 1,
.RowPitch = this->pitch
}
}
};
// if full frame damage
if (desc.nbDirtyRects == 0)
{
this->nbDirtyRects = 0;
ID3D12GraphicsCommandList_CopyTextureRegion(
*this->copyCommand.gfxList, &dstLoc, 0, 0, 0, &srcLoc, NULL);
}
else
{
/* if the prior frame was a full update */
if (this->nbDirtyRects == 0)
{
/* the prior frame was fully damaged, we must update everything */
ID3D12GraphicsCommandList_CopyTextureRegion(
*this->copyCommand.gfxList, &dstLoc, 0, 0, 0, &srcLoc, NULL);
}
else
{
FrameDamageRect allRects[this->nbDirtyRects + desc.nbDirtyRects];
unsigned count = 0;
/* we must update the rects that were dirty in the prior frame also,
* otherwise the frame in memory will not be consistent when areas need to
* be redrawn by the client, such as under the cursor */
for(const RECT * rect = this->dirtyRects;
rect < this->dirtyRects + this->nbDirtyRects; ++rect)
allRects[count++] = (FrameDamageRect){
.x = rect->left,
.y = rect->top,
.width = rect->right - rect->left,
.height = rect->bottom - rect->top
};
/* add the new dirtyRects to the array */
for(const RECT * rect = desc.dirtyRects;
rect < desc.dirtyRects + desc.nbDirtyRects; ++rect)
allRects[count++] = (FrameDamageRect){
.x = rect->left,
.y = rect->top,
.width = rect->right - rect->left,
.height = rect->bottom - rect->top
};
/* resolve the rects */
count = rectsMergeOverlapping(allRects, count);
/* copy all the rects */
for(FrameDamageRect * rect = allRects; rect < allRects + count; ++rect)
{
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(
*this->copyCommand.gfxList, &dstLoc,
box.left, box.top, 0, &srcLoc, &box);
}
}
/* store the dirty rects for the next frame */
memcpy(this->dirtyRects, desc.dirtyRects,
desc.nbDirtyRects * sizeof(*this->dirtyRects));
this->nbDirtyRects = desc.nbDirtyRects;
}
// execute the compute commands
if (this->effectsActive)
{
d12_commandGroupExecute(*this->computeQueue, &this->computeCommand);
// insert a fence to wait for the compute commands to finish
ID3D12CommandQueue_Wait(*this->copyQueue,
*this->computeCommand.fence, this->computeCommand.fenceValue);
}
// execute the copy commands
d12_commandGroupExecute(*this->copyQueue, &this->copyCommand);
// wait for the copy to complete
d12_commandGroupWait(&this->copyCommand);
// signal the frame is complete
framebuffer_set_write_ptr(frameBuffer,
this->dstFormat.desc.Height * this->pitch);
// reset the command queues
if (this->effectsActive && !d12_commandGroupReset(&this->computeCommand))
goto exit;
if (!d12_commandGroupReset(&this->copyCommand))
goto exit;
result = CAPTURE_RESULT_OK;
exit:
comRef_scopePop();
return result;
}
static bool d12_enumerateDevices(
IDXGIFactory2 ** factory,
IDXGIAdapter1 ** adapter,
IDXGIOutput ** output)
{
DXGI_ADAPTER_DESC1 adapterDesc;
DXGI_OUTPUT_DESC outputDesc;
const char * _optAdapter = option_get_string("d12", "adapter");
const char * _optOutput = option_get_string("d12", "output" );
wchar_t * optAdapter = NULL;
wchar_t * optOutput = NULL;
if (_optAdapter)
{
optAdapter = malloc((strlen(_optAdapter) + 1) * 2);
mbstowcs(optAdapter, _optAdapter, strlen(_optAdapter));
}
if (_optOutput)
{
optOutput = malloc((strlen(_optOutput) + 1) * 2);
mbstowcs(optOutput, _optOutput, strlen(_optOutput));
}
for(
int i = 0;
IDXGIFactory2_EnumAdapters1(*factory, i, adapter)
!= DXGI_ERROR_NOT_FOUND;
++i, comRef_release(adapter))
{
HRESULT hr = IDXGIAdapter1_GetDesc1(*adapter, &adapterDesc);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to get the device description", hr);
comRef_release(adapter);
return false;
}
// check for devices without D3D support
static const UINT blacklist[][2] =
{
//VID , PID
{0x1414, 0x008c}, // Microsoft Basic Render Driver
{0x1b36, 0x000d}, // QXL
{0x1234, 0x1111} // QEMU Standard VGA
};
bool skip = false;
for(int n = 0; n < ARRAY_LENGTH(blacklist); ++n)
{
if (adapterDesc.VendorId == blacklist[n][0] &&
adapterDesc.DeviceId == blacklist[n][1])
{
DEBUG_INFO("Not using unsupported adapter: %ls",
adapterDesc.Description);
skip = true;
break;
}
}
if (skip)
continue;
if (optAdapter)
{
if (wcsstr(adapterDesc.Description, optAdapter) == NULL)
{
DEBUG_INFO("Not using adapter: %ls", adapterDesc.Description);
continue;
}
DEBUG_INFO("Adapter matched, trying: %ls", adapterDesc.Description);
}
for(
int n = 0;
IDXGIAdapter1_EnumOutputs(*adapter, n, output) != DXGI_ERROR_NOT_FOUND;
++n, comRef_release(output))
{
IDXGIOutput_GetDesc(*output, &outputDesc);
if (optOutput)
{
if (wcsstr(outputDesc.DeviceName, optOutput) == NULL)
{
DEBUG_INFO("Not using adapter output: %ls", outputDesc.DeviceName);
continue;
}
DEBUG_INFO("Adapter output matched, trying: %ls", outputDesc.DeviceName);
}
if (outputDesc.AttachedToDesktop)
break;
}
if (*output)
break;
}
free(optAdapter);
free(optOutput );
if (!*output)
{
DEBUG_ERROR("Failed to locate a valid output device");
return false;
}
DEBUG_INFO("Device Name : %ls" , outputDesc.DeviceName);
DEBUG_INFO("Device Description: %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));
return true;
}
static ID3D12Resource * d12_frameBufferToResource(unsigned frameBufferIndex,
FrameBuffer * frameBuffer, unsigned size)
{
ID3D12Resource * result = NULL;
comRef_scopePush(10);
typeof(this->frameBuffers[0]) * fb = &this->frameBuffers[frameBufferIndex];
// nothing to do if the resource is already setup and is big enough
if (fb->resource && fb->frameBuffer == frameBuffer && fb->size >= size)
{
result = *fb->resource;
ID3D12Resource_AddRef(result);
goto exit;
}
fb->size = size;
fb->frameBuffer = frameBuffer;
D3D12_RESOURCE_DESC desc =
{
.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
.Width = size,
.Height = 1,
.DepthOrArraySize = 1,
.MipLevels = 1,
.Format = DXGI_FORMAT_UNKNOWN,
.SampleDesc.Count = 1,
.SampleDesc.Quality = 0,
.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER
};
comRef_defineLocal(ID3D12Resource, resource);
HRESULT hr = ID3D12Device3_CreatePlacedResource(
*this->device,
*this->ivshmemHeap,
(uintptr_t)framebuffer_get_data(frameBuffer) - (uintptr_t)this->ivshmemBase,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
NULL,
&IID_ID3D12Resource,
(void **)resource);
if (FAILED(hr))
{
DEBUG_WINERROR("Failed to create the FrameBuffer ID3D12Resource", hr);
goto exit;
}
// cache the resource
comRef_toGlobal(fb->resource, resource);
result = *fb->resource;
ID3D12Resource_AddRef(result);
exit:
comRef_scopePop();
return result;
}
void d12_updatePointer(CapturePointer * pointer, void * shape, size_t shapeSize)
{
if (pointer->shapeUpdate)
{
void * dst;
UINT dstSize;
if (!this->getPointerBufferFn(&dst, &dstSize))
{
DEBUG_ERROR("Failed to obtain a buffer for the pointer shape");
pointer->shapeUpdate = false;
}
size_t copySize = min(dstSize, shapeSize);
memcpy(dst, shape, copySize);
}
this->postPointerBufferFn(pointer);
}
struct CaptureInterface Capture_D12 =
{
.shortName = "D12",
.asyncCapture = false,
.getName = d12_getName,
.initOptions = d12_initOptions,
.create = d12_create,
.init = d12_init,
.stop = d12_stop,
.deinit = d12_deinit,
.free = d12_free,
.capture = d12_capture,
.waitFrame = d12_waitFrame,
.getFrame = d12_getFrame
};