mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[idd] transport: abstract LGMP implementation
Introduce transport, frame, and control interfaces with an LGMP factory backend. Move LGMP and IVSHMEM implementation details under transport/lgmp and expose direct frame-buffer memory through a neutral capability.
This commit is contained in:
@@ -21,7 +21,6 @@
|
||||
#include "display/CDisplayConfiguration.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "common/LGMPConfig.h"
|
||||
#include "util/CSRWLock.h"
|
||||
|
||||
#include <d3d12.h>
|
||||
@@ -59,12 +58,12 @@ bool CDisplayConfiguration::CalculateFrameSize(
|
||||
|
||||
bool CDisplayConfiguration::GetResolutionMemoryRequirements(
|
||||
uint32_t width, uint32_t height, UINT64 alignment,
|
||||
const FrameMemoryLimits& limits, UINT64& frameSize, UINT64& sharedSize)
|
||||
const FrameMemoryLimits& limits, UINT64& frameSize, UINT64& requiredSize)
|
||||
{
|
||||
frameSize = 0;
|
||||
sharedSize = 0;
|
||||
frameSize = 0;
|
||||
requiredSize = 0;
|
||||
|
||||
if (!alignment || !limits.frameMemoryOffset ||
|
||||
if (!alignment || !limits.frameMemoryOffset || !limits.bufferCount ||
|
||||
!CalculateFrameSize(width, height, frameSize))
|
||||
return false;
|
||||
|
||||
@@ -76,12 +75,12 @@ bool CDisplayConfiguration::GetResolutionMemoryRequirements(
|
||||
if (!AlignUp(limits.frameMemoryOffset, alignment, frameMemoryStart))
|
||||
return false;
|
||||
|
||||
sharedSize = frameMemoryStart +
|
||||
frameAllocationSize * LGMP_Q_FRAME_BUFFER_LEN;
|
||||
requiredSize = frameMemoryStart +
|
||||
frameAllocationSize * limits.bufferCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t CDisplayConfiguration::RecommendedIVSHMEMSizeMiB(
|
||||
uint32_t CDisplayConfiguration::RecommendedMemorySizeMiB(
|
||||
UINT64 requiredSize)
|
||||
{
|
||||
UINT64 sizeMiB = requiredSize / 1048576;
|
||||
@@ -135,10 +134,10 @@ bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits)
|
||||
for (const auto& configuredMode : configuredModes)
|
||||
{
|
||||
UINT64 frameSize;
|
||||
UINT64 requiredIVSHMEMSize;
|
||||
UINT64 requiredMemorySize;
|
||||
if (!GetResolutionMemoryRequirements(configuredMode.width,
|
||||
configuredMode.height, alignment, limits, frameSize,
|
||||
requiredIVSHMEMSize))
|
||||
requiredMemorySize))
|
||||
{
|
||||
DEBUG_WARN("Filtering invalid %s mode %ux%u@%.3f",
|
||||
configuredMode.extraMode ? "extra" : "configured",
|
||||
@@ -147,15 +146,15 @@ bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (requiredIVSHMEMSize > limits.sharedSize)
|
||||
if (requiredMemorySize > limits.capacity)
|
||||
{
|
||||
DEBUG_WARN(
|
||||
"Filtering %s mode %ux%u@%.3f: requires %llu bytes of IVSHMEM, only %llu bytes are available",
|
||||
"Filtering %s mode %ux%u@%.3f: requires %llu bytes of transport memory, only %llu bytes are available",
|
||||
configuredMode.extraMode ? "extra" : "configured",
|
||||
configuredMode.width, configuredMode.height,
|
||||
configuredMode.refreshMilliHz / 1000.0,
|
||||
(unsigned long long)requiredIVSHMEMSize,
|
||||
(unsigned long long)limits.sharedSize);
|
||||
(unsigned long long)requiredMemorySize,
|
||||
(unsigned long long)limits.capacity);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -170,7 +169,7 @@ bool CDisplayConfiguration::LoadModes(const FrameMemoryLimits& limits)
|
||||
|
||||
if (newModes.empty())
|
||||
{
|
||||
DEBUG_ERROR("No configured display modes fit in IVSHMEM");
|
||||
DEBUG_ERROR("No configured display modes fit in transport memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -225,20 +224,20 @@ CDisplayConfiguration::SetResolution(
|
||||
ResolutionResult result;
|
||||
|
||||
UINT64 frameSize;
|
||||
UINT64 requiredIVSHMEMSize;
|
||||
UINT64 requiredMemorySize;
|
||||
if (!GetResolutionMemoryRequirements(width, height, limits.alignment,
|
||||
limits, frameSize, requiredIVSHMEMSize))
|
||||
limits, frameSize, requiredMemorySize))
|
||||
{
|
||||
DEBUG_WARN("Ignoring invalid resolution request: %ux%u", width, height);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (requiredIVSHMEMSize > limits.sharedSize)
|
||||
if (requiredMemorySize > limits.capacity)
|
||||
{
|
||||
result.status = ResolutionStatus::TOO_LARGE;
|
||||
result.requiredMiB = RecommendedIVSHMEMSizeMiB(requiredIVSHMEMSize);
|
||||
result.requiredMiB = RecommendedMemorySizeMiB(requiredMemorySize);
|
||||
DEBUG_WARN(
|
||||
"Refusing resolution %ux%u: frame requires %llu bytes, only %llu bytes are available; IVSHMEM must be at least %u MiB",
|
||||
"Refusing resolution %ux%u: frame requires %llu bytes, only %llu bytes are available; transport memory must be at least %u MiB",
|
||||
width, height,
|
||||
(unsigned long long)frameSize,
|
||||
(unsigned long long)limits.maxFrameSize,
|
||||
|
||||
@@ -74,8 +74,8 @@ private:
|
||||
UINT64& frameSize);
|
||||
static bool GetResolutionMemoryRequirements(uint32_t width,
|
||||
uint32_t height, UINT64 alignment, const FrameMemoryLimits& limits,
|
||||
UINT64& frameSize, UINT64& sharedSize);
|
||||
static uint32_t RecommendedIVSHMEMSizeMiB(UINT64 requiredSize);
|
||||
UINT64& frameSize, UINT64& requiredSize);
|
||||
static uint32_t RecommendedMemorySizeMiB(UINT64 requiredSize);
|
||||
|
||||
public:
|
||||
explicit CDisplayConfiguration(CSettings& settings);
|
||||
|
||||
@@ -57,7 +57,7 @@ private:
|
||||
IDDCX_MONITOR m_monitor = nullptr;
|
||||
|
||||
// Guards the monitor/replug/swap-chain state. These values are touched by
|
||||
// IddCx callback threads, the swap-chain thread, and the LGMP timer.
|
||||
// IddCx callback threads, the swap-chain thread, and the transport timer.
|
||||
SRWLOCK m_lock = SRWLOCK_INIT;
|
||||
|
||||
bool m_replugMonitor = false;
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "display/IddCxCompat.h"
|
||||
#include "transport/CPipeServer.h"
|
||||
#include "transport/IFrameTransport.h"
|
||||
#include "transport/TransportFactory.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
#include <dxgi1_2.h>
|
||||
@@ -33,8 +35,7 @@ static const UINT IDDCX_VERSION_1_10 = 0x1A00;
|
||||
|
||||
CDeviceContext::CDeviceContext(WDFDEVICE wdfDevice) :
|
||||
m_wdfDevice(wdfDevice),
|
||||
m_lgmpControl(m_lgmpHost),
|
||||
m_frameTransport(m_lgmpHost, m_ivshmem),
|
||||
m_transport(CreateTransport()),
|
||||
m_displayConfiguration(g_settings)
|
||||
{
|
||||
}
|
||||
@@ -49,10 +50,10 @@ CDeviceContext::~CDeviceContext()
|
||||
m_initTimer = nullptr;
|
||||
}
|
||||
|
||||
if (m_lgmpTimer)
|
||||
if (m_transportTimer)
|
||||
{
|
||||
WdfTimerStop(m_lgmpTimer, TRUE);
|
||||
m_lgmpTimer = nullptr;
|
||||
WdfTimerStop(m_transportTimer, TRUE);
|
||||
m_transportTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,19 +157,32 @@ void CDeviceContext::InitAdapter()
|
||||
return;
|
||||
}
|
||||
|
||||
// At boot the IVSHMEM PCI device may not have enumerated yet. Rather than
|
||||
// At boot the selected transport may not be available yet. Rather than
|
||||
// silently abandoning the adapter (leaving the device loaded but with no
|
||||
// monitor), retry from a timer until the shared memory becomes available.
|
||||
if (!m_ivshmemOpened)
|
||||
// monitor), retry from a timer until it can be opened.
|
||||
if (!m_transportOpened)
|
||||
{
|
||||
if (!m_ivshmem.Init() || !m_ivshmem.Open())
|
||||
if (!m_transport)
|
||||
{
|
||||
DEBUG_WARN("IVSHMEM not available yet, scheduling init retry");
|
||||
ScheduleInitRetry();
|
||||
DEBUG_ERROR("Failed to create the frame transport");
|
||||
m_initInProgress.store(0);
|
||||
return;
|
||||
}
|
||||
m_ivshmemOpened = true;
|
||||
|
||||
const ITransport::OpenResult result = m_transport->Open();
|
||||
if (result != ITransport::OpenResult::SUCCESS)
|
||||
{
|
||||
if (result == ITransport::OpenResult::RETRY)
|
||||
{
|
||||
DEBUG_WARN("Frame transport not available yet, scheduling init retry");
|
||||
ScheduleInitRetry();
|
||||
}
|
||||
else
|
||||
DEBUG_ERROR("Failed to open the frame transport");
|
||||
m_initInProgress.store(0);
|
||||
return;
|
||||
}
|
||||
m_transportOpened = true;
|
||||
}
|
||||
|
||||
// Select the render adapter before advertising capabilities. If no hardware
|
||||
@@ -234,14 +248,14 @@ void CDeviceContext::InitAdapter()
|
||||
DEBUG_INFO("No hardware render adapter available; using SDR software mode");
|
||||
|
||||
QueryIddCxCapabilities();
|
||||
DEBUG_TRACE("Initializing LGMP metadata");
|
||||
if (!InitializeLGMP())
|
||||
DEBUG_TRACE("Initializing frame transport metadata");
|
||||
if (!InitializeTransport())
|
||||
{
|
||||
m_initInProgress.store(0);
|
||||
return;
|
||||
}
|
||||
DEBUG_TRACE("Loading configured display modes");
|
||||
if (!m_displayConfiguration.Load(m_frameTransport.GetMemoryLimits()))
|
||||
if (!m_displayConfiguration.Load(m_transport->GetMemoryLimits()))
|
||||
{
|
||||
m_initInProgress.store(0);
|
||||
return;
|
||||
@@ -374,7 +388,7 @@ void CDeviceContext::ReplugMonitor()
|
||||
void CDeviceContext::ReloadSettings()
|
||||
{
|
||||
if (!m_displayConfiguration.ReloadSettings(
|
||||
m_frameTransport.GetMemoryLimits()))
|
||||
m_transport->GetMemoryLimits()))
|
||||
return;
|
||||
|
||||
ReplugMonitor();
|
||||
@@ -417,7 +431,7 @@ void CDeviceContext::SetResolution(uint32_t width, uint32_t height)
|
||||
{
|
||||
const CDisplayConfiguration::ResolutionResult result =
|
||||
m_displayConfiguration.SetResolution(
|
||||
width, height, m_frameTransport.GetMemoryLimits());
|
||||
width, height, m_transport->GetMemoryLimits());
|
||||
|
||||
switch (result.status)
|
||||
{
|
||||
@@ -437,33 +451,21 @@ void CDeviceContext::SetResolution(uint32_t width, uint32_t height)
|
||||
}
|
||||
}
|
||||
|
||||
// LGMP transport
|
||||
// Frame transport
|
||||
|
||||
bool CDeviceContext::InitializeLGMP()
|
||||
bool CDeviceContext::InitializeTransport()
|
||||
{
|
||||
if (m_lgmpHost.IsInitialized())
|
||||
return true;
|
||||
|
||||
if (!m_lgmpHost.Initialize(m_ivshmem))
|
||||
return false;
|
||||
|
||||
// Preserve the shared-memory layout: frame queues precede the pointer queue
|
||||
// and its retained cursor and color-transform allocations.
|
||||
if (!m_frameTransport.Initialize() || !m_lgmpControl.Initialize())
|
||||
return false;
|
||||
|
||||
m_frameTransport.SealMemoryLayout();
|
||||
return true;
|
||||
return m_transport && m_transport->Initialize();
|
||||
}
|
||||
|
||||
bool CDeviceContext::SetupLGMP(size_t alignSize)
|
||||
bool CDeviceContext::SetupTransport(size_t alignSize)
|
||||
{
|
||||
// Frame buffers cannot be allocated until the GPU-specific alignment is
|
||||
// known. The swap-chain path may call this again after setup completed.
|
||||
if (m_frameTransport.GetMaxFrameSize())
|
||||
if (m_transport->Frames().GetMaxFrameSize())
|
||||
return true;
|
||||
|
||||
if (!InitializeLGMP() || !m_frameTransport.Setup(alignSize))
|
||||
if (!InitializeTransport() || !m_transport->Setup(alignSize))
|
||||
return false;
|
||||
|
||||
WDF_TIMER_CONFIG config;
|
||||
@@ -472,7 +474,7 @@ bool CDeviceContext::SetupLGMP(size_t alignSize)
|
||||
{
|
||||
WDFOBJECT parent = WdfTimerGetParentObject(timer);
|
||||
auto wrapper = WdfObjectGet_CDeviceContextWrapper(parent);
|
||||
wrapper->context->LGMPTimer();
|
||||
wrapper->context->TransportTimer();
|
||||
},
|
||||
10);
|
||||
config.AutomaticSerialization = FALSE;
|
||||
@@ -487,18 +489,18 @@ bool CDeviceContext::SetupLGMP(size_t alignSize)
|
||||
attribs.ExecutionLevel = WdfExecutionLevelDispatch;
|
||||
|
||||
NTSTATUS status = WdfTimerCreate(
|
||||
&config, &attribs, &m_lgmpTimer);
|
||||
&config, &attribs, &m_transportTimer);
|
||||
if (!NT_SUCCESS(status))
|
||||
{
|
||||
DEBUG_ERROR_HR(status, "Timer creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
WdfTimerStart(m_lgmpTimer, WDF_REL_TIMEOUT_IN_MS(10));
|
||||
WdfTimerStart(m_transportTimer, WDF_REL_TIMEOUT_IN_MS(10));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDeviceContext::LGMPTimer()
|
||||
void CDeviceContext::TransportTimer()
|
||||
{
|
||||
// Monitor work is deferred off IddCx callback threads.
|
||||
switch (m_monitorManager.TakeDeferredAction())
|
||||
@@ -515,74 +517,15 @@ void CDeviceContext::LGMPTimer()
|
||||
break;
|
||||
}
|
||||
|
||||
const LGMP_STATUS processStatus = m_lgmpHost.Process();
|
||||
if (processStatus != LGMP_OK)
|
||||
{
|
||||
if (processStatus == LGMP_ERR_CORRUPTED)
|
||||
{
|
||||
DEBUG_WARN(
|
||||
"LGMP reported the shared memory has been corrupted, attempting to recover\n");
|
||||
// TODO: reinitialize LGMP.
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_ERROR("lgmpHostProcess Failed: %s",
|
||||
lgmpStatusString(processStatus));
|
||||
// TODO: shut down LGMP.
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t now = CFrameScheduler::Nanotime();
|
||||
|
||||
// Take the frame subscriber snapshot before processing scheduling messages,
|
||||
// then publish both updates together just as the original timer did.
|
||||
const CFrameTransport::SubscriberSnapshot subscribers =
|
||||
m_frameTransport.SnapshotSubscribers();
|
||||
|
||||
uint8_t data[LGMP_MSGS_SIZE];
|
||||
size_t size;
|
||||
uint32_t sourceClientID;
|
||||
LGMP_STATUS status;
|
||||
while ((status = m_lgmpControl.ReadDataWithSource(
|
||||
data, &size, &sourceClientID)) == LGMP_OK)
|
||||
{
|
||||
KVMFRMessage * msg = reinterpret_cast<KVMFRMessage *>(data);
|
||||
switch (msg->type)
|
||||
{
|
||||
case KVMFR_MESSAGE_SETCURSORPOS:
|
||||
{
|
||||
KVMFRSetCursorPos * position =
|
||||
reinterpret_cast<KVMFRSetCursorPos *>(msg);
|
||||
g_pipe.SetCursorPos(position->x, position->y);
|
||||
break;
|
||||
}
|
||||
|
||||
case KVMFR_MESSAGE_WINDOWSIZE:
|
||||
{
|
||||
KVMFRWindowSize * window =
|
||||
reinterpret_cast<KVMFRWindowSize *>(msg);
|
||||
SetResolution(window->w, window->h);
|
||||
break;
|
||||
}
|
||||
|
||||
case KVMFR_MESSAGE_FRAME_SCHEDULE:
|
||||
{
|
||||
const KVMFRFrameSchedule * schedule =
|
||||
reinterpret_cast<KVMFRFrameSchedule *>(msg);
|
||||
const bool valid = size == sizeof(*schedule) &&
|
||||
m_frameTransport.UpdateSchedule(
|
||||
sourceClientID, *schedule, now);
|
||||
if (!valid)
|
||||
DEBUG_WARN("Ignoring invalid KVMFR frame schedule");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_lgmpControl.AckData();
|
||||
}
|
||||
|
||||
m_frameTransport.FinalizeSubscribers(subscribers, now);
|
||||
|
||||
if (m_lgmpControl.HasNewSubscribers())
|
||||
m_lgmpControl.ResendState();
|
||||
m_transport->Process(*this);
|
||||
}
|
||||
|
||||
void CDeviceContext::OnSetCursorPos(int32_t x, int32_t y)
|
||||
{
|
||||
g_pipe.SetCursorPos(x, y);
|
||||
}
|
||||
|
||||
void CDeviceContext::OnSetResolution(uint32_t width, uint32_t height)
|
||||
{
|
||||
SetResolution(width, height);
|
||||
}
|
||||
|
||||
@@ -25,17 +25,15 @@
|
||||
#include <IddCx.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "display/CDisplayConfiguration.h"
|
||||
#include "display/CMonitorManager.h"
|
||||
#include "transport/CFrameTransport.h"
|
||||
#include "transport/CIVSHMEM.h"
|
||||
#include "transport/CLGMPControl.h"
|
||||
#include "transport/CLGMPHost.h"
|
||||
#include "transport/ITransport.h"
|
||||
|
||||
class CDeviceContext
|
||||
class CDeviceContext : private ITransportEvents
|
||||
{
|
||||
private:
|
||||
WDFDEVICE m_wdfDevice;
|
||||
@@ -43,20 +41,17 @@ private:
|
||||
LUID m_preferredRenderAdapter = {};
|
||||
bool m_havePreferredRenderAdapter = false;
|
||||
|
||||
// At boot IVSHMEM may not have enumerated yet. The retry timer and atomic
|
||||
// gate keep adapter creation single-threaded until it becomes available.
|
||||
WDFTIMER m_initTimer = nullptr;
|
||||
bool m_ivshmemOpened = false;
|
||||
std::atomic<LONG> m_initInProgress = 0;
|
||||
// At boot the selected transport may not be available yet. The retry timer
|
||||
// and atomic gate keep adapter creation single-threaded until it is ready.
|
||||
WDFTIMER m_initTimer = nullptr;
|
||||
bool m_transportOpened = false;
|
||||
std::atomic<LONG> m_initInProgress = 0;
|
||||
|
||||
CIVSHMEM m_ivshmem;
|
||||
CLGMPHost m_lgmpHost;
|
||||
CLGMPControl m_lgmpControl;
|
||||
CFrameTransport m_frameTransport;
|
||||
CDisplayConfiguration m_displayConfiguration;
|
||||
CMonitorManager m_monitorManager;
|
||||
std::unique_ptr<ITransport> m_transport;
|
||||
CDisplayConfiguration m_displayConfiguration;
|
||||
CMonitorManager m_monitorManager;
|
||||
|
||||
WDFTIMER m_lgmpTimer = nullptr;
|
||||
WDFTIMER m_transportTimer = nullptr;
|
||||
|
||||
UINT m_iddCxVersion = 0;
|
||||
bool m_hasIddCx110DDIs = false;
|
||||
@@ -68,8 +63,10 @@ private:
|
||||
void ScheduleInitRetry();
|
||||
void StopInitRetry();
|
||||
|
||||
bool InitializeLGMP();
|
||||
void LGMPTimer();
|
||||
bool InitializeTransport();
|
||||
void TransportTimer();
|
||||
void OnSetCursorPos(int32_t x, int32_t y) override;
|
||||
void OnSetResolution(uint32_t width, uint32_t height) override;
|
||||
void SetResolution(uint32_t width, uint32_t height);
|
||||
|
||||
public:
|
||||
@@ -79,7 +76,7 @@ public:
|
||||
CDeviceContext(const CDeviceContext&) = delete;
|
||||
CDeviceContext& operator=(const CDeviceContext&) = delete;
|
||||
|
||||
bool SetupLGMP(size_t alignSize);
|
||||
bool SetupTransport(size_t alignSize);
|
||||
|
||||
void InitAdapter();
|
||||
void FinishAdapterInit(UINT connectorIndex);
|
||||
@@ -96,14 +93,9 @@ public:
|
||||
bool CanProcessFP16 () const { return m_canProcessFP16; }
|
||||
bool IsSoftwareMode () const { return m_softwareMode; }
|
||||
|
||||
CFrameTransport& GetFrameTransport()
|
||||
ITransport& GetTransport()
|
||||
{
|
||||
return m_frameTransport;
|
||||
}
|
||||
|
||||
CLGMPControl& GetLGMPControl()
|
||||
{
|
||||
return m_lgmpControl;
|
||||
return *m_transport;
|
||||
}
|
||||
|
||||
CDisplayConfiguration& GetDisplayConfiguration()
|
||||
|
||||
@@ -53,7 +53,7 @@ NTSTATUS CMonitorContext::AssignSwapChain(
|
||||
|
||||
// Build the D3D11 device into a local so the member is never observed
|
||||
// half-constructed. The worker binds it before performing the expensive
|
||||
// D3D12, LGMP and post-processing initialization.
|
||||
// D3D12, transport and post-processing initialization.
|
||||
auto dx11Device = std::make_shared<CD3D11Device>(renderAdapter);
|
||||
const HRESULT initStatus = dx11Device->Init();
|
||||
if (FAILED(initStatus))
|
||||
|
||||
Reference in New Issue
Block a user