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:
1056
idd/LGIdd/transport/CFrameScheduler.cpp
Normal file
1056
idd/LGIdd/transport/CFrameScheduler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
167
idd/LGIdd/transport/CFrameScheduler.h
Normal file
167
idd/LGIdd/transport/CFrameScheduler.h
Normal file
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* 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 <Windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "transport/TransportTypes.h"
|
||||
|
||||
class CFrameScheduler
|
||||
{
|
||||
public:
|
||||
struct Schedule
|
||||
{
|
||||
uint32_t clientID;
|
||||
uint32_t generation;
|
||||
uint32_t epoch;
|
||||
uint64_t period;
|
||||
uint64_t targetSlack;
|
||||
uint64_t deadline;
|
||||
uint64_t forceTicket;
|
||||
uint64_t republishTicket;
|
||||
uint32_t deadlineSerial;
|
||||
uint32_t deliveryDeadlineSerial;
|
||||
bool phaseEligible;
|
||||
};
|
||||
|
||||
private:
|
||||
struct Client
|
||||
{
|
||||
uint32_t clientID;
|
||||
uint32_t generation;
|
||||
uint64_t period;
|
||||
uint64_t targetSlack;
|
||||
uint64_t expiry;
|
||||
uint64_t nextDelivery;
|
||||
uint32_t lastFeedbackDeadlineSerial;
|
||||
uint32_t lastDeliveredFrameSerial;
|
||||
bool subscribed;
|
||||
bool ownerCapable;
|
||||
bool subscriptionSeen;
|
||||
bool active;
|
||||
bool immediate;
|
||||
bool deliveredFrameValid;
|
||||
};
|
||||
|
||||
struct Publication
|
||||
{
|
||||
uint32_t generation;
|
||||
uint32_t epoch;
|
||||
uint32_t deadlineSerial;
|
||||
uint32_t frameSerial;
|
||||
uint64_t deadline;
|
||||
bool committed;
|
||||
bool completed;
|
||||
bool phaseValid;
|
||||
bool accepted;
|
||||
};
|
||||
|
||||
static const unsigned PUBLICATION_HISTORY_SIZE = 128;
|
||||
static const unsigned WORK_TIMING_HISTORY_SIZE = 32;
|
||||
|
||||
mutable SRWLOCK m_lock = SRWLOCK_INIT;
|
||||
HANDLE m_wakeEvent = nullptr;
|
||||
Client m_clients[TRANSPORT_MAX_CLIENTS] = {};
|
||||
Schedule m_schedule = {};
|
||||
bool m_scheduling = false;
|
||||
uint32_t m_epoch = 0;
|
||||
|
||||
// A result acknowledges only the request tickets captured by its attempt.
|
||||
uint64_t m_forceRequestTicket = 0;
|
||||
uint64_t m_forceAckTicket = 0;
|
||||
uint64_t m_republishRequestTicket = 0;
|
||||
uint64_t m_republishAckTicket = 0;
|
||||
|
||||
uint64_t m_lastArrival = 0;
|
||||
uint64_t m_guestPeriod = 0;
|
||||
uint64_t m_workEstimate = 0;
|
||||
uint64_t m_workTiming[WORK_TIMING_HISTORY_SIZE] = {};
|
||||
unsigned m_workTimingCount = 0;
|
||||
unsigned m_workTimingIndex = 0;
|
||||
uint64_t m_nextDeadline = 0;
|
||||
uint32_t m_deadlineSerial = 0;
|
||||
int64_t m_pendingCorrection = 0;
|
||||
|
||||
Publication m_publications[PUBLICATION_HISTORY_SIZE] = {};
|
||||
unsigned m_publicationIndex = 0;
|
||||
|
||||
int64_t m_lastPhaseError = 0;
|
||||
uint64_t m_acquiredFrames = 0;
|
||||
uint64_t m_skippedFrames = 0;
|
||||
uint64_t m_publishedFrames = 0;
|
||||
uint64_t m_lastLog = 0;
|
||||
uint64_t m_lastLogAcquired = 0;
|
||||
uint64_t m_lastLogSkipped = 0;
|
||||
uint64_t m_lastLogPublished = 0;
|
||||
|
||||
Client * FindClient(uint32_t clientID);
|
||||
Client * FindOrAllocateClient(uint32_t clientID);
|
||||
Publication * FindPublication(const Schedule& schedule,
|
||||
uint32_t frameSerial);
|
||||
bool ElectOwner(uint64_t now, uint32_t resetClientID = 0);
|
||||
bool ApplyFeedback(Client& client, const FrameScheduleUpdate& schedule);
|
||||
void AdvanceCurrentDeadline();
|
||||
void AdvanceDeadlineSerial(uint64_t count);
|
||||
void AdvanceDeadline(uint64_t now);
|
||||
static void AdvanceDelivery(Client& client, uint64_t now);
|
||||
void WakePublisher() const;
|
||||
|
||||
public:
|
||||
CFrameScheduler();
|
||||
~CFrameScheduler();
|
||||
|
||||
static uint64_t Nanotime();
|
||||
|
||||
void Reset();
|
||||
void UpdateSubscribers(const uint32_t * clientIDs, unsigned count,
|
||||
const uint32_t * ownerClientIDs, unsigned ownerCount, uint64_t now);
|
||||
bool UpdateSchedule(uint32_t sourceClientID,
|
||||
const FrameScheduleUpdate& schedule, uint64_t now);
|
||||
bool GetSchedule(Schedule& schedule) const;
|
||||
HANDLE GetWakeEvent() const { return m_wakeEvent; }
|
||||
void ObserveFrame(uint64_t now);
|
||||
void ForceFrame();
|
||||
bool GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
Schedule& schedule, bool& periodic, bool& republish);
|
||||
void FrameMissed(const Schedule& schedule, uint64_t now, bool periodic);
|
||||
void FrameSuperseded();
|
||||
bool TryFrameSubmitted(const Schedule& schedule, uint32_t frameSerial);
|
||||
void FramePublished(const Schedule& schedule, uint32_t frameSerial,
|
||||
uint64_t now, bool periodic);
|
||||
void FrameRetained(const Schedule& schedule, uint64_t now,
|
||||
bool periodic);
|
||||
void FrameRepublished(const Schedule& schedule, uint32_t frameSerial);
|
||||
bool TryFrameCompleted(const Schedule& schedule, uint32_t frameSerial,
|
||||
uint64_t completedAt);
|
||||
unsigned GetSecondaryRecipients(const uint32_t * clientIDs,
|
||||
unsigned count, uint32_t frameSerial, uint64_t now,
|
||||
uint32_t * recipients) const;
|
||||
bool GetSecondaryTarget(uint32_t frameSerial, uint64_t now,
|
||||
const uint32_t * blockedClientIDs, unsigned blockedCount,
|
||||
uint64_t& target) const;
|
||||
void FrameDelivered(const uint32_t * clientIDs, unsigned count,
|
||||
uint32_t frameSerial, uint64_t now);
|
||||
void RequestRepublish();
|
||||
void NotifyPublisher() const { WakePublisher(); }
|
||||
void TryRecordFrameTiming(uint64_t duration);
|
||||
void LogStatistics(uint64_t now);
|
||||
};
|
||||
31
idd/LGIdd/transport/FrameBufferTypes.h
Normal file
31
idd/LGIdd/transport/FrameBufferTypes.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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 <stdint.h>
|
||||
|
||||
struct PreparedFrameBuffer
|
||||
{
|
||||
unsigned frameIndex;
|
||||
uint8_t * mem;
|
||||
uint64_t heapOffset;
|
||||
bool fullCopy;
|
||||
};
|
||||
@@ -24,8 +24,9 @@
|
||||
|
||||
struct FrameMemoryLimits
|
||||
{
|
||||
uint64_t sharedSize = 0;
|
||||
uint64_t capacity = 0;
|
||||
uint64_t frameMemoryOffset = 0;
|
||||
uint64_t alignment = 0;
|
||||
uint64_t maxFrameSize = 0;
|
||||
unsigned bufferCount = 0;
|
||||
};
|
||||
|
||||
42
idd/LGIdd/transport/IControlTransport.h
Normal file
42
idd/LGIdd/transport/IControlTransport.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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 "postprocess/D12FrameFormat.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <wdf.h>
|
||||
#include <IddCx.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class IControlTransport
|
||||
{
|
||||
public:
|
||||
virtual ~IControlTransport() = default;
|
||||
|
||||
virtual void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
|
||||
const BYTE * data, UINT sdrWhiteLevel) = 0;
|
||||
virtual void SetColorTransform(
|
||||
std::shared_ptr<const D12ColorTransform> transform) = 0;
|
||||
virtual std::shared_ptr<const D12ColorTransform>
|
||||
GetColorTransform() const = 0;
|
||||
};
|
||||
87
idd/LGIdd/transport/IFrameTransport.h
Normal file
87
idd/LGIdd/transport/IFrameTransport.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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 "postprocess/D12FrameFormat.h"
|
||||
#include "transport/CFrameScheduler.h"
|
||||
#include "transport/FrameBufferTypes.h"
|
||||
#include "transport/TransportTypes.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class IFrameTransport
|
||||
{
|
||||
public:
|
||||
virtual ~IFrameTransport() = default;
|
||||
|
||||
virtual size_t GetMaxFrameSize() const = 0;
|
||||
|
||||
virtual bool FrameBufferAvailable(
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true) = 0;
|
||||
virtual bool HasPublishedFrame() const = 0;
|
||||
virtual void ProcessDeliveries() = 0;
|
||||
virtual bool GetPendingDeliveryTarget(
|
||||
uint64_t now, uint64_t& target) = 0;
|
||||
virtual bool RetryPendingDelivery(uint64_t now, bool& retry) = 0;
|
||||
virtual PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch,
|
||||
const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
|
||||
const RECT * dirtyRects, unsigned nbDirtyRects,
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true) = 0;
|
||||
virtual bool PublishFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool& deliveredToOwner) = 0;
|
||||
virtual bool RepublishFrameBuffer(
|
||||
const CFrameScheduler::Schedule& schedule) = 0;
|
||||
virtual bool TryFrameSubmitted(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule) = 0;
|
||||
virtual void CommitFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool periodic,
|
||||
bool deliveredToOwner) = 0;
|
||||
virtual void AbortFrameBuffer(unsigned frameIndex) = 0;
|
||||
virtual void FailFrameBuffer(unsigned frameIndex) = 0;
|
||||
virtual void CompleteFrameBuffer(
|
||||
unsigned frameIndex, bool succeeded) = 0;
|
||||
virtual void SetFrameTiming(unsigned frameIndex, uint64_t captureTime,
|
||||
uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime,
|
||||
uint64_t holdTime, const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t completedAt) = 0;
|
||||
virtual void WriteFrameBuffer(unsigned frameIndex, void * src,
|
||||
size_t offset, size_t len, bool setWritePos) const = 0;
|
||||
virtual void WriteFrameBufferRows(unsigned frameIndex, void * src,
|
||||
size_t offset, size_t rowBytes, size_t pitch,
|
||||
unsigned rows) const = 0;
|
||||
virtual void FinalizeFrameBuffer(unsigned frameIndex) const = 0;
|
||||
|
||||
virtual void ObserveFrame(uint64_t now) = 0;
|
||||
virtual void ForceFrame() = 0;
|
||||
virtual bool GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
CFrameScheduler::Schedule& schedule, bool& periodic,
|
||||
bool& republish) = 0;
|
||||
virtual void FrameMissed(const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t now, bool periodic) = 0;
|
||||
virtual void FrameSuperseded() = 0;
|
||||
virtual HANDLE GetFrameScheduleEvent() const = 0;
|
||||
virtual void TryRecordFrameTiming(uint64_t duration) = 0;
|
||||
};
|
||||
63
idd/LGIdd/transport/ITransport.h
Normal file
63
idd/LGIdd/transport/ITransport.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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 "transport/FrameMemoryLimits.h"
|
||||
#include "transport/TransportTypes.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class IControlTransport;
|
||||
class IFrameTransport;
|
||||
|
||||
class ITransportEvents
|
||||
{
|
||||
public:
|
||||
virtual ~ITransportEvents() = default;
|
||||
|
||||
virtual void OnSetCursorPos(int32_t x, int32_t y) = 0;
|
||||
virtual void OnSetResolution(uint32_t width, uint32_t height) = 0;
|
||||
};
|
||||
|
||||
class ITransport
|
||||
{
|
||||
public:
|
||||
enum class OpenResult
|
||||
{
|
||||
SUCCESS,
|
||||
RETRY,
|
||||
FAILURE,
|
||||
};
|
||||
|
||||
virtual ~ITransport() = default;
|
||||
|
||||
virtual OpenResult Open() = 0;
|
||||
virtual bool Initialize() = 0;
|
||||
virtual bool Setup(size_t alignment) = 0;
|
||||
virtual void Process(ITransportEvents& events) = 0;
|
||||
|
||||
virtual FrameMemoryLimits GetMemoryLimits() const = 0;
|
||||
virtual DirectFrameBufferMemory GetDirectMemory() const = 0;
|
||||
|
||||
virtual IFrameTransport& Frames() = 0;
|
||||
virtual IControlTransport& Control() = 0;
|
||||
};
|
||||
30
idd/LGIdd/transport/TransportFactory.cpp
Normal file
30
idd/LGIdd/transport/TransportFactory.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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/TransportFactory.h"
|
||||
|
||||
#include "transport/lgmp/CLGMPTransport.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
std::unique_ptr<ITransport> CreateTransport()
|
||||
{
|
||||
return std::unique_ptr<ITransport>(new (std::nothrow) CLGMPTransport());
|
||||
}
|
||||
27
idd/LGIdd/transport/TransportFactory.h
Normal file
27
idd/LGIdd/transport/TransportFactory.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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 "transport/ITransport.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<ITransport> CreateTransport();
|
||||
64
idd/LGIdd/transport/TransportTypes.h
Normal file
64
idd/LGIdd/transport/TransportTypes.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
enum : unsigned
|
||||
{
|
||||
TRANSPORT_FRAME_QUEUE_LENGTH = 2,
|
||||
TRANSPORT_FRAME_BUFFER_COUNT = 3,
|
||||
TRANSPORT_MAX_CLIENTS = 8,
|
||||
};
|
||||
|
||||
enum : uint32_t
|
||||
{
|
||||
FRAME_SCHEDULE_ACTIVE = 0x1,
|
||||
FRAME_SCHEDULE_RELEASE = 0x2,
|
||||
FRAME_SCHEDULE_RESET = 0x4,
|
||||
FRAME_SCHEDULE_IMMEDIATE = 0x8,
|
||||
};
|
||||
|
||||
struct FrameScheduleUpdate
|
||||
{
|
||||
uint32_t clientID;
|
||||
uint32_t generation;
|
||||
uint32_t flags;
|
||||
uint64_t period;
|
||||
uint64_t targetSlack;
|
||||
int64_t phaseError;
|
||||
uint32_t feedbackFrameSerial;
|
||||
uint32_t feedbackScheduleEpoch;
|
||||
uint32_t feedbackDeadlineSerial;
|
||||
uint32_t lease;
|
||||
};
|
||||
|
||||
struct DirectFrameBufferMemory
|
||||
{
|
||||
void * address = nullptr;
|
||||
size_t size = 0;
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return address && size;
|
||||
}
|
||||
};
|
||||
@@ -18,7 +18,7 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "transport/CIVSHMEM.h"
|
||||
#include "transport/lgmp/CIVSHMEM.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <SetupAPI.h>
|
||||
@@ -47,5 +47,5 @@ public:
|
||||
void Close();
|
||||
|
||||
size_t GetSize() const { return m_size; }
|
||||
void * GetMem () { return m_mem; }
|
||||
void * GetMem () const { return m_mem; }
|
||||
};
|
||||
@@ -18,7 +18,7 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "transport/CLGMPControl.h"
|
||||
#include "transport/lgmp/CLGMPControl.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "transport/CLGMPHost.h"
|
||||
#include "postprocess/D12FrameFormat.h"
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
#include "transport/IControlTransport.h"
|
||||
|
||||
#include "common/KVMFR.h"
|
||||
|
||||
@@ -31,9 +31,13 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
class CLGMPControl
|
||||
class CLGMPTransport;
|
||||
|
||||
class CLGMPControl final : public IControlTransport
|
||||
{
|
||||
private:
|
||||
friend class CLGMPTransport;
|
||||
|
||||
static constexpr int POINTER_SHAPE_BUFFERS = 3;
|
||||
static constexpr int COLOR_TRANSFORM_BUFFERS = 3;
|
||||
|
||||
@@ -56,27 +60,26 @@ private:
|
||||
|
||||
void SendColorTransform();
|
||||
void ResendCursor();
|
||||
|
||||
public:
|
||||
explicit CLGMPControl(CLGMPHost& host) :
|
||||
m_host(host) {}
|
||||
~CLGMPControl();
|
||||
|
||||
CLGMPControl(const CLGMPControl&) = delete;
|
||||
CLGMPControl& operator=(const CLGMPControl&) = delete;
|
||||
|
||||
bool Initialize();
|
||||
void DeInit();
|
||||
|
||||
LGMP_STATUS ReadDataWithSource(void * data, size_t * size,
|
||||
uint32_t * sourceClientID);
|
||||
LGMP_STATUS AckData();
|
||||
bool HasNewSubscribers();
|
||||
void ResendState();
|
||||
|
||||
public:
|
||||
explicit CLGMPControl(CLGMPHost& host) :
|
||||
m_host(host) {}
|
||||
~CLGMPControl() override;
|
||||
|
||||
CLGMPControl(const CLGMPControl&) = delete;
|
||||
CLGMPControl& operator=(const CLGMPControl&) = delete;
|
||||
|
||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data,
|
||||
UINT sdrWhiteLevel);
|
||||
UINT sdrWhiteLevel) override;
|
||||
void SetColorTransform(
|
||||
std::shared_ptr<const D12ColorTransform> transform);
|
||||
std::shared_ptr<const D12ColorTransform> GetColorTransform() const;
|
||||
void ResendState();
|
||||
std::shared_ptr<const D12ColorTransform> transform) override;
|
||||
std::shared_ptr<const D12ColorTransform>
|
||||
GetColorTransform() const override;
|
||||
};
|
||||
@@ -18,14 +18,30 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "transport/CFrameTransport.h"
|
||||
#include "transport/lgmp/CLGMPFrameTransport.h"
|
||||
|
||||
#include "transport/CIVSHMEM.h"
|
||||
#include "transport/CLGMPHost.h"
|
||||
#include "transport/lgmp/CIVSHMEM.h"
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
#include "CDebug.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4200)
|
||||
struct LGMPBuffer
|
||||
{
|
||||
volatile uint32_t wp;
|
||||
uint8_t data[0];
|
||||
};
|
||||
#pragma warning(pop)
|
||||
|
||||
static_assert(TRANSPORT_FRAME_QUEUE_LENGTH == LGMP_Q_FRAME_LEN,
|
||||
"The capture pipeline must match the LGMP frame queue");
|
||||
static_assert(TRANSPORT_FRAME_BUFFER_COUNT == LGMP_Q_FRAME_BUFFER_LEN,
|
||||
"The capture buffer pool must match the LGMP frame buffers");
|
||||
static_assert(TRANSPORT_MAX_CLIENTS == LGMP_MAX_CLIENTS,
|
||||
"The scheduler must support every LGMP client");
|
||||
|
||||
static const struct LGMPQueueConfig FRAME_QUEUE_CONFIG =
|
||||
{
|
||||
LGMP_Q_FRAME, // queueID
|
||||
@@ -49,19 +65,19 @@ static bool FrameScheduleMatches(
|
||||
a.epoch == b.epoch;
|
||||
}
|
||||
|
||||
CFrameTransport::CFrameTransport(
|
||||
CLGMPFrameTransport::CLGMPFrameTransport(
|
||||
CLGMPHost& host, CIVSHMEM& ivshmem) :
|
||||
m_host(host),
|
||||
m_ivshmem(ivshmem)
|
||||
{
|
||||
}
|
||||
|
||||
CFrameTransport::~CFrameTransport()
|
||||
CLGMPFrameTransport::~CLGMPFrameTransport()
|
||||
{
|
||||
DeInit();
|
||||
}
|
||||
|
||||
bool CFrameTransport::Initialize()
|
||||
bool CLGMPFrameTransport::Initialize()
|
||||
{
|
||||
if (m_frameQueue)
|
||||
{
|
||||
@@ -100,13 +116,13 @@ bool CFrameTransport::Initialize()
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFrameTransport::SealMemoryLayout()
|
||||
void CLGMPFrameTransport::SealMemoryLayout()
|
||||
{
|
||||
m_frameMemoryOffset =
|
||||
m_ivshmem.GetSize() - m_host.Available();
|
||||
}
|
||||
|
||||
bool CFrameTransport::Setup(size_t alignSize)
|
||||
bool CLGMPFrameTransport::Setup(size_t alignSize)
|
||||
{
|
||||
// This may get called multiple times as frame buffers cannot be allocated
|
||||
// until the GPU-specific alignment is known.
|
||||
@@ -116,7 +132,7 @@ bool CFrameTransport::Setup(size_t alignSize)
|
||||
m_alignSize = alignSize;
|
||||
|
||||
if (!m_alignSize || (m_alignSize & (m_alignSize - 1)) ||
|
||||
m_alignSize < sizeof(KVMFRFrame) + sizeof(FrameBuffer))
|
||||
m_alignSize < sizeof(KVMFRFrame) + sizeof(LGMPBuffer))
|
||||
{
|
||||
DEBUG_ERROR("Invalid frame buffer alignment: %llu",
|
||||
(unsigned long long)m_alignSize);
|
||||
@@ -147,7 +163,7 @@ bool CFrameTransport::Setup(size_t alignSize)
|
||||
return false;
|
||||
}
|
||||
|
||||
// The KVMFR frame header and FrameBuffer write position occupy the first
|
||||
// The KVMFR frame header and frame-buffer write position occupy the first
|
||||
// alignment unit. Only the bytes after it are usable for pixel data.
|
||||
const size_t maxFrameSize = frameAllocationSize - m_alignSize;
|
||||
DEBUG_INFO("Max Frame Data Size: %u MiB",
|
||||
@@ -173,9 +189,9 @@ bool CFrameTransport::Setup(size_t alignSize)
|
||||
* Put the framebuffer on the border of the next page, this is to allow
|
||||
* for aligned DMA transfers by the receiver.
|
||||
*/
|
||||
const size_t alignOffset = alignSize - sizeof(FrameBuffer);
|
||||
const size_t alignOffset = alignSize - sizeof(LGMPBuffer);
|
||||
m_frame[i]->offset = (uint32_t)alignOffset;
|
||||
m_frameBuffer[i] = reinterpret_cast<FrameBuffer *>(
|
||||
m_frameBuffer[i] = reinterpret_cast<LGMPBuffer *>(
|
||||
reinterpret_cast<uint8_t *>(m_frame[i]) + alignOffset);
|
||||
m_frameInFlight[i].store(false, std::memory_order_release);
|
||||
m_frameCompleted[i] = false;
|
||||
@@ -196,7 +212,7 @@ bool CFrameTransport::Setup(size_t alignSize)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFrameTransport::DeInit()
|
||||
void CLGMPFrameTransport::DeInit()
|
||||
{
|
||||
m_frameScheduler.Reset();
|
||||
|
||||
@@ -226,18 +242,19 @@ void CFrameTransport::DeInit()
|
||||
memset(m_frameOwnerQueue, 0, sizeof(m_frameOwnerQueue));
|
||||
}
|
||||
|
||||
FrameMemoryLimits CFrameTransport::GetMemoryLimits() const
|
||||
FrameMemoryLimits CLGMPFrameTransport::GetMemoryLimits() const
|
||||
{
|
||||
FrameMemoryLimits limits;
|
||||
limits.sharedSize = m_ivshmem.GetSize();
|
||||
limits.capacity = m_ivshmem.GetSize();
|
||||
limits.frameMemoryOffset = m_frameMemoryOffset;
|
||||
limits.alignment = m_alignSize;
|
||||
limits.maxFrameSize = m_maxFrameSize;
|
||||
limits.bufferCount = LGMP_Q_FRAME_BUFFER_LEN;
|
||||
return limits;
|
||||
}
|
||||
|
||||
CFrameTransport::SubscriberSnapshot
|
||||
CFrameTransport::SnapshotSubscribers() const
|
||||
CLGMPFrameTransport::SubscriberSnapshot
|
||||
CLGMPFrameTransport::SnapshotSubscribers() const
|
||||
{
|
||||
SubscriberSnapshot snapshot;
|
||||
snapshot.status = lgmpHostGetClientIDs(
|
||||
@@ -276,7 +293,7 @@ CFrameTransport::SnapshotSubscribers() const
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
void CFrameTransport::FinalizeSubscribers(
|
||||
void CLGMPFrameTransport::FinalizeSubscribers(
|
||||
const SubscriberSnapshot& snapshot, uint64_t now)
|
||||
{
|
||||
if (snapshot.status == LGMP_OK)
|
||||
@@ -303,14 +320,14 @@ void CFrameTransport::FinalizeSubscribers(
|
||||
ProcessFrameDeliveries();
|
||||
}
|
||||
|
||||
bool CFrameTransport::UpdateSchedule(uint32_t sourceClientID,
|
||||
const KVMFRFrameSchedule& schedule, uint64_t now)
|
||||
bool CLGMPFrameTransport::UpdateSchedule(uint32_t sourceClientID,
|
||||
const FrameScheduleUpdate& schedule, uint64_t now)
|
||||
{
|
||||
return m_frameScheduler.UpdateSchedule(sourceClientID, schedule, now);
|
||||
}
|
||||
|
||||
CFrameTransport::SharedFramePostResult
|
||||
CFrameTransport::PostSharedFrame(unsigned frameIndex,
|
||||
CLGMPFrameTransport::SharedFramePostResult
|
||||
CLGMPFrameTransport::PostSharedFrame(unsigned frameIndex,
|
||||
uint32_t excludeClientID, uint64_t now)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN ||
|
||||
@@ -391,7 +408,7 @@ CFrameTransport::PostSharedFrame(unsigned frameIndex,
|
||||
return SHARED_FRAME_POSTED;
|
||||
}
|
||||
|
||||
bool CFrameTransport::PostSharedOwnerFrame(unsigned frameIndex,
|
||||
bool CLGMPFrameTransport::PostSharedOwnerFrame(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN || !schedule.clientID ||
|
||||
@@ -419,7 +436,7 @@ bool CFrameTransport::PostSharedOwnerFrame(unsigned frameIndex,
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFrameTransport::ProcessFrameDeliveries()
|
||||
void CLGMPFrameTransport::ProcessFrameDeliveries()
|
||||
{
|
||||
if (!m_frameQueue)
|
||||
return;
|
||||
@@ -471,7 +488,7 @@ void CFrameTransport::ProcessFrameDeliveries()
|
||||
m_frameScheduler.NotifyPublisher();
|
||||
}
|
||||
|
||||
int CFrameTransport::FindAvailableOwnerQueue(
|
||||
int CLGMPFrameTransport::FindAvailableOwnerQueue(
|
||||
unsigned preferredIndex) const
|
||||
{
|
||||
for (unsigned i = 0; i < LGMP_Q_FRAME_LEN; ++i)
|
||||
@@ -487,7 +504,7 @@ int CFrameTransport::FindAvailableOwnerQueue(
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned CFrameTransport::CountOwnerDeliveries(
|
||||
unsigned CLGMPFrameTransport::CountOwnerDeliveries(
|
||||
uint32_t clientID) const
|
||||
{
|
||||
unsigned count = 0;
|
||||
@@ -503,7 +520,7 @@ unsigned CFrameTransport::CountOwnerDeliveries(
|
||||
return count;
|
||||
}
|
||||
|
||||
bool CFrameTransport::HasMatchingOwnerDelivery(
|
||||
bool CLGMPFrameTransport::HasMatchingOwnerDelivery(
|
||||
uint32_t clientID, unsigned frameIndex, uint64_t token) const
|
||||
{
|
||||
for (const OwnerDelivery& delivery : m_ownerDelivery)
|
||||
@@ -526,7 +543,7 @@ bool CFrameTransport::HasMatchingOwnerDelivery(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CFrameTransport::FrameBufferReferenced(
|
||||
bool CLGMPFrameTransport::FrameBufferReferenced(
|
||||
unsigned frameIndex) const
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
|
||||
@@ -548,7 +565,7 @@ bool CFrameTransport::FrameBufferReferenced(
|
||||
return false;
|
||||
}
|
||||
|
||||
int CFrameTransport::FindAvailableFrameBuffer(
|
||||
int CLGMPFrameTransport::FindAvailableFrameBuffer(
|
||||
bool allowReady) const
|
||||
{
|
||||
const LONG readyFrameIndex =
|
||||
@@ -582,7 +599,7 @@ int CFrameTransport::FindAvailableFrameBuffer(
|
||||
return static_cast<int>(readyFrameIndex);
|
||||
}
|
||||
|
||||
int CFrameTransport::FindNewestCompletedFrame(
|
||||
int CLGMPFrameTransport::FindNewestCompletedFrame(
|
||||
unsigned excludeFrameIndex) const
|
||||
{
|
||||
int newestFrame = -1;
|
||||
@@ -605,7 +622,7 @@ int CFrameTransport::FindNewestCompletedFrame(
|
||||
return newestFrame;
|
||||
}
|
||||
|
||||
bool CFrameTransport::FrameBufferAvailable(
|
||||
bool CLGMPFrameTransport::FrameBufferAvailable(
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement)
|
||||
{
|
||||
@@ -642,7 +659,7 @@ bool CFrameTransport::FrameBufferAvailable(
|
||||
return available;
|
||||
}
|
||||
|
||||
void CFrameTransport::ProcessFrameQueue()
|
||||
void CLGMPFrameTransport::ProcessDeliveries()
|
||||
{
|
||||
if (!m_host.IsInitialized())
|
||||
return;
|
||||
@@ -656,7 +673,7 @@ void CFrameTransport::ProcessFrameQueue()
|
||||
ProcessFrameDeliveries();
|
||||
}
|
||||
|
||||
bool CFrameTransport::GetSharedFrameTarget(uint64_t now,
|
||||
bool CLGMPFrameTransport::GetPendingDeliveryTarget(uint64_t now,
|
||||
uint64_t& target)
|
||||
{
|
||||
if (!m_frameQueue)
|
||||
@@ -686,7 +703,7 @@ bool CFrameTransport::GetSharedFrameTarget(uint64_t now,
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CFrameTransport::ReplaySharedFrame(uint64_t now, bool& retry)
|
||||
bool CLGMPFrameTransport::RetryPendingDelivery(uint64_t now, bool& retry)
|
||||
{
|
||||
retry = false;
|
||||
if (!m_frameQueue)
|
||||
@@ -710,7 +727,7 @@ bool CFrameTransport::ReplaySharedFrame(uint64_t now, bool& retry)
|
||||
return result == SHARED_FRAME_POSTED;
|
||||
}
|
||||
|
||||
PreparedFrameBuffer CFrameTransport::PrepareFrameBuffer(
|
||||
PreparedFrameBuffer CLGMPFrameTransport::PrepareFrameBuffer(
|
||||
unsigned pitch, const D12FrameFormat& srcFormat,
|
||||
const D12FrameFormat& dstFormat, const RECT * dirtyRects,
|
||||
unsigned nbDirtyRects, const CFrameScheduler::Schedule& schedule,
|
||||
@@ -903,16 +920,18 @@ PreparedFrameBuffer CFrameTransport::PrepareFrameBuffer(
|
||||
}
|
||||
}
|
||||
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
LGMPBuffer * fb = m_frameBuffer[frameIndex];
|
||||
fb->wp = 0;
|
||||
|
||||
result.frameIndex = frameIndex;
|
||||
result.mem = fb->data;
|
||||
result.heapOffset = reinterpret_cast<uintptr_t>(fb->data) -
|
||||
reinterpret_cast<uintptr_t>(m_ivshmem.GetMem());
|
||||
result.fullCopy = fullCopy;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CFrameTransport::PublishFrameBuffer(unsigned frameIndex,
|
||||
bool CLGMPFrameTransport::PublishFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner)
|
||||
{
|
||||
deliveredToOwner = false;
|
||||
@@ -1017,7 +1036,7 @@ bool CFrameTransport::PublishFrameBuffer(unsigned frameIndex,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CFrameTransport::RepublishFrameBuffer(
|
||||
bool CLGMPFrameTransport::RepublishFrameBuffer(
|
||||
const CFrameScheduler::Schedule& schedule)
|
||||
{
|
||||
if (!schedule.clientID)
|
||||
@@ -1118,7 +1137,7 @@ bool CFrameTransport::RepublishFrameBuffer(
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFrameTransport::CommitFrameBuffer(unsigned frameIndex,
|
||||
void CLGMPFrameTransport::CommitFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool periodic,
|
||||
bool deliveredToOwner)
|
||||
{
|
||||
@@ -1133,7 +1152,7 @@ void CFrameTransport::CommitFrameBuffer(unsigned frameIndex,
|
||||
m_frameScheduler.FrameRetained(schedule, now, periodic);
|
||||
}
|
||||
|
||||
bool CFrameTransport::TryFrameSubmitted(unsigned frameIndex,
|
||||
bool CLGMPFrameTransport::TryFrameSubmitted(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
|
||||
@@ -1143,17 +1162,17 @@ bool CFrameTransport::TryFrameSubmitted(unsigned frameIndex,
|
||||
schedule, m_frame[frameIndex]->frameSerial);
|
||||
}
|
||||
|
||||
void CFrameTransport::ObserveFrame(uint64_t now)
|
||||
void CLGMPFrameTransport::ObserveFrame(uint64_t now)
|
||||
{
|
||||
m_frameScheduler.ObserveFrame(now);
|
||||
}
|
||||
|
||||
void CFrameTransport::ForceFrame()
|
||||
void CLGMPFrameTransport::ForceFrame()
|
||||
{
|
||||
m_frameScheduler.ForceFrame();
|
||||
}
|
||||
|
||||
bool CFrameTransport::GetPublishTarget(uint64_t now,
|
||||
bool CLGMPFrameTransport::GetPublishTarget(uint64_t now,
|
||||
uint64_t& target, CFrameScheduler::Schedule& schedule, bool& periodic,
|
||||
bool& republish)
|
||||
{
|
||||
@@ -1161,23 +1180,23 @@ bool CFrameTransport::GetPublishTarget(uint64_t now,
|
||||
now, target, schedule, periodic, republish);
|
||||
}
|
||||
|
||||
void CFrameTransport::FrameMissed(
|
||||
void CLGMPFrameTransport::FrameMissed(
|
||||
const CFrameScheduler::Schedule& schedule, uint64_t now, bool periodic)
|
||||
{
|
||||
m_frameScheduler.FrameMissed(schedule, now, periodic);
|
||||
}
|
||||
|
||||
void CFrameTransport::FrameSuperseded()
|
||||
void CLGMPFrameTransport::FrameSuperseded()
|
||||
{
|
||||
m_frameScheduler.FrameSuperseded();
|
||||
}
|
||||
|
||||
void CFrameTransport::TryRecordFrameTiming(uint64_t duration)
|
||||
void CLGMPFrameTransport::TryRecordFrameTiming(uint64_t duration)
|
||||
{
|
||||
m_frameScheduler.TryRecordFrameTiming(duration);
|
||||
}
|
||||
|
||||
void CFrameTransport::AbortFrameBuffer(unsigned frameIndex)
|
||||
void CLGMPFrameTransport::AbortFrameBuffer(unsigned frameIndex)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
|
||||
return;
|
||||
@@ -1193,7 +1212,7 @@ void CFrameTransport::AbortFrameBuffer(unsigned frameIndex)
|
||||
ReleaseSRWLockExclusive(&m_framePublishLock);
|
||||
}
|
||||
|
||||
void CFrameTransport::FailFrameBuffer(unsigned frameIndex)
|
||||
void CLGMPFrameTransport::FailFrameBuffer(unsigned frameIndex)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
|
||||
return;
|
||||
@@ -1204,7 +1223,7 @@ void CFrameTransport::FailFrameBuffer(unsigned frameIndex)
|
||||
CompleteFrameBuffer(frameIndex, false);
|
||||
}
|
||||
|
||||
void CFrameTransport::CompleteFrameBuffer(
|
||||
void CLGMPFrameTransport::CompleteFrameBuffer(
|
||||
unsigned frameIndex, bool succeeded)
|
||||
{
|
||||
if (frameIndex >= LGMP_Q_FRAME_BUFFER_LEN)
|
||||
@@ -1232,7 +1251,7 @@ void CFrameTransport::CompleteFrameBuffer(
|
||||
ReleaseSRWLockExclusive(&m_framePublishLock);
|
||||
}
|
||||
|
||||
void CFrameTransport::SetFrameTiming(unsigned frameIndex,
|
||||
void CLGMPFrameTransport::SetFrameTiming(unsigned frameIndex,
|
||||
uint64_t captureTime, uint64_t postProcessTime, uint64_t copyTime,
|
||||
uint64_t readyTime, uint64_t holdTime,
|
||||
const CFrameScheduler::Schedule& schedule, uint64_t completedAt)
|
||||
@@ -1257,10 +1276,10 @@ void CFrameTransport::SetFrameTiming(unsigned frameIndex,
|
||||
InterlockedExchange((volatile LONG *)&frame->timingValid, 1);
|
||||
}
|
||||
|
||||
void CFrameTransport::WriteFrameBuffer(unsigned frameIndex, void * src,
|
||||
void CLGMPFrameTransport::WriteFrameBuffer(unsigned frameIndex, void * src,
|
||||
size_t offset, size_t len, bool setWritePos) const
|
||||
{
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
LGMPBuffer * fb = m_frameBuffer[frameIndex];
|
||||
|
||||
memcpy(
|
||||
reinterpret_cast<void *>(
|
||||
@@ -1273,11 +1292,11 @@ void CFrameTransport::WriteFrameBuffer(unsigned frameIndex, void * src,
|
||||
fb->wp = (uint32_t)(offset + len);
|
||||
}
|
||||
|
||||
void CFrameTransport::WriteFrameBufferRows(unsigned frameIndex,
|
||||
void CLGMPFrameTransport::WriteFrameBufferRows(unsigned frameIndex,
|
||||
void * src, size_t offset, size_t rowBytes, size_t pitch,
|
||||
unsigned rows) const
|
||||
{
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
LGMPBuffer * fb = m_frameBuffer[frameIndex];
|
||||
uint8_t * dst = fb->data + offset;
|
||||
uint8_t * source = static_cast<uint8_t *>(src) + offset;
|
||||
for (unsigned row = 0; row < rows; ++row)
|
||||
@@ -1288,9 +1307,9 @@ void CFrameTransport::WriteFrameBufferRows(unsigned frameIndex,
|
||||
}
|
||||
}
|
||||
|
||||
void CFrameTransport::FinalizeFrameBuffer(unsigned frameIndex) const
|
||||
void CLGMPFrameTransport::FinalizeFrameBuffer(unsigned frameIndex) const
|
||||
{
|
||||
const KVMFRFrame * frame = m_frame[frameIndex];
|
||||
FrameBuffer * fb = m_frameBuffer[frameIndex];
|
||||
LGMPBuffer * fb = m_frameBuffer[frameIndex];
|
||||
fb->wp = frame->dataHeight * frame->pitch;
|
||||
}
|
||||
@@ -28,18 +28,21 @@ extern "C" {
|
||||
#include "lgmp/host.h"
|
||||
}
|
||||
|
||||
#include "capture/CFrameScheduler.h"
|
||||
#include "capture/FrameBufferTypes.h"
|
||||
#include "common/KVMFR.h"
|
||||
#include "postprocess/D12FrameFormat.h"
|
||||
#include "transport/CFrameScheduler.h"
|
||||
#include "transport/FrameMemoryLimits.h"
|
||||
#include "transport/IFrameTransport.h"
|
||||
|
||||
class CIVSHMEM;
|
||||
class CLGMPHost;
|
||||
class CLGMPTransport;
|
||||
struct LGMPBuffer;
|
||||
|
||||
class CFrameTransport
|
||||
class CLGMPFrameTransport final : public IFrameTransport
|
||||
{
|
||||
public:
|
||||
private:
|
||||
friend class CLGMPTransport;
|
||||
|
||||
struct SubscriberSnapshot
|
||||
{
|
||||
uint32_t clientIDs [LGMP_MAX_CLIENTS] = {};
|
||||
@@ -103,7 +106,7 @@ private:
|
||||
uint32_t m_frameSerial = 0;
|
||||
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_BUFFER_LEN] = {};
|
||||
KVMFRFrame * m_frame [LGMP_Q_FRAME_BUFFER_LEN] = {};
|
||||
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_BUFFER_LEN] = {};
|
||||
LGMPBuffer * m_frameBuffer[LGMP_Q_FRAME_BUFFER_LEN] = {};
|
||||
|
||||
unsigned m_width = 0;
|
||||
unsigned m_height = 0;
|
||||
@@ -136,74 +139,77 @@ private:
|
||||
uint32_t excludeClientID, uint64_t now);
|
||||
bool PostSharedOwnerFrame(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule);
|
||||
|
||||
public:
|
||||
CFrameTransport(CLGMPHost& host, CIVSHMEM& ivshmem);
|
||||
~CFrameTransport();
|
||||
|
||||
CFrameTransport(const CFrameTransport&) = delete;
|
||||
CFrameTransport& operator=(const CFrameTransport&) = delete;
|
||||
|
||||
CLGMPFrameTransport(CLGMPHost& host, CIVSHMEM& ivshmem);
|
||||
bool Initialize();
|
||||
void SealMemoryLayout();
|
||||
bool Setup(size_t alignSize);
|
||||
void DeInit();
|
||||
|
||||
FrameMemoryLimits GetMemoryLimits() const;
|
||||
size_t GetMaxFrameSize() const { return m_maxFrameSize; }
|
||||
CIVSHMEM& GetIVSHMEM() { return m_ivshmem; }
|
||||
|
||||
SubscriberSnapshot SnapshotSubscribers() const;
|
||||
void FinalizeSubscribers(
|
||||
const SubscriberSnapshot& snapshot, uint64_t now);
|
||||
bool UpdateSchedule(uint32_t sourceClientID,
|
||||
const KVMFRFrameSchedule& schedule, uint64_t now);
|
||||
const FrameScheduleUpdate& schedule, uint64_t now);
|
||||
|
||||
public:
|
||||
~CLGMPFrameTransport() override;
|
||||
|
||||
CLGMPFrameTransport(const CLGMPFrameTransport&) = delete;
|
||||
CLGMPFrameTransport& operator=(const CLGMPFrameTransport&) = delete;
|
||||
|
||||
size_t GetMaxFrameSize() const override { return m_maxFrameSize; }
|
||||
|
||||
bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true);
|
||||
bool HasPublishedFrame() const
|
||||
bool allowReadyReplacement = true) override;
|
||||
bool HasPublishedFrame() const override
|
||||
{
|
||||
return m_readyFrameIndex.load(std::memory_order_acquire) >= 0;
|
||||
}
|
||||
void ProcessFrameQueue();
|
||||
bool GetSharedFrameTarget(uint64_t now, uint64_t& target);
|
||||
bool ReplaySharedFrame(uint64_t now, bool& retry);
|
||||
void ProcessDeliveries() override;
|
||||
bool GetPendingDeliveryTarget(
|
||||
uint64_t now, uint64_t& target) override;
|
||||
bool RetryPendingDelivery(uint64_t now, bool& retry) override;
|
||||
PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch,
|
||||
const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat,
|
||||
const RECT * dirtyRects, unsigned nbDirtyRects,
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool allowReadyReplacement = true);
|
||||
bool allowReadyReplacement = true) override;
|
||||
bool PublishFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner);
|
||||
bool RepublishFrameBuffer(const CFrameScheduler::Schedule& schedule);
|
||||
const CFrameScheduler::Schedule& schedule,
|
||||
bool& deliveredToOwner) override;
|
||||
bool RepublishFrameBuffer(
|
||||
const CFrameScheduler::Schedule& schedule) override;
|
||||
bool TryFrameSubmitted(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule);
|
||||
const CFrameScheduler::Schedule& schedule) override;
|
||||
void CommitFrameBuffer(unsigned frameIndex,
|
||||
const CFrameScheduler::Schedule& schedule, bool periodic,
|
||||
bool deliveredToOwner);
|
||||
void AbortFrameBuffer(unsigned frameIndex);
|
||||
void FailFrameBuffer(unsigned frameIndex);
|
||||
void CompleteFrameBuffer(unsigned frameIndex, bool succeeded);
|
||||
bool deliveredToOwner) override;
|
||||
void AbortFrameBuffer(unsigned frameIndex) override;
|
||||
void FailFrameBuffer(unsigned frameIndex) override;
|
||||
void CompleteFrameBuffer(
|
||||
unsigned frameIndex, bool succeeded) override;
|
||||
void SetFrameTiming(unsigned frameIndex, uint64_t captureTime,
|
||||
uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime,
|
||||
uint64_t holdTime, const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t completedAt);
|
||||
uint64_t completedAt) override;
|
||||
void WriteFrameBuffer(unsigned frameIndex, void * src, size_t offset,
|
||||
size_t len, bool setWritePos) const;
|
||||
size_t len, bool setWritePos) const override;
|
||||
void WriteFrameBufferRows(unsigned frameIndex, void * src,
|
||||
size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const;
|
||||
void FinalizeFrameBuffer(unsigned frameIndex) const;
|
||||
size_t offset, size_t rowBytes, size_t pitch,
|
||||
unsigned rows) const override;
|
||||
void FinalizeFrameBuffer(unsigned frameIndex) const override;
|
||||
|
||||
void ObserveFrame(uint64_t now);
|
||||
void ForceFrame();
|
||||
void ObserveFrame(uint64_t now) override;
|
||||
void ForceFrame() override;
|
||||
bool GetPublishTarget(uint64_t now, uint64_t& target,
|
||||
CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish);
|
||||
CFrameScheduler::Schedule& schedule, bool& periodic,
|
||||
bool& republish) override;
|
||||
void FrameMissed(const CFrameScheduler::Schedule& schedule,
|
||||
uint64_t now, bool periodic);
|
||||
void FrameSuperseded();
|
||||
HANDLE GetFrameScheduleEvent() const
|
||||
uint64_t now, bool periodic) override;
|
||||
void FrameSuperseded() override;
|
||||
HANDLE GetFrameScheduleEvent() const override
|
||||
{
|
||||
return m_frameScheduler.GetWakeEvent();
|
||||
}
|
||||
void TryRecordFrameTiming(uint64_t duration);
|
||||
void TryRecordFrameTiming(uint64_t duration) override;
|
||||
};
|
||||
@@ -18,9 +18,9 @@
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "transport/CLGMPHost.h"
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
|
||||
#include "transport/CIVSHMEM.h"
|
||||
#include "transport/lgmp/CIVSHMEM.h"
|
||||
#include "platform/CPlatformInfo.h"
|
||||
#include "CDebug.h"
|
||||
#include "VersionInfo.h"
|
||||
183
idd/LGIdd/transport/lgmp/CLGMPTransport.cpp
Normal file
183
idd/LGIdd/transport/lgmp/CLGMPTransport.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* 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/lgmp/CLGMPTransport.h"
|
||||
|
||||
#include "CDebug.h"
|
||||
#include "common/KVMFR.h"
|
||||
|
||||
static bool TranslateFrameScheduleFlags(
|
||||
uint32_t source, uint32_t& destination)
|
||||
{
|
||||
static const uint32_t validFlags =
|
||||
KVMFR_FRAME_SCHEDULE_ACTIVE |
|
||||
KVMFR_FRAME_SCHEDULE_RELEASE |
|
||||
KVMFR_FRAME_SCHEDULE_RESET |
|
||||
KVMFR_FRAME_SCHEDULE_IMMEDIATE;
|
||||
|
||||
if (source & ~validFlags)
|
||||
return false;
|
||||
|
||||
destination = 0;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_ACTIVE)
|
||||
destination |= FRAME_SCHEDULE_ACTIVE;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_RELEASE)
|
||||
destination |= FRAME_SCHEDULE_RELEASE;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_RESET)
|
||||
destination |= FRAME_SCHEDULE_RESET;
|
||||
if (source & KVMFR_FRAME_SCHEDULE_IMMEDIATE)
|
||||
destination |= FRAME_SCHEDULE_IMMEDIATE;
|
||||
return true;
|
||||
}
|
||||
|
||||
CLGMPTransport::CLGMPTransport() :
|
||||
m_control(m_host),
|
||||
m_frames(m_host, m_ivshmem)
|
||||
{
|
||||
}
|
||||
|
||||
ITransport::OpenResult CLGMPTransport::Open()
|
||||
{
|
||||
if (m_ivshmem.GetMem())
|
||||
return OpenResult::SUCCESS;
|
||||
|
||||
if (!m_ivshmem.Init() || !m_ivshmem.Open())
|
||||
return OpenResult::RETRY;
|
||||
|
||||
return OpenResult::SUCCESS;
|
||||
}
|
||||
|
||||
bool CLGMPTransport::Initialize()
|
||||
{
|
||||
if (!m_host.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_frames.Initialize() || !m_control.Initialize())
|
||||
return false;
|
||||
|
||||
m_frames.SealMemoryLayout();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLGMPTransport::Setup(size_t alignment)
|
||||
{
|
||||
return m_frames.Setup(alignment);
|
||||
}
|
||||
|
||||
void CLGMPTransport::Process(ITransportEvents& events)
|
||||
{
|
||||
const LGMP_STATUS processStatus = m_host.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 CLGMPFrameTransport::SubscriberSnapshot subscribers =
|
||||
m_frames.SnapshotSubscribers();
|
||||
|
||||
uint8_t data[LGMP_MSGS_SIZE];
|
||||
size_t size;
|
||||
uint32_t sourceClientID;
|
||||
LGMP_STATUS status;
|
||||
while ((status = m_control.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);
|
||||
events.OnSetCursorPos(position->x, position->y);
|
||||
break;
|
||||
}
|
||||
|
||||
case KVMFR_MESSAGE_WINDOWSIZE:
|
||||
{
|
||||
KVMFRWindowSize * window =
|
||||
reinterpret_cast<KVMFRWindowSize *>(msg);
|
||||
events.OnSetResolution(window->w, window->h);
|
||||
break;
|
||||
}
|
||||
|
||||
case KVMFR_MESSAGE_FRAME_SCHEDULE:
|
||||
{
|
||||
const KVMFRFrameSchedule * schedule =
|
||||
reinterpret_cast<KVMFRFrameSchedule *>(msg);
|
||||
uint32_t translatedFlags = 0;
|
||||
bool valid = size == sizeof(*schedule) &&
|
||||
TranslateFrameScheduleFlags(schedule->flags, translatedFlags);
|
||||
if (valid)
|
||||
{
|
||||
FrameScheduleUpdate update = {};
|
||||
update.clientID = schedule->clientID;
|
||||
update.generation = schedule->generation;
|
||||
update.flags = translatedFlags;
|
||||
update.period = schedule->period;
|
||||
update.targetSlack = schedule->targetSlack;
|
||||
update.phaseError = schedule->phaseError;
|
||||
update.feedbackFrameSerial = schedule->feedbackFrameSerial;
|
||||
update.feedbackScheduleEpoch = schedule->feedbackScheduleEpoch;
|
||||
update.feedbackDeadlineSerial = schedule->feedbackDeadlineSerial;
|
||||
update.lease = schedule->lease;
|
||||
valid = m_frames.UpdateSchedule(sourceClientID, update, now);
|
||||
}
|
||||
if (!valid)
|
||||
DEBUG_WARN("Ignoring invalid KVMFR frame schedule");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_control.AckData();
|
||||
}
|
||||
|
||||
m_frames.FinalizeSubscribers(subscribers, now);
|
||||
|
||||
if (m_control.HasNewSubscribers())
|
||||
m_control.ResendState();
|
||||
}
|
||||
|
||||
FrameMemoryLimits CLGMPTransport::GetMemoryLimits() const
|
||||
{
|
||||
return m_frames.GetMemoryLimits();
|
||||
}
|
||||
|
||||
DirectFrameBufferMemory CLGMPTransport::GetDirectMemory() const
|
||||
{
|
||||
return {m_ivshmem.GetMem(), m_ivshmem.GetSize()};
|
||||
}
|
||||
56
idd/LGIdd/transport/lgmp/CLGMPTransport.h
Normal file
56
idd/LGIdd/transport/lgmp/CLGMPTransport.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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 "transport/ITransport.h"
|
||||
#include "transport/lgmp/CIVSHMEM.h"
|
||||
#include "transport/lgmp/CLGMPControl.h"
|
||||
#include "transport/lgmp/CLGMPFrameTransport.h"
|
||||
#include "transport/lgmp/CLGMPHost.h"
|
||||
|
||||
class CLGMPTransport final : public ITransport
|
||||
{
|
||||
private:
|
||||
// Keep this declaration order. Destruction must release frame and control
|
||||
// allocations before the LGMP host and its IVSHMEM mapping are destroyed.
|
||||
CIVSHMEM m_ivshmem;
|
||||
CLGMPHost m_host;
|
||||
CLGMPControl m_control;
|
||||
CLGMPFrameTransport m_frames;
|
||||
|
||||
public:
|
||||
CLGMPTransport();
|
||||
~CLGMPTransport() override = default;
|
||||
|
||||
CLGMPTransport(const CLGMPTransport&) = delete;
|
||||
CLGMPTransport& operator=(const CLGMPTransport&) = delete;
|
||||
|
||||
OpenResult Open() override;
|
||||
bool Initialize() override;
|
||||
bool Setup(size_t alignment) override;
|
||||
void Process(ITransportEvents& events) override;
|
||||
|
||||
FrameMemoryLimits GetMemoryLimits() const override;
|
||||
DirectFrameBufferMemory GetDirectMemory() const override;
|
||||
|
||||
IFrameTransport& Frames() override { return m_frames; }
|
||||
IControlTransport& Control() override { return m_control; }
|
||||
};
|
||||
Reference in New Issue
Block a user