[idd] transport: centralize source and control state

This commit is contained in:
Geoffrey McRae
2026-08-12 16:31:35 +10:00
parent 1ebf80fbe8
commit 801a1e58f8
10 changed files with 339 additions and 16 deletions

View File

@@ -60,6 +60,7 @@
<ClCompile Include="postprocess\effect\CDownsampleEffect.cpp" /> <ClCompile Include="postprocess\effect\CDownsampleEffect.cpp" />
<ClCompile Include="postprocess\effect\CHDR16to10Effect.cpp" /> <ClCompile Include="postprocess\effect\CHDR16to10Effect.cpp" />
<ClCompile Include="postprocess\effect\CRGB24Effect.cpp" /> <ClCompile Include="postprocess\effect\CRGB24Effect.cpp" />
<ClCompile Include="transport\CControlHub.cpp" />
<ClCompile Include="transport\CTransportManager.cpp" /> <ClCompile Include="transport\CTransportManager.cpp" />
<ClCompile Include="transport\TransportFactory.cpp" /> <ClCompile Include="transport\TransportFactory.cpp" />
<ClCompile Include="transport\lgmp\CIVSHMEM.cpp" /> <ClCompile Include="transport\lgmp\CIVSHMEM.cpp" />
@@ -107,6 +108,7 @@
<ClInclude Include="postprocess\effect\CDownsampleEffect.h" /> <ClInclude Include="postprocess\effect\CDownsampleEffect.h" />
<ClInclude Include="postprocess\effect\CHDR16to10Effect.h" /> <ClInclude Include="postprocess\effect\CHDR16to10Effect.h" />
<ClInclude Include="postprocess\effect\CRGB24Effect.h" /> <ClInclude Include="postprocess\effect\CRGB24Effect.h" />
<ClInclude Include="transport\CControlHub.h" />
<ClInclude Include="transport\CTransportManager.h" /> <ClInclude Include="transport\CTransportManager.h" />
<ClInclude Include="transport\DirectFrameBufferMemory.h" /> <ClInclude Include="transport\DirectFrameBufferMemory.h" />
<ClInclude Include="transport\FrameMemoryLimits.h" /> <ClInclude Include="transport\FrameMemoryLimits.h" />

View File

@@ -154,6 +154,9 @@
<ClInclude Include="postprocess\effect\CRGB24Effect.h"> <ClInclude Include="postprocess\effect\CRGB24Effect.h">
<Filter>Post-processing\Effects</Filter> <Filter>Post-processing\Effects</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="transport\CControlHub.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\CTransportManager.h"> <ClInclude Include="transport\CTransportManager.h">
<Filter>Transport</Filter> <Filter>Transport</Filter>
</ClInclude> </ClInclude>
@@ -294,6 +297,9 @@
<ClCompile Include="postprocess\effect\CRGB24Effect.cpp"> <ClCompile Include="postprocess\effect\CRGB24Effect.cpp">
<Filter>Post-processing\Effects</Filter> <Filter>Post-processing\Effects</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="transport\CControlHub.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\CTransportManager.cpp"> <ClCompile Include="transport\CTransportManager.cpp">
<Filter>Transport</Filter> <Filter>Transport</Filter>
</ClCompile> </ClCompile>

View File

@@ -593,18 +593,23 @@ void CDeviceContext::TransportTimer()
m_transport->Process(*this); m_transport->Process(*this);
} }
void CDeviceContext::OnSetCursorPos(int32_t x, int32_t y) void CDeviceContext::OnSetCursorPos(
const SourceKey& source, int32_t x, int32_t y)
{ {
UNREFERENCED_PARAMETER(source);
g_pipe.SetCursorPos(x, y); g_pipe.SetCursorPos(x, y);
} }
void CDeviceContext::OnSetResolution(uint32_t width, uint32_t height) void CDeviceContext::OnSetResolution(const SourceKey& source,
uint32_t width, uint32_t height)
{ {
UNREFERENCED_PARAMETER(source);
SetResolution(width, height); SetResolution(width, height);
} }
void CDeviceContext::OnRecoveryRequest( void CDeviceContext::OnRecoveryRequest(const SourceKey& source,
uint64_t session, uint32_t serial, bool active) uint64_t session, uint32_t serial, bool active)
{ {
UNREFERENCED_PARAMETER(source);
g_pipe.SetRecovery(this, session, serial, active); g_pipe.SetRecovery(this, session, serial, active);
} }

