[idd] transport: isolate interactive services

This commit is contained in:
Geoffrey McRae
2026-08-12 21:49:04 +10:00
parent 51f432db00
commit c92acb0a34
21 changed files with 2026 additions and 401 deletions

View File

@@ -98,6 +98,7 @@ bool CLGMPControl::Initialize()
void CLGMPControl::DeInit()
{
SetControlEvents(nullptr, {});
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
lgmpHostMemFree(&m_pointerMemory[i]);
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
@@ -106,7 +107,6 @@ void CLGMPControl::DeInit()
lgmpHostMemFree(&m_pointerTransformMemory[i]);
m_pointerQueue = nullptr;
m_pointerShape = nullptr;
m_pointerMemoryIndex = 0;
m_pointerShapeIndex = 0;
m_pointerTransformIndex = 0;
@@ -129,43 +129,101 @@ bool CLGMPControl::HasNewSubscribers()
return lgmpHostQueueNewSubs(m_pointerQueue) != 0;
}
void CLGMPControl::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, UINT sdrWhiteLevel)
void CLGMPControl::SetControlEvents(
IControlEvents * events, const ControlToken& token)
{
PLGMPMemory mem;
if (info.CursorShapeInfo.CursorType == IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
CSRWExclusiveLock lock(m_eventLock);
m_events = events;
m_token = events ? token : ControlToken {};
}
PLGMPMemory CLGMPControl::FindAvailable(
PLGMPMemory * memory, int count, int& index) const
{
for (int offset = 0; offset < count; ++offset)
{
mem = m_pointerMemory[m_pointerMemoryIndex];
if (++m_pointerMemoryIndex == LGMP_Q_POINTER_LEN)
m_pointerMemoryIndex = 0;
const int candidate = (index + offset) % count;
if (memory[candidate] &&
!lgmpHostQueuePayloadPending(m_pointerQueue, memory[candidate]))
{
index = candidate;
return memory[candidate];
}
}
return nullptr;
}
ControlResult CLGMPControl::SendCursor(
const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, size_t size, UINT sdrWhiteLevel)
{
if (!m_pointerQueue)
return ControlResult::FAILED;
const bool hasShape = info.CursorShapeInfo.CursorType !=
IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED;
if (hasShape)
{
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_ALPHA &&
info.CursorShapeInfo.CursorType !=
IDDCX_CURSOR_SHAPE_TYPE_MASKED_COLOR)
{
DEBUG_ERROR("Unsupported pointer shape type: %u",
static_cast<unsigned>(info.CursorShapeInfo.CursorType));
return ControlResult::FAILED;
}
if (info.CursorShapeInfo.Height &&
info.CursorShapeInfo.Pitch > SIZE_MAX / info.CursorShapeInfo.Height)
{
DEBUG_ERROR("Pointer shape size overflow");
return ControlResult::FAILED;
}
const size_t required = static_cast<size_t>(
info.CursorShapeInfo.Height) * info.CursorShapeInfo.Pitch;
if (required != size || (required && !data) ||
required > MAX_POINTER_SIZE - sizeof(KVMFRCursor))
{
DEBUG_ERROR("Invalid pointer shape payload: %zu bytes", size);
return ControlResult::FAILED;
}
}
PLGMPMemory mem;
int * index;
int count;
if (!hasShape)
{
index = &m_pointerMemoryIndex;
count = LGMP_Q_POINTER_LEN;
mem = FindAvailable(m_pointerMemory, count, *index);
}
else
{
mem = m_pointerShapeMemory[m_pointerShapeIndex];
if (++m_pointerShapeIndex == POINTER_SHAPE_BUFFERS)
m_pointerShapeIndex = 0;
index = &m_pointerShapeIndex;
count = POINTER_SHAPE_BUFFERS;
mem = FindAvailable(m_pointerShapeMemory, count, *index);
}
if (!mem)
return ControlResult::RETRY;
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
cursor->sdrWhiteLevel = sdrWhiteLevel ?
sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
m_cursorVisible = info.IsCursorVisible;
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
if (info.IsCursorVisible)
{
m_cursorX = info.X;
m_cursorY = info.Y;
cursor->x = (int16_t)info.X;
cursor->y = (int16_t)info.Y;
flags |= CURSOR_FLAG_POSITION | CURSOR_FLAG_VISIBLE;
}
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
if (hasShape)
{
memcpy(cursor + 1, data,
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch);
if (size)
memcpy(cursor + 1, data, size);
cursor->hx = (int8_t )info.CursorShapeInfo.XHot;
cursor->hy = (int8_t )info.CursorShapeInfo.YHot;
@@ -185,56 +243,43 @@ void CLGMPControl::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
}
flags |= CURSOR_FLAG_SHAPE;
m_pointerShape = mem;
}
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(
m_pointerQueue, flags, mem)) != LGMP_OK)
const LGMP_STATUS status =
lgmpHostQueuePost(m_pointerQueue, flags, mem);
if (status == LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
lgmpStatusString(status));
break;
*index = (*index + 1) % count;
return ControlResult::APPLIED;
}
if (status == LGMP_ERR_QUEUE_FULL)
return ControlResult::RETRY;
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
lgmpStatusString(status));
return ControlResult::FAILED;
}
void CLGMPControl::SetColorTransform(
ControlResult CLGMPControl::SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform)
{
{
CSRWExclusiveLock lock(m_colorTransformLock);
m_colorTransform = std::move(transform);
}
SendColorTransform();
return SendColorTransform(transform);
}
std::shared_ptr<const D12ColorTransform>
CLGMPControl::GetColorTransform() const
{
CSRWSharedLock lock(m_colorTransformLock);
std::shared_ptr<const D12ColorTransform> transform = m_colorTransform;
return transform;
}
void CLGMPControl::SendColorTransform()
ControlResult CLGMPControl::SendColorTransform(
const std::shared_ptr<const D12ColorTransform>& transform)
{
if (!m_pointerQueue || !m_pointerTransformMemory[0])
return;
return ControlResult::FAILED;
PLGMPMemory mem = m_pointerTransformMemory[m_pointerTransformIndex];
if (++m_pointerTransformIndex == COLOR_TRANSFORM_BUFFERS)
m_pointerTransformIndex = 0;
PLGMPMemory mem = FindAvailable(m_pointerTransformMemory,
COLOR_TRANSFORM_BUFFERS, m_pointerTransformIndex);
if (!mem)
return ControlResult::RETRY;
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
KVMFRColorTransform * output =
(KVMFRColorTransform *)(cursor + 1);
const auto transform = GetColorTransform();
output->flags = 0;
if (transform)
@@ -248,54 +293,25 @@ void CLGMPControl::SendColorTransform()
memcpy(output->lut, transform->lut, sizeof(output->lut));
}
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(m_pointerQueue,
CURSOR_FLAG_COLOR_TRANSFORM, mem)) != LGMP_OK)
const LGMP_STATUS status = lgmpHostQueuePost(m_pointerQueue,
CURSOR_FLAG_COLOR_TRANSFORM, mem);
if (status == LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer Transform): %s",
lgmpStatusString(status));
break;
m_pointerTransformIndex =
(m_pointerTransformIndex + 1) % COLOR_TRANSFORM_BUFFERS;
return ControlResult::APPLIED;
}
if (status == LGMP_ERR_QUEUE_FULL)
return ControlResult::RETRY;
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer Transform): %s",
lgmpStatusString(status));
return ControlResult::FAILED;
}
void CLGMPControl::ResendCursor()
void CLGMPControl::RequestReplay()
{
PLGMPMemory mem = m_pointerShape;
if (!mem)
return;
KVMFRCursor* cursor = (KVMFRCursor*)lgmpHostMemPtr(mem);
cursor->x = (int16_t)m_cursorX;
cursor->y = (int16_t)m_cursorY;
const uint32_t flags =
CURSOR_FLAG_POSITION | CURSOR_FLAG_SHAPE | CURSOR_FLAG_VISIBLE_VALID |
(m_cursorVisible ? CURSOR_FLAG_VISIBLE : 0);
LGMP_STATUS status;
while ((status = lgmpHostQueuePost(
m_pointerQueue, flags, mem)) != LGMP_OK)
{
if (status == LGMP_ERR_QUEUE_FULL)
{
Sleep(1);
continue;
}
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s",
lgmpStatusString(status));
break;
}
}
void CLGMPControl::ResendState()
{
ResendCursor();
SendColorTransform();
CSRWSharedLock lock(m_eventLock);
if (m_events)
m_events->OnControlReplay(m_token);
}

View File

@@ -23,7 +23,7 @@
#include "CSRWLock.h"
#include "transport/lgmp/CLGMPHost.h"
#include "transport/IControlTransport.h"
#include "transport/IControlSink.h"
#include "common/KVMFR.h"
@@ -35,7 +35,7 @@
class CLGMPTransport;
class CLGMPControl final : public IControlTransport
class CLGMPControl final : public IControlSink
{
private:
friend class CLGMPTransport;
@@ -49,26 +49,25 @@ private:
PLGMPMemory m_pointerMemory[LGMP_Q_POINTER_LEN] = {};
PLGMPMemory m_pointerShapeMemory[POINTER_SHAPE_BUFFERS] = {};
PLGMPMemory m_pointerTransformMemory[COLOR_TRANSFORM_BUFFERS] = {};
PLGMPMemory m_pointerShape = nullptr;
int m_pointerMemoryIndex = 0;
int m_pointerShapeIndex = 0;
int m_pointerTransformIndex = 0;
bool m_cursorVisible = false;
int m_cursorX = 0;
int m_cursorY = 0;
mutable CSRWLock m_colorTransformLock;
std::shared_ptr<const D12ColorTransform> m_colorTransform;
mutable CSRWLock m_eventLock;
IControlEvents * m_events = nullptr;
ControlToken m_token = {};
void SendColorTransform();
void ResendCursor();
PLGMPMemory FindAvailable(
PLGMPMemory * memory, int count, int& index) const;
ControlResult SendColorTransform(
const std::shared_ptr<const D12ColorTransform>& transform);
bool Initialize();
void DeInit();
LGMP_STATUS ReadDataWithSource(void * data, size_t * size,
uint32_t * sourceClientID);
LGMP_STATUS AckData();
bool HasNewSubscribers();
void ResendState();
void RequestReplay();
public:
explicit CLGMPControl(CLGMPHost& host) :
@@ -78,10 +77,10 @@ public:
CLGMPControl(const CLGMPControl&) = delete;
CLGMPControl& operator=(const CLGMPControl&) = delete;
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data,
UINT sdrWhiteLevel) override;
void SetColorTransform(
void SetControlEvents(
IControlEvents * events, const ControlToken& token) override;
ControlResult SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
const BYTE * data, size_t size, UINT sdrWhiteLevel) override;
ControlResult SetColorTransform(
std::shared_ptr<const D12ColorTransform> transform) override;
std::shared_ptr<const D12ColorTransform>
GetColorTransform() const override;
};

View File

@@ -20,7 +20,6 @@
#include "transport/lgmp/CLGMPInputTransport.h"
#include "input/IInputSink.h"
#include "transport/lgmp/CLGMPHost.h"
#include "CDebug.h"
#include "CSRWLock.h"
@@ -101,7 +100,10 @@ bool CLGMPInputTransport::Initialize()
memset(lgmpHostMemPtr(memory), 0, sizeof(KVMFRInputStatus));
}
m_statusDirty = true;
{
CSRWExclusiveLock lock(m_statusLock);
m_statusDirty = true;
}
return true;
}
@@ -113,26 +115,44 @@ void CLGMPInputTransport::DeInit()
m_queue = nullptr;
}
void CLGMPInputTransport::UpdateSinkState(uint64_t state)
InputSourceId CLGMPInputTransport::Owner() const
{
if (state == m_sinkState)
InputSourceId source;
source.client = m_ownerClientID;
source.generation = m_ownerGeneration;
return source;
}
void CLGMPInputTransport::UpdateTargetState(
const InputTargetState& state)
{
CSRWExclusiveLock lock(m_statusLock);
if (state.state == m_targetState.state &&
state.available == m_targetState.available &&
state.owned == m_targetState.owned &&
state.owner.client == m_targetState.owner.client &&
state.owner.generation == m_targetState.owner.generation)
return;
m_sinkState = state;
if (++m_endpointGeneration == 0)
++m_endpointGeneration;
if (state.state != m_targetState.state)
{
if (++m_endpointGeneration == 0)
++m_endpointGeneration;
}
m_targetState = state;
m_statusDirty = true;
}
void CLGMPInputTransport::PublishStatus()
bool CLGMPInputTransport::PublishStatus()
{
CSRWExclusiveLock lock(m_statusLock);
if (!m_queue)
return;
return true;
if (lgmpHostQueueNewSubs(m_queue))
m_statusDirty = true;
if (!m_statusDirty || !lgmpHostQueueHasSubs(m_queue))
return;
return true;
PLGMPMemory memory = nullptr;
for (PLGMPMemory candidate : m_statusMemory)
@@ -142,9 +162,9 @@ void CLGMPInputTransport::PublishStatus()
break;
}
if (!memory)
return;
return true;
const bool available = (m_sinkState & 1) != 0;
const bool available = m_targetState.available;
KVMFRInputStatus status = {};
status.version = KVMFR_INPUT_VERSION;
status.capabilities = available ?
@@ -152,11 +172,11 @@ void CLGMPInputTransport::PublishStatus()
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE |
KVMFR_INPUT_CAP_KEYBOARD : 0;
status.flags = available ? KVMFR_INPUT_STATUS_AVAILABLE : 0;
if (m_ownerClientID)
if (m_targetState.owned)
{
status.flags |= KVMFR_INPUT_STATUS_HAS_OWNER;
status.ownerClientID = m_ownerClientID;
status.ownerGeneration = m_ownerGeneration;
status.ownerClientID = m_targetState.owner.client;
status.ownerGeneration = m_targetState.owner.generation;
}
status.generation = m_endpointGeneration;
status.lease = static_cast<uint32_t>(OWNER_LEASE_MS);
@@ -173,11 +193,26 @@ void CLGMPInputTransport::PublishStatus()
m_statusDirty = false;
}
else if (result != LGMP_ERR_QUEUE_FULL)
{
DEBUG_WARN("lgmpHostQueuePost Failed (Input Status): %s",
lgmpStatusString(result));
return false;
}
return true;
}
bool CLGMPInputTransport::Start(IInputSink& sink)
void CLGMPInputTransport::FlushStatus()
{
if (m_statusFailed.load(std::memory_order_acquire) || PublishStatus())
return;
m_statusFailed.store(true, std::memory_order_release);
CSRWSharedLock lock(m_lifecycleLock);
if (m_stopEvent)
SetEvent(m_stopEvent);
}
bool CLGMPInputTransport::Start(IInputTarget& target)
{
CSRWExclusiveLock lock(m_lifecycleLock);
if (m_thread)
@@ -198,7 +233,7 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
m_thread = nullptr;
m_pollTimer = nullptr;
m_stopEvent = nullptr;
m_sink = nullptr;
m_target = nullptr;
}
if (!m_queue)
@@ -224,19 +259,25 @@ bool CLGMPInputTransport::Start(IInputSink& sink)
return false;
}
m_sink = &sink;
UpdateSinkState(sink.GetState());
if (!m_endpointGeneration)
m_target = &target;
{
m_endpointGeneration = 1;
m_statusDirty = true;
CSRWExclusiveLock statusLock(m_statusLock);
m_targetState = target.GetState({});
if (++m_endpointGeneration == 0)
++m_endpointGeneration;
m_statusDirty = true;
}
m_statusFailed.store(false, std::memory_order_release);
m_thread = CreateThread(nullptr, 0, ThreadProc, this, 0, nullptr);
if (!m_thread)
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create LGMP input worker");
m_sink = nullptr;
m_sinkState = 0;
m_target = nullptr;
{
CSRWExclusiveLock statusLock(m_statusLock);
m_targetState = {};
m_statusDirty = true;
}
CloseHandle(m_pollTimer);
CloseHandle(m_stopEvent);
m_pollTimer = nullptr;
@@ -271,12 +312,16 @@ void CLGMPInputTransport::Stop()
CloseHandle(m_stopEvent);
m_stopEvent = nullptr;
}
m_sink = nullptr;
m_target = nullptr;
m_ownerClientID = 0;
m_ownerGeneration = 0;
m_ownerSequence = 0;
m_ownerDeadline = 0;
m_sinkState = 0;
{
CSRWExclusiveLock statusLock(m_statusLock);
m_targetState = {};
m_statusDirty = true;
}
}
bool CLGMPInputTransport::IsOwner(
@@ -294,16 +339,19 @@ bool CLGMPInputTransport::Claim(
++m_statistics.sequenceErrors;
return false;
}
if (!m_sink)
if (!m_target)
{
++m_statistics.deliveryFailures;
return false;
}
const uint64_t sinkState = m_sink->GetState();
if (!(sinkState & 1) || sinkState != m_sinkState ||
!m_sink->Reset() || m_sink->GetState() != sinkState)
InputSourceId source;
source.client = sourceClientID;
source.generation = message.generation;
const InputResult result = m_target->Claim(source);
if (result != InputResult::ACCEPTED)
{
UpdateTargetState(m_target->GetState(source));
++m_statistics.deliveryFailures;
return false;
}
@@ -312,7 +360,7 @@ bool CLGMPInputTransport::Claim(
m_ownerGeneration = message.generation;
m_ownerSequence = message.sequence;
RenewLease();
m_statusDirty = true;
UpdateTargetState(m_target->GetState(source));
++m_statistics.claims;
DEBUG_INFO("Input owner %u generation %u acquired",
m_ownerClientID, m_ownerGeneration);
@@ -332,14 +380,16 @@ void CLGMPInputTransport::ReleaseOwner(
const uint32_t clientID = m_ownerClientID;
const uint32_t generation = m_ownerGeneration;
if (reset && m_sink)
m_sink->Reset();
if (m_target)
{
m_target->Release(Owner(), reset);
UpdateTargetState(m_target->GetState({}));
}
m_ownerClientID = 0;
m_ownerGeneration = 0;
m_ownerSequence = 0;
m_ownerDeadline = 0;
m_statusDirty = true;
++m_statistics.releases;
DEBUG_INFO("Input owner %u generation %u released (%s)",
clientID, generation, reason);
@@ -347,23 +397,18 @@ void CLGMPInputTransport::ReleaseOwner(
void CLGMPInputTransport::CheckOwner()
{
if (!m_sink)
if (!m_target)
return;
const uint64_t state = m_sink->GetState();
if (state != m_sinkState)
{
UpdateSinkState(state);
ReleaseOwner(true, "input endpoint changed");
return;
}
const InputTargetState state = m_target->GetState(Owner());
UpdateTargetState(state);
if (!m_ownerClientID)
return;
if (!(state & 1))
if (!state.available || !state.owned)
{
ReleaseOwner(true, "input unavailable");
ReleaseOwner(false, "input ownership changed");
return;
}
@@ -418,17 +463,9 @@ bool CLGMPInputTransport::ProcessMessage(
uint32_t sourceClientID, const KVMFRInputMessage& message)
{
const bool owner = IsOwner(sourceClientID, message.generation);
const uint64_t sinkState = m_sink ?
m_sink->GetState() : m_sinkState;
if (sinkState != m_sinkState)
{
UpdateSinkState(sinkState);
if (m_ownerClientID)
{
ReleaseOwner(true, "input endpoint changed");
return false;
}
}
InputSourceId source;
source.client = sourceClientID;
source.generation = message.generation;
if (!message.generation || !message.sequence || message.reserved ||
!ValidatePayload(message))
@@ -439,6 +476,10 @@ bool CLGMPInputTransport::ProcessMessage(
return false;
}
if (m_target)
UpdateTargetState(m_target->GetState(
m_ownerClientID ? Owner() : source));
if (message.type == KVMFR_INPUT_MESSAGE_CLAIM)
{
if (m_ownerClientID)
@@ -474,7 +515,7 @@ bool CLGMPInputTransport::ProcessMessage(
return false;
}
bool accepted = false;
InputResult result = InputResult::STALE;
bool inputReport = false;
switch (message.type)
{
@@ -483,56 +524,52 @@ bool CLGMPInputTransport::ProcessMessage(
return true;
case KVMFR_INPUT_MESSAGE_KEEPALIVE:
accepted = true;
result = m_target ? m_target->Touch(source) :
InputResult::UNAVAILABLE;
break;
case KVMFR_INPUT_MESSAGE_RESET:
accepted = m_sink && m_sink->Reset();
result = m_target ? m_target->Reset(source) :
InputResult::UNAVAILABLE;
break;
case KVMFR_INPUT_MESSAGE_MOUSE_RELATIVE:
inputReport = true;
accepted = m_sink && m_sink->SendMouseRelative(
result = m_target ? m_target->SendMouseRelative(source,
message.payload.mouseRelative.deltaX,
message.payload.mouseRelative.deltaY,
message.payload.mouseRelative.wheel,
message.payload.mouseRelative.buttons);
message.payload.mouseRelative.buttons) : InputResult::UNAVAILABLE;
break;
case KVMFR_INPUT_MESSAGE_MOUSE_ABSOLUTE:
inputReport = true;
accepted = m_sink && m_sink->SendMouseAbsolute(
result = m_target ? m_target->SendMouseAbsolute(source,
message.payload.mouseAbsolute.x,
message.payload.mouseAbsolute.y,
message.payload.mouseAbsolute.wheel,
message.payload.mouseAbsolute.buttons);
message.payload.mouseAbsolute.buttons) : InputResult::UNAVAILABLE;
break;
case KVMFR_INPUT_MESSAGE_KEYBOARD:
inputReport = true;
accepted = m_sink && m_sink->SendKeyboard(
result = m_target ? m_target->SendKeyboard(source,
message.payload.keyboard.modifiers,
message.payload.keyboard.keys);
message.payload.keyboard.keys) : InputResult::UNAVAILABLE;
break;
default:
break;
}
if (!accepted)
if (result != InputResult::ACCEPTED)
{
++m_statistics.deliveryFailures;
ReleaseOwner(true, "input delivery failed");
return false;
}
const uint64_t deliveredState = m_sink->GetState();
if (deliveredState != m_sinkState)
{
UpdateSinkState(deliveredState);
ReleaseOwner(true, "input endpoint changed");
return false;
}
UpdateTargetState(m_target->GetState(source));
m_ownerSequence = message.sequence;
RenewLease();
@@ -541,9 +578,9 @@ bool CLGMPInputTransport::ProcessMessage(
return true;
}
bool CLGMPInputTransport::DrainMessages()
bool CLGMPInputTransport::DrainMessages(bool& received)
{
bool received = false;
received = false;
unsigned count = 0;
for (; count < 256; ++count)
{
@@ -558,7 +595,7 @@ bool CLGMPInputTransport::DrainMessages()
{
DEBUG_ERROR("lgmpHostReadData Failed (Input): %s",
lgmpStatusString(status));
break;
return false;
}
received = true;
@@ -577,13 +614,19 @@ bool CLGMPInputTransport::DrainMessages()
ProcessMessage(sourceClientID, message);
}
lgmpHostAckData(m_queue);
const LGMP_STATUS ackStatus = lgmpHostAckData(m_queue);
if (ackStatus != LGMP_OK)
{
DEBUG_ERROR("lgmpHostAckData Failed (Input): %s",
lgmpStatusString(ackStatus));
return false;
}
}
if (count > m_statistics.maxDrain)
m_statistics.maxDrain = count;
if (count == 256)
++m_statistics.drainLimit;
return received;
return true;
}
void CLGMPInputTransport::LogStatistics(ULONGLONG now)
@@ -643,11 +686,22 @@ void CLGMPInputTransport::Thread()
m_statistics = {};
m_statistics.lastLog = GetTickCount64();
const HANDLE waitHandles[] = { m_stopEvent, m_pollTimer };
bool failed = false;
for (;;)
{
CheckOwner();
const bool received = DrainMessages();
PublishStatus();
bool received = false;
if (!DrainMessages(received))
{
failed = true;
break;
}
if (!PublishStatus())
{
m_statusFailed.store(true, std::memory_order_release);
failed = true;
break;
}
const ULONGLONG now = GetTickCount64();
if (received)
activeUntil = now + ACTIVE_POLL_MS;
@@ -659,23 +713,30 @@ void CLGMPInputTransport::Thread()
DEBUG_ERROR_HR(GetLastError(), "Failed to arm LGMP input timer");
if (WaitForSingleObject(m_stopEvent, 1) != WAIT_TIMEOUT)
break;
continue;
failed = true;
break;
}
const DWORD wait = WaitForMultipleObjects(
_countof(waitHandles), waitHandles, FALSE, INFINITE);
if (wait == WAIT_FIRST_OBJECT_VALUE)
{
failed = m_statusFailed.load(std::memory_order_acquire);
break;
}
if (wait != WAIT_FIRST_OBJECT_VALUE + 1)
{
DEBUG_ERROR_HR(GetLastError(), "LGMP input worker wait failed");
failed = true;
break;
}
}
ReleaseOwner(true, "transport stopped");
UpdateSinkState(0);
UpdateTargetState({});
PublishStatus();
if (failed && m_target)
m_target->Failed();
if (avTaskHandle)
AvRevertMmThreadCharacteristics(avTaskHandle);
}

View File

@@ -21,11 +21,12 @@
#pragma once
#include "CSRWLock.h"
#include "transport/IInputTransport.h"
#include "transport/IInputSource.h"
#include "common/LGMPConfig.h"
#include <Windows.h>
#include <atomic>
#include <stdint.h>
extern "C" {
@@ -33,10 +34,10 @@ extern "C" {
}
class CLGMPHost;
class IInputSink;
class IInputTarget;
struct KVMFRInputMessage;
class CLGMPInputTransport final : public IInputTransport
class CLGMPInputTransport final : public IInputSource
{
private:
static constexpr ULONGLONG OWNER_LEASE_MS = 500;
@@ -63,9 +64,10 @@ private:
PLGMPHostQueue m_queue = nullptr;
PLGMPMemory m_statusMemory[LGMP_Q_INPUT_LEN] = {};
IInputSink * m_sink = nullptr;
IInputTarget * m_target = nullptr;
CSRWLock m_lifecycleLock;
CSRWLock m_statusLock;
HANDLE m_stopEvent = nullptr;
HANDLE m_pollTimer = nullptr;
HANDLE m_thread = nullptr;
@@ -74,18 +76,21 @@ private:
uint32_t m_ownerGeneration = 0;
uint32_t m_ownerSequence = 0;
ULONGLONG m_ownerDeadline = 0;
uint64_t m_sinkState = 0;
InputTargetState m_targetState;
uint32_t m_endpointGeneration = 0;
uint32_t m_statusSerial = 0;
bool m_statusDirty = false;
std::atomic<bool> m_statusFailed = false;
Statistics m_statistics = {};
bool Initialize();
void DeInit();
void UpdateSinkState(uint64_t state);
void PublishStatus();
InputSourceId Owner() const;
void UpdateTargetState(const InputTargetState& state);
bool PublishStatus();
void FlushStatus();
void LogStatistics(ULONGLONG now);
bool DrainMessages();
bool DrainMessages(bool& received);
bool ProcessMessage(uint32_t sourceClientID,
const KVMFRInputMessage& message);
bool ValidatePayload(const KVMFRInputMessage& message) const;
@@ -109,6 +114,6 @@ public:
CLGMPInputTransport(const CLGMPInputTransport&) = delete;
CLGMPInputTransport& operator=(const CLGMPInputTransport&) = delete;
bool Start(IInputSink& sink) override;
bool Start(IInputTarget& target) override;
void Stop() override;
};

View File

@@ -121,6 +121,8 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
return ProcessResult::FAILURE;
}
m_input.FlushStatus();
const uint64_t now = CFrameScheduler::Nanotime();
// Take the frame subscriber snapshot before processing scheduling messages,
@@ -192,7 +194,7 @@ ITransport::ProcessResult CLGMPTransport::Process(ITransportEvents& events)
m_frames.FinalizeSubscribers(subscribers, now);
if (m_control.HasNewSubscribers())
m_control.ResendState();
m_control.RequestReplay();
return ProcessResult::OK;
}

View File

@@ -63,7 +63,7 @@ public:
FrameMemoryLimits GetMemoryLimits() const override;
DirectFrameBufferMemory GetDirectMemory() const override;
IFrameSink& FrameSink() override { return m_frames; }
IControlTransport& Control() override { return m_control; }
IInputTransport * Input() override { return &m_input; }
IFrameSink * FrameSink() override { return &m_frames; }
IControlSink * Control() override { return &m_control; }
IInputSource * Input() override { return &m_input; }
};