From 0c3dce3ca6522c04d5d510d6f2619764fe820b7d Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Tue, 11 Apr 2023 16:55:58 +1000 Subject: [PATCH] [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. --- idd/LGIdd/CIndirectDeviceContext.cpp | 106 ++++++++++++++- idd/LGIdd/CIndirectDeviceContext.h | 9 ++ idd/LGIdd/CIndirectMonitorContext.cpp | 7 +- idd/LGIdd/CIndirectMonitorContext.h | 4 +- idd/LGIdd/CPlatformInfo.cpp | 5 + idd/LGIdd/CSwapChainProcessor.cpp | 177 +++++++++++++++++++++++--- idd/LGIdd/CSwapChainProcessor.h | 39 +++++- idd/LGIdd/Device.cpp | 1 + 8 files changed, 315 insertions(+), 33 deletions(-) diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index 654d16fe..30ed13cd 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -4,6 +4,8 @@ #include "CPlatformInfo.h" #include "Debug.h" +#include + static const struct LGMPQueueConfig FRAME_QUEUE_CONFIG = { LGMP_Q_FRAME, //queueID @@ -44,7 +46,7 @@ void CIndirectDeviceContext::InitAdapter() return; IDDCX_ADAPTER_CAPS caps = {}; - caps.Size = sizeof(caps); + caps.Size = sizeof(caps); caps.MaxMonitorsSupported = 1; @@ -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 - 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(calloc(1, sizeof(*vmInfo))); + if (!vmInfo) + { + DBGPRINT("Failed to allocate KVMFRRecord_VMInfo"); + return false; + } vmInfo->cpus = static_cast(CPlatformInfo::GetProcCount ()); vmInfo->cores = static_cast(CPlatformInfo::GetCoreCount ()); vmInfo->sockets = static_cast(CPlatformInfo::GetSocketCount()); @@ -186,6 +197,12 @@ bool CIndirectDeviceContext::SetupLGMP() strncpy_s(vmInfo->capture, "Idd Driver", sizeof(vmInfo->capture)); KVMFRRecord * record = static_cast(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(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(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; @@ -329,4 +358,73 @@ 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; } \ No newline at end of file diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 61d6a730..611db8e2 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -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 diff --git a/idd/LGIdd/CIndirectMonitorContext.cpp b/idd/LGIdd/CIndirectMonitorContext.cpp index dbebaa43..6d8fe0cd 100644 --- a/idd/LGIdd/CIndirectMonitorContext.cpp +++ b/idd/LGIdd/CIndirectMonitorContext.cpp @@ -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() diff --git a/idd/LGIdd/CIndirectMonitorContext.h b/idd/LGIdd/CIndirectMonitorContext.h index ea9a3cc8..23cfb704 100644 --- a/idd/LGIdd/CIndirectMonitorContext.h +++ b/idd/LGIdd/CIndirectMonitorContext.h @@ -5,16 +5,18 @@ #include #include +#include "CIndirectDeviceContext.h" #include "CSwapChainProcessor.h" class CIndirectMonitorContext { protected: IDDCX_MONITOR m_monitor; + CIndirectDeviceContext * m_devContext; std::unique_ptr m_thread; public: - CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor); + CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device); virtual ~CIndirectMonitorContext(); diff --git a/idd/LGIdd/CPlatformInfo.cpp b/idd/LGIdd/CPlatformInfo.cpp index ac184c8d..0df3769f 100644 --- a/idd/LGIdd/CPlatformInfo.cpp +++ b/idd/LGIdd/CPlatformInfo.cpp @@ -218,6 +218,11 @@ void CPlatformInfo::InitCPUInfo() } BYTE * buffer = static_cast(_malloca(cb)); + if (!buffer) + { + DBGPRINT("Failed to allocate buffer for processor information"); + return; + } if (!GetLogicalProcessorInformationEx(RelationAll, (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)buffer, &cb)) { diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index 69e4d766..764499fd 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -1,36 +1,48 @@ #include "CSwapChainProcessor.h" #include +#include "Debug.h" +#define LOCK_CONTEXT() \ + while (InterlockedCompareExchange((volatile LONG*)&m_contextLock, 1, 0) != 0) {}; -CSwapChainProcessor::CSwapChainProcessor(IDDCX_SWAPCHAIN hSwapChain, std::shared_ptr device, HANDLE newFrameEvent) : +#define UNLOCK_CONTEXT() \ + InterlockedExchange((volatile LONG*)&m_contextLock, 0); + +CSwapChainProcessor::CSwapChainProcessor(CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain, + std::shared_ptr 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(argument)->Run(); + reinterpret_cast(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 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 acquiredBuffer; + ComPtr acquiredBuffer; IDARG_OUT_RELEASEANDACQUIREBUFFER buffer = {}; - hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &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 acquiredBuffer) +{ + Microsoft::WRL::ComPtr 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(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; } \ No newline at end of file diff --git a/idd/LGIdd/CSwapChainProcessor.h b/idd/LGIdd/CSwapChainProcessor.h index eff8511f..43359712 100644 --- a/idd/LGIdd/CSwapChainProcessor.h +++ b/idd/LGIdd/CSwapChainProcessor.h @@ -1,30 +1,55 @@ #pragma once #include "Direct3DDevice.h" +#include "CIndirectDeviceContext.h" #include #include #include #include +using namespace Microsoft::WRL; + +#define STAGING_TEXTURES 3 + class CSwapChainProcessor { private: + CIndirectDeviceContext * m_devContext; IDDCX_SWAPCHAIN m_hSwapChain; std::shared_ptr m_device; HANDLE m_newFrameEvent; - Microsoft::WRL::Wrappers::HandleT< - Microsoft::WRL::Wrappers::HandleTraits::HANDLENullTraits> m_thread; - Microsoft::WRL::Wrappers::Event m_terminateEvent; + Wrappers::HandleT 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 acquiredBuffer); + + void FrameThread(); + + struct StagingTexture + { + int width; + int height; + DXGI_FORMAT format; + Microsoft::WRL::ComPtr 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 device, HANDLE newFrameEvent); ~CSwapChainProcessor(); }; \ No newline at end of file diff --git a/idd/LGIdd/Device.cpp b/idd/LGIdd/Device.cpp index cbee809b..d0ca822a 100644 --- a/idd/LGIdd/Device.cpp +++ b/idd/LGIdd/Device.cpp @@ -9,6 +9,7 @@ #include #include +#include "Debug.h" #include "CIndirectDeviceContext.h" #include "CIndirectMonitorContext.h"