View File

@@ -66,9 +66,11 @@ private:
bool InitializeTransport(); bool InitializeTransport();
void TransportTimer(); void TransportTimer();
void OnSetCursorPos(int32_t x, int32_t y) override; void OnSetCursorPos(
void OnSetResolution(uint32_t width, uint32_t height) override; const SourceKey& source, int32_t x, int32_t y) override;
void OnRecoveryRequest( void OnSetResolution(const SourceKey& source,
uint32_t width, uint32_t height) override;
void OnRecoveryRequest(const SourceKey& source,
uint64_t session, uint32_t serial, bool active) override; uint64_t session, uint32_t serial, bool active) override;
void SetResolution(uint32_t width, uint32_t height); void SetResolution(uint32_t width, uint32_t height);

View File

@@ -0,0 +1,161 @@
/**
* Looking Glass
* Copyright © 2017-2026 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 "transport/CControlHub.h"
#include <cstring>
#include <new>
#include <utility>
std::vector<std::shared_ptr<CControlHub::Sink>> CControlHub::Snapshot() const
{
CSRWSharedLock lock(m_sinkLock);
return m_sinks;
}
bool CControlHub::Add(
BackendId backend, uint32_t epoch, IControlTransport& control)
{
std::shared_ptr<Sink> sink(new (std::nothrow) Sink);
if (!sink)
return false;
sink->backend = backend;
sink->epoch = epoch;
sink->control = &control;
{
CSRWExclusiveLock lock(m_sinkLock);
for (const auto& current : m_sinks)
if (current->backend == backend && current->epoch == epoch)
return false;
m_sinks.push_back(sink);
}
Replay(sink);
return true;
}
void CControlHub::Remove(BackendId backend, uint32_t epoch)
{
std::shared_ptr<Sink> removed;
{
CSRWExclusiveLock lock(m_sinkLock);
for (auto current = m_sinks.begin(); current != m_sinks.end(); ++current)
if ((*current)->backend == backend && (*current)->epoch == epoch)
{
removed = *current;
m_sinks.erase(current);
break;
}
}
if (removed)
{
CSRWExclusiveLock lock(removed->lock);
removed->control = nullptr;
}
}
void CControlHub::Replay(const std::shared_ptr<Sink>& sink)
{
IDARG_OUT_QUERY_HWCURSOR cursor = {};
std::vector<BYTE> cursorData;
UINT sdrWhiteLevel;
bool cursorValid;
std::shared_ptr<const D12ColorTransform> transform;
{
CSRWSharedLock lock(m_stateLock);
cursor = m_cursor;
cursorData = m_cursorData;
sdrWhiteLevel = m_sdrWhiteLevel;
cursorValid = m_cursorValid;
transform = m_colorTransform;
}
CSRWSharedLock lock(sink->lock);
if (!sink->control)
return;
sink->control->SetColorTransform(std::move(transform));
if (cursorValid)
sink->control->SendCursor(
cursor, cursorData.empty() ? nullptr : cursorData.data(),
sdrWhiteLevel);
}
void CControlHub::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, UINT sdrWhiteLevel)
{
{
CSRWExclusiveLock lock(m_stateLock);
m_cursor.IsCursorVisible = info.IsCursorVisible;
m_cursor.X = info.X;
m_cursor.Y = info.Y;
m_cursorValid = true;
m_sdrWhiteLevel = sdrWhiteLevel;
if (info.CursorShapeInfo.CursorType !=
IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
{
const size_t size =
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch;
m_cursor.IsCursorShapeUpdated = info.IsCursorShapeUpdated;
m_cursor.CursorShapeInfo = info.CursorShapeInfo;
m_cursorData.resize(size);
if (size)
memcpy(m_cursorData.data(), data, size);
m_shapeValid = true;
}
else if (!m_shapeValid)
m_cursor.CursorShapeInfo.CursorType =
IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED;
}
const auto sinks = Snapshot();
for (const auto& sink : sinks)
{
CSRWSharedLock lock(sink->lock);
if (sink->control)
sink->control->SendCursor(info, data, sdrWhiteLevel);
}
}
void CControlHub::SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform)
{
{
CSRWExclusiveLock lock(m_stateLock);
m_colorTransform = transform;
}
const auto sinks = Snapshot();
for (const auto& sink : sinks)
{
CSRWSharedLock lock(sink->lock);
if (sink->control)
sink->control->SetColorTransform(transform);
}
}
std::shared_ptr<const D12ColorTransform>
CControlHub::GetColorTransform() const
{
CSRWSharedLock lock(m_stateLock);
return m_colorTransform;
}

View File

@@ -0,0 +1,65 @@
/**
* Looking Glass
* Copyright © 2017-2026 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
*/
#pragma once
#include "CSRWLock.h"
#include "transport/IControlTransport.h"
#include "transport/ITransport.h"
#include <memory>
#include <vector>
class CControlHub final : public IControlTransport
{
private:
struct Sink
{
BackendId backend;
uint32_t epoch;
CSRWLock lock;
IControlTransport * control;
};
mutable CSRWLock m_sinkLock;
std::vector<std::shared_ptr<Sink>> m_sinks;
mutable CSRWLock m_stateLock;
std::shared_ptr<const D12ColorTransform> m_colorTransform;
IDARG_OUT_QUERY_HWCURSOR m_cursor = {};
std::vector<BYTE> m_cursorData;
UINT m_sdrWhiteLevel = 0;
bool m_cursorValid = false;
bool m_shapeValid = false;
std::vector<std::shared_ptr<Sink>> Snapshot() const;
void Replay(const std::shared_ptr<Sink>& sink);
public:
bool Add(BackendId backend, uint32_t epoch, IControlTransport& control);
void Remove(BackendId backend, uint32_t epoch);
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, UINT sdrWhiteLevel) override;
void SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform) override;
std::shared_ptr<const D12ColorTransform>
GetColorTransform() const override;
};

View File

@@ -24,9 +24,50 @@
#include <Windows.h> #include <Windows.h>
#include <new> #include <new>
#include <utility>
static const uint64_t RETRY_DELAY_MS = 500; static const uint64_t RETRY_DELAY_MS = 500;
class CSourceEvents final : public ITransportEvents
{
private:
BackendId m_backend;
uint32_t m_epoch;
ITransportEvents& m_events;
SourceKey Stamp(const SourceKey& source) const
{
SourceKey stamped = source;
stamped.backend = m_backend;
stamped.epoch = m_epoch;
return stamped;
}
public:
CSourceEvents(
BackendId backend, uint32_t epoch, ITransportEvents& events) :
m_backend(backend), m_epoch(epoch), m_events(events) {}
void OnSetCursorPos(
const SourceKey& source, int32_t x, int32_t y) override
{
m_events.OnSetCursorPos(Stamp(source), x, y);
}
void OnSetResolution(const SourceKey& source,
uint32_t width, uint32_t height) override
{
m_events.OnSetResolution(Stamp(source), width, height);
}
void OnRecoveryRequest(const SourceKey& source,
uint64_t session, uint32_t serial, bool active) override
{
m_events.OnRecoveryRequest(
Stamp(source), session, serial, active);
}
};
CTransportManager::~CTransportManager() CTransportManager::~CTransportManager()
{ {
Stop(); Stop();
@@ -97,6 +138,13 @@ bool CTransportManager::InitializeEntry(Entry& entry)
return false; return false;
} }
if (!m_control.Add(entry.id, entry.epoch, entry.transport->Control()))
{
entry.state = State::FAILED;
return false;
}
entry.controlAdded = true;
entry.state = State::INITIALIZED; entry.state = State::INITIALIZED;
return true; return true;
} }
@@ -187,6 +235,7 @@ void CTransportManager::RetryEntry(Entry& entry, uint64_t now)
if (entry.primary && m_exposed) if (entry.primary && m_exposed)
return; return;
RemoveServices(entry);
if (entry.transport) if (entry.transport)
entry.transport->Stop(); entry.transport->Stop();
entry.transport.reset(); entry.transport.reset();
@@ -220,10 +269,12 @@ void CTransportManager::HandleProcessResult(
if (result == ProcessResult::RETRY || !entry.required) if (result == ProcessResult::RETRY || !entry.required)
{ {
RemoveServices(entry);
ScheduleRetry(entry); ScheduleRetry(entry);
return; return;
} }
RemoveServices(entry);
entry.state = State::FAILED; entry.state = State::FAILED;
} }
@@ -238,7 +289,8 @@ ITransport::ProcessResult CTransportManager::Process(
if (entry.state != State::INITIALIZED && entry.state != State::READY) if (entry.state != State::INITIALIZED && entry.state != State::READY)
continue; continue;
const ProcessResult result = entry.transport->Process(events); CSourceEvents sourceEvents(entry.id, entry.epoch, events);
const ProcessResult result = entry.transport->Process(sourceEvents);
HandleProcessResult(entry, result); HandleProcessResult(entry, result);
} }
return ProcessResult::OK; return ProcessResult::OK;
@@ -250,12 +302,22 @@ void CTransportManager::Stop()
++current) ++current)
{ {
Entry& entry = **current; Entry& entry = **current;
RemoveServices(entry);
if (entry.transport && entry.state != State::STOPPED) if (entry.transport && entry.state != State::STOPPED)
entry.transport->Stop(); entry.transport->Stop();
entry.state = State::STOPPED; entry.state = State::STOPPED;
} }
} }
void CTransportManager::RemoveServices(Entry& entry)
{
if (entry.controlAdded)
{
m_control.Remove(entry.id, entry.epoch);
entry.controlAdded = false;
}
}
void CTransportManager::SyncRecovery() void CTransportManager::SyncRecovery()
{ {
for (const auto& current : m_entries) for (const auto& current : m_entries)
@@ -299,7 +361,7 @@ IFrameTransport& CTransportManager::Frames()
IControlTransport& CTransportManager::Control() IControlTransport& CTransportManager::Control()
{ {
return Primary().Control(); return m_control;
} }
IInputTransport * CTransportManager::Input() IInputTransport * CTransportManager::Input()

View File

@@ -20,6 +20,7 @@
#pragma once #pragma once
#include "transport/CControlHub.h"
#include "transport/ITransport.h" #include "transport/ITransport.h"
#include <memory> #include <memory>
@@ -53,9 +54,11 @@ private:
State state = State::CLOSED; State state = State::CLOSED;
uint32_t epoch = 1; uint32_t epoch = 1;
uint64_t retryAt = 0; uint64_t retryAt = 0;
bool controlAdded = false;
}; };
std::vector<std::unique_ptr<Entry>> m_entries; std::vector<std::unique_ptr<Entry>> m_entries;
CControlHub m_control;
Entry * m_primary = nullptr; Entry * m_primary = nullptr;
bool m_initialized = false; bool m_initialized = false;
bool m_setup = false; bool m_setup = false;
@@ -68,6 +71,7 @@ private:
void RetryEntry(Entry& entry, uint64_t now); void RetryEntry(Entry& entry, uint64_t now);
void HandleProcessResult(Entry& entry, ProcessResult result); void HandleProcessResult(Entry& entry, ProcessResult result);
void ScheduleRetry(Entry& entry); void ScheduleRetry(Entry& entry);
void RemoveServices(Entry& entry);
ITransport& Primary(); ITransport& Primary();
public: public:

View File

@@ -30,22 +30,32 @@ class IControlTransport;
class IFrameTransport; class IFrameTransport;
class IInputTransport; class IInputTransport;
using BackendId = uint32_t;
struct SourceKey
{
BackendId backend = 0;
uint32_t epoch = 0;
uint32_t client = 0;
uint32_t generation = 0;
};
class ITransportEvents class ITransportEvents
{ {
public: public:
virtual ~ITransportEvents() = default; virtual ~ITransportEvents() = default;
virtual void OnSetCursorPos(int32_t x, int32_t y) = 0; virtual void OnSetCursorPos(
virtual void OnSetResolution(uint32_t width, uint32_t height) = 0; const SourceKey& source, int32_t x, int32_t y) = 0;
virtual void OnRecoveryRequest( virtual void OnSetResolution(
const SourceKey& source, uint32_t width, uint32_t height) = 0;
virtual void OnRecoveryRequest(const SourceKey& source,
uint64_t session, uint32_t serial, bool active) = 0; uint64_t session, uint32_t serial, bool active) = 0;
}; };
class ITransport class ITransport
{ {
public: public:
using BackendId = uint32_t;
enum class OpenResult enum class OpenResult
{ {
SUCCESS, SUCCESS,
@@ -72,6 +82,8 @@ public:
virtual OpenResult Open() = 0; virtual OpenResult Open() = 0;
virtual bool Initialize() = 0; virtual bool Initialize() = 0;
virtual bool Setup(size_t alignment) = 0; virtual bool Setup(size_t alignment) = 0;
// Process performs a bounded, non-blocking drain. The events reference is
// valid only for the duration of this call and must not be retained.
virtual ProcessResult Process(ITransportEvents& events) = 0; virtual ProcessResult Process(ITransportEvents& events) = 0;
virtual void Stop() = 0; virtual void Stop() = 0;
virtual void SyncRecovery() {} virtual void SyncRecovery() {}

View File

@@ -97,7 +97,7 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
{ {
const CRecovery::Request recovery = m_recovery.Process(); const CRecovery::Request recovery = m_recovery.Process();
if (recovery.valid) if (recovery.valid)
events.OnRecoveryRequest( events.OnRecoveryRequest(SourceKey(),
recovery.session, recovery.serial, recovery.active); recovery.session, recovery.serial, recovery.active);
// Before the swap chain establishes the frame-buffer alignment, service // Before the swap chain establishes the frame-buffer alignment, service
@@ -140,17 +140,21 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
{ {
case KVMFR_MESSAGE_SETCURSORPOS: case KVMFR_MESSAGE_SETCURSORPOS:
{ {
SourceKey source;
source.client = sourceClientID;
KVMFRSetCursorPos * position = KVMFRSetCursorPos * position =
reinterpret_cast<KVMFRSetCursorPos *>(msg); reinterpret_cast<KVMFRSetCursorPos *>(msg);
events.OnSetCursorPos(position->x, position->y); events.OnSetCursorPos(source, position->x, position->y);
break; break;
} }
case KVMFR_MESSAGE_WINDOWSIZE: case KVMFR_MESSAGE_WINDOWSIZE:
{ {
SourceKey source;
source.client = sourceClientID;
KVMFRWindowSize * window = KVMFRWindowSize * window =
reinterpret_cast<KVMFRWindowSize *>(msg); reinterpret_cast<KVMFRWindowSize *>(msg);
events.OnSetResolution(window->w, window->h); events.OnSetResolution(source, window->w, window->h);
break; break;
} }