[idd] implemented frame feed from the guest (very hacky)

This is NOT READY for general consumption, if you decide to make
use of this driver, DO NOT ASK FOR SUPPORT.
This commit is contained in:
Geoffrey McRae 2023-04-11 16:55:58 +10:00
parent 3c85957b99
commit 0c3dce3ca6
8 changed files with 315 additions and 33 deletions

View File

@ -4,6 +4,8 @@
#include "CPlatformInfo.h"
#include "Debug.h"
#include <sstream>
static const struct LGMPQueueConfig FRAME_QUEUE_CONFIG =
{
LGMP_Q_FRAME, //queueID
@ -73,7 +75,10 @@ void CIndirectDeviceContext::InitAdapter()
IDARG_OUT_ADAPTER_INIT initOut;
NTSTATUS status = IddCxAdapterInitAsync(&init, &initOut);
if (!NT_SUCCESS(status))
{
DBGPRINT("IddCxAdapterInitAsync Failed");
return;
}
m_adapter = initOut.AdapterObject;
@ -147,17 +152,18 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
IDARG_OUT_MONITORCREATE createOut;
NTSTATUS status = IddCxMonitorCreate(m_adapter, &create, &createOut);
if (!NT_SUCCESS(status))
{
DBGPRINT("IddCxMonitorCreate Failed");
return;
}
auto * wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(createOut.MonitorObject);
wrapper->context = new CIndirectMonitorContext(createOut.MonitorObject);
wrapper->context = new CIndirectMonitorContext(createOut.MonitorObject, this);
IDARG_OUT_MONITORARRIVAL out;
status = IddCxMonitorArrival(createOut.MonitorObject, &out);
}
#include <sstream>
bool CIndirectDeviceContext::SetupLGMP()
{
if (!m_ivshmem.Init() || !m_ivshmem.Open())
@ -177,6 +183,11 @@ bool CIndirectDeviceContext::SetupLGMP()
const std::string & model = CPlatformInfo::GetCPUModel();
KVMFRRecord_VMInfo * vmInfo = static_cast<KVMFRRecord_VMInfo *>(calloc(1, sizeof(*vmInfo)));
if (!vmInfo)
{
DBGPRINT("Failed to allocate KVMFRRecord_VMInfo");
return false;
}
vmInfo->cpus = static_cast<uint8_t>(CPlatformInfo::GetProcCount ());
vmInfo->cores = static_cast<uint8_t>(CPlatformInfo::GetCoreCount ());
vmInfo->sockets = static_cast<uint8_t>(CPlatformInfo::GetSocketCount());
@ -186,6 +197,12 @@ bool CIndirectDeviceContext::SetupLGMP()
strncpy_s(vmInfo->capture, "Idd Driver", sizeof(vmInfo->capture));
KVMFRRecord * record = static_cast<KVMFRRecord *>(calloc(1, sizeof(*record)));
if (!record)
{
DBGPRINT("Failed to allocate KVMFRRecord");
return false;
}
record->type = KVMFR_RECORD_VMINFO;
record->size = sizeof(*vmInfo) + (uint32_t)model.length() + 1;
@ -196,11 +213,23 @@ bool CIndirectDeviceContext::SetupLGMP()
{
KVMFRRecord_OSInfo * osInfo = static_cast<KVMFRRecord_OSInfo *>(calloc(1, sizeof(*osInfo)));
if (!osInfo)
{
DBGPRINT("Failed to allocate KVMFRRecord_OSInfo");
return false;
}
osInfo->os = KVMFR_OS_WINDOWS;
const std::string & osName = CPlatformInfo::GetProductName();
KVMFRRecord* record = static_cast<KVMFRRecord*>(calloc(1, sizeof(*record)));
if (!record)
{
DBGPRINT("Failed to allocate KVMFRRecord");
return false;
}
record->type = KVMFR_RECORD_OSINFO;
record->size = sizeof(*osInfo) + (uint32_t)osName.length() + 1;
@ -330,3 +359,72 @@ void CIndirectDeviceContext::LGMPTimer()
lgmpHostAckData(m_pointerQueue);
}
}
void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data)
{
if (!m_lgmp || !m_frameQueue)
return;
if (m_width != width || m_height != height || m_format != format)
{
m_width = width;
m_height = height;
m_format = format;
++m_formatVer;
}
while (lgmpHostQueuePending(m_frameQueue) == LGMP_Q_FRAME_LEN)
Sleep(0);
if (++m_frameIndex == LGMP_Q_FRAME_LEN)
m_frameIndex = 0;
KVMFRFrame * fi = (KVMFRFrame *)lgmpHostMemPtr(m_frameMemory[m_frameIndex]);
int bpp = 4;
switch (format)
{
case DXGI_FORMAT_B8G8R8A8_UNORM : fi->type = FRAME_TYPE_BGRA ; break;
case DXGI_FORMAT_R8G8B8A8_UNORM : fi->type = FRAME_TYPE_RGBA ; break;
case DXGI_FORMAT_R10G10B10A2_UNORM : fi->type = FRAME_TYPE_RGBA10 ; break;
case DXGI_FORMAT_R16G16B16A16_FLOAT:
fi->type = FRAME_TYPE_RGBA16F;
bpp = 8;
break;
default:
DBGPRINT("Unsuppoted DXGI format");
return;
}
//FIXME: this should not really be done here, this is a hack
#pragma warning(push)
#pragma warning(disable: 4200)
struct FrameBuffer
{
volatile uint32_t wp;
uint8_t data[0];
};
#pragma warning(pop)
fi->formatVer = m_formatVer;
fi->frameSerial = m_frameSerial++;
fi->screenWidth = width;
fi->screenHeight = height;
fi->frameWidth = width;
fi->frameHeight = height;
fi->stride = width * bpp;
fi->pitch = pitch;
fi->offset = (uint32_t)(CPlatformInfo::GetPageSize() - sizeof(FrameBuffer));
fi->flags = 0;
fi->rotation = FRAME_ROT_0;
fi->damageRectsCount = 0;
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
fb->wp = 0;
lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]);
memcpy(fb->data, data, (size_t)height * (size_t)pitch);
fb->wp = height * pitch;
}

View File

@ -29,8 +29,15 @@ private:
PLGMPMemory m_pointerShapeMemory[POINTER_SHAPE_BUFFERS] = {};
size_t m_maxFrameSize = 0;
int m_frameIndex = 0;
uint32_t m_formatVer = 0;
uint32_t m_frameSerial = 0;
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_LEN] = {};
int m_width = 0;
int m_height = 0;
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
bool SetupLGMP();
void LGMPTimer();
@ -46,6 +53,8 @@ public:
void InitAdapter();
void FinishInit(UINT connectorIndex);
void SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data);
};
struct CIndirectDeviceContextWrapper

View File

@ -1,8 +1,9 @@
#include "CIndirectMonitorContext.h"
#include "Direct3DDevice.h"
CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor) :
m_monitor(monitor)
CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device) :
m_monitor(monitor),
m_devContext(device)
{
}
@ -21,7 +22,7 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re
return;
}
m_thread.reset(new CSwapChainProcessor(swapChain, device, newFrameEvent));
m_thread.reset(new CSwapChainProcessor(m_devContext, swapChain, device, newFrameEvent));
}
void CIndirectMonitorContext::UnassignSwapChain()

View File

@ -5,16 +5,18 @@
#include <IddCx.h>
#include <memory>
#include "CIndirectDeviceContext.h"
#include "CSwapChainProcessor.h"
class CIndirectMonitorContext
{
protected:
IDDCX_MONITOR m_monitor;
CIndirectDeviceContext * m_devContext;
std::unique_ptr<CSwapChainProcessor> m_thread;
public:
CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor);
CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device);
virtual ~CIndirectMonitorContext();

View File

@ -218,6 +218,11 @@ void CPlatformInfo::InitCPUInfo()
}
BYTE * buffer = static_cast<BYTE *>(_malloca(cb));
if (!buffer)
{
DBGPRINT("Failed to allocate buffer for processor information");
return;
}
if (!GetLogicalProcessorInformationEx(RelationAll,
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)buffer, &cb))
{

View File

@ -1,36 +1,48 @@
#include "CSwapChainProcessor.h"
#include <avrt.h>
#include "Debug.h"
#define LOCK_CONTEXT() \
while (InterlockedCompareExchange((volatile LONG*)&m_contextLock, 1, 0) != 0) {};
CSwapChainProcessor::CSwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent) :
#define UNLOCK_CONTEXT() \
InterlockedExchange((volatile LONG*)&m_contextLock, 0);
CSwapChainProcessor::CSwapChainProcessor(CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain,
std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent) :
m_devContext(devContext),
m_hSwapChain(hSwapChain),
m_device(device),
m_newFrameEvent(newFrameEvent)
{
m_terminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_thread.Attach(CreateThread(nullptr, 0, RunThread, this, 0, nullptr));
m_thread[0].Attach(CreateThread(nullptr, 0, _SwapChainThread, this, 0, nullptr));
m_thread[1].Attach(CreateThread(nullptr, 0, _FrameThread , this, 0, nullptr));
}
CSwapChainProcessor::~CSwapChainProcessor()
{
SetEvent(m_terminateEvent.Get());
if (m_thread.Get())
WaitForSingleObject(m_thread.Get(), INFINITE);
if (m_thread[0].Get())
WaitForSingleObject(m_thread[0].Get(), INFINITE);
if (m_thread[1].Get())
WaitForSingleObject(m_thread[1].Get(), INFINITE);
}
DWORD CALLBACK CSwapChainProcessor::RunThread(LPVOID argument)
DWORD CALLBACK CSwapChainProcessor::_SwapChainThread(LPVOID arg)
{
reinterpret_cast<CSwapChainProcessor*>(argument)->Run();
reinterpret_cast<CSwapChainProcessor*>(arg)->SwapChainThread();
return 0;
}
void CSwapChainProcessor::Run()
void CSwapChainProcessor::SwapChainThread()
{
DWORD avTask = 0;
HANDLE avTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &avTask);
RunCore();
DBGPRINT("Start");
SwapChainThreadCore();
WdfObjectDelete((WDFOBJECT)m_hSwapChain);
m_hSwapChain = nullptr;
@ -38,28 +50,41 @@ void CSwapChainProcessor::Run()
AvRevertMmThreadCharacteristics(avTaskHandle);
}
void CSwapChainProcessor::RunCore()
void CSwapChainProcessor::SwapChainThreadCore()
{
Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
HRESULT hr = m_device->m_device.As(&dxgiDevice);
if (FAILED(hr))
{
DBGPRINT("Failed to get the dxgiDevice");
return;
}
IDARG_IN_SWAPCHAINSETDEVICE setDevice = {};
setDevice.pDevice = dxgiDevice.Get();
LOCK_CONTEXT();
hr = IddCxSwapChainSetDevice(m_hSwapChain, &setDevice);
if (FAILED(hr))
return;
UNLOCK_CONTEXT();
if (FAILED(hr))
{
DBGPRINT("IddCxSwapChainSetDevice Failed");
return;
}
UINT lastFrameNumber = 0;
for (;;)
{
Microsoft::WRL::ComPtr<IDXGIResource> acquiredBuffer;
ComPtr<IDXGIResource> acquiredBuffer;
IDARG_OUT_RELEASEANDACQUIREBUFFER buffer = {};
LOCK_CONTEXT();
hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &buffer);
if (hr == E_PENDING)
{
UNLOCK_CONTEXT();
HANDLE waitHandles[] =
{
m_newFrameEvent,
@ -78,16 +103,132 @@ void CSwapChainProcessor::RunCore()
}
else if (SUCCEEDED(hr))
{
//acquiredBuffer.Attach(buffer.MetaData.pSurface);
if (buffer.MetaData.PresentationFrameNumber != lastFrameNumber)
{
lastFrameNumber = buffer.MetaData.PresentationFrameNumber;
if (m_copyCount < STAGING_TEXTURES)
{
acquiredBuffer.Attach(buffer.MetaData.pSurface);
SwapChainNewFrame(acquiredBuffer);
acquiredBuffer.Reset();
}
}
//TODO: process the frame
//acquiredBuffer.Reset();
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
UNLOCK_CONTEXT();
if (FAILED(hr))
break;
}
else
{
UNLOCK_CONTEXT();
break;
}
}
}
void CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer)
{
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
if (FAILED(acquiredBuffer->QueryInterface(__uuidof(ID3D11Texture2D), (void**)&texture)))
{
DBGPRINT("Failed to obtain the ID3D11Texture2D from the acquiredBuffer");
return;
}
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
if (!SetupStagingTexture(m_cpuTex[m_texWIndex], desc.Width, desc.Height, desc.Format))
return;
m_device->m_context->CopyResource(m_cpuTex[m_texWIndex].tex.Get(), texture.Get());
InterlockedAdd(&m_copyCount, 1);
if (++m_texWIndex == STAGING_TEXTURES)
m_texWIndex = 0;
}
DWORD CALLBACK CSwapChainProcessor::_FrameThread(LPVOID arg)
{
reinterpret_cast<CSwapChainProcessor*>(arg)->FrameThread();
return 0;
}
void CSwapChainProcessor::FrameThread()
{
for(;;)
{
if (WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0)
break;
if (!m_copyCount)
{
Sleep(0);
continue;
}
D3D11_MAPPED_SUBRESOURCE map;
StagingTexture & t = m_cpuTex[m_texRIndex];
LOCK_CONTEXT();
HRESULT status = m_device->m_context->Map(t.tex.Get(), 0, D3D11_MAP_READ, D3D11_MAP_FLAG_DO_NOT_WAIT, &map);
UNLOCK_CONTEXT();
if (FAILED(status))
{
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
continue;
DBGPRINT("Failed to map staging texture");
InterlockedAdd(&m_copyCount, -1);
if (++m_texRIndex == STAGING_TEXTURES)
m_texRIndex = 0;
continue;
}
m_devContext->SendFrame(t.width, t.height, map.RowPitch, t.format, map.pData);
LOCK_CONTEXT();
m_device->m_context->Unmap(t.tex.Get(), 0);
UNLOCK_CONTEXT();
InterlockedAdd(&m_copyCount, -1);
if (++m_texRIndex == STAGING_TEXTURES)
m_texRIndex = 0;
}
}
bool CSwapChainProcessor::SetupStagingTexture(StagingTexture & t, int width, int height, DXGI_FORMAT format)
{
if (t.width == width && t.height == height && t.format == format)
return true;
t.tex.Reset();
t.width = width;
t.height = height;
t.format = format;
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.Format = format;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
if (FAILED(m_device->m_device->CreateTexture2D(&desc, nullptr, &t.tex)))
{
DBGPRINT("Failed to create staging texture");
return false;
}
return true;
}

View File

@ -1,30 +1,55 @@
#pragma once
#include "Direct3DDevice.h"
#include "CIndirectDeviceContext.h"
#include <Windows.h>
#include <wrl.h>
#include <IddCx.h>
#include <memory>
using namespace Microsoft::WRL;
#define STAGING_TEXTURES 3
class CSwapChainProcessor
{
private:
CIndirectDeviceContext * m_devContext;
IDDCX_SWAPCHAIN m_hSwapChain;
std::shared_ptr<Direct3DDevice> m_device;
HANDLE m_newFrameEvent;
Microsoft::WRL::Wrappers::HandleT<
Microsoft::WRL::Wrappers::HandleTraits::HANDLENullTraits> m_thread;
Microsoft::WRL::Wrappers::Event m_terminateEvent;
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[2];
Wrappers::Event m_terminateEvent;
static DWORD CALLBACK RunThread(LPVOID argument);
static DWORD CALLBACK _SwapChainThread(LPVOID arg);
static DWORD CALLBACK _FrameThread(LPVOID arg);
void Run();
void RunCore();
void SwapChainThread();
void SwapChainThreadCore();
void SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer);
void FrameThread();
struct StagingTexture
{
int width;
int height;
DXGI_FORMAT format;
Microsoft::WRL::ComPtr<ID3D11Texture2D> tex;
};
StagingTexture m_cpuTex[STAGING_TEXTURES] = {};
volatile LONG m_copyCount = 0;
volatile LONG m_contextLock = 0;
int m_texRIndex = 0;
int m_texWIndex = 0;
bool SetupStagingTexture(StagingTexture & t, int width, int height, DXGI_FORMAT format);
public:
CSwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain,
CSwapChainProcessor(CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent);
~CSwapChainProcessor();
};

View File

@ -9,6 +9,7 @@
#include <avrt.h>
#include <wrl.h>
#include "Debug.h"
#include "CIndirectDeviceContext.h"
#include "CIndirectMonitorContext.h"