/** * 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 #include #include extern "C" { #include "lgmp/host.h" } #include "common/KVMFR.h" #include "capture/CFrameScheduler.h" #include "capture/FramePipeline.h" #include "transport/FrameMemoryLimits.h" #include "transport/IFrameTransport.h" class CIVSHMEM; class CLGMPHost; class CLGMPTransport; struct LGMPBuffer; class CLGMPFrameTransport final : public IFrameTransport { private: friend class CLGMPTransport; struct SubscriberSnapshot { uint32_t clientIDs [LGMP_MAX_CLIENTS] = {}; uint32_t ownerClientIDs[LGMP_MAX_CLIENTS] = {}; unsigned clientCount = 0; unsigned ownerClientCount = 0; LGMP_STATUS status = LGMP_OK; }; private: enum SharedFramePostResult { SHARED_FRAME_FAILED, SHARED_FRAME_IDLE, SHARED_FRAME_POSTED, }; struct FrameDelivery { uint64_t sharedOwnerToken = 0; unsigned ownerQueueMask = 0; uint32_t sharedOwnerClientID = 0; bool sharedOwnerPending = false; bool sharedPending = false; }; struct OwnerDelivery { uint64_t token = 0; uint32_t clientID = 0; unsigned frameIndex = 0; bool active = false; }; CLGMPHost& m_host; CIVSHMEM& m_ivshmem; PLGMPHostQueue m_frameQueue = nullptr; PLGMPHostQueue m_frameOwnerQueue[LGMP_Q_FRAME_LEN] = {}; CFrameScheduler m_frameScheduler; size_t m_alignSize = 0; size_t m_frameMemoryOffset = 0; size_t m_maxFrameSize = 0; // LGMP publication precedes copy completion. Replay only completed frames; // the deferred index tracks the newest frame still owed to the owner. std::atomic m_submittedFrameIndex = -1; std::atomic m_readyFrameIndex = -1; LONG m_deferredOwnerFrameIndex = -1; std::atomic m_frameInFlight[LGMP_Q_FRAME_BUFFER_LEN] = {}; bool m_frameCompleted[LGMP_Q_FRAME_BUFFER_LEN] = {}; SRWLOCK m_framePublishLock = SRWLOCK_INIT; uint64_t m_framePublishSequence = 0; uint64_t m_frameLastPublishSequence[LGMP_Q_FRAME_BUFFER_LEN] = {}; FrameDelivery m_frameDelivery[LGMP_Q_FRAME_BUFFER_LEN] = {}; OwnerDelivery m_ownerDelivery[LGMP_Q_FRAME_LEN] = {}; uint32_t m_formatVer = 0; uint32_t m_frameSerial = 0; PLGMPMemory m_frameMemory[LGMP_Q_FRAME_BUFFER_LEN] = {}; KVMFRFrame * m_frame [LGMP_Q_FRAME_BUFFER_LEN] = {}; LGMPBuffer * m_frameBuffer[LGMP_Q_FRAME_BUFFER_LEN] = {}; unsigned m_width = 0; unsigned m_height = 0; unsigned m_frameWidth = 0; unsigned m_frameHeight = 0; unsigned m_pitch = 0; DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN; FrameType m_frameType = FRAME_TYPE_INVALID; // Previous HDR metadata used to detect changes for formatVer bumps. uint16_t m_lastHDRDisplayPrimary[3][2] = {}; uint16_t m_lastHDRWhitePoint[2] = {}; uint32_t m_lastHDRMaxDisplayLuminance = 0; uint32_t m_lastHDRMinDisplayLuminance = 0; uint32_t m_lastHDRMaxContentLightLevel = 0; uint32_t m_lastHDRMaxFrameAverageLightLevel = 0; uint32_t m_lastSDRWhiteLevel = 0; bool m_lastHDRActive = false; bool m_lastHDRMetadata = false; void ProcessFrameDeliveries(); bool FrameBufferReferenced(unsigned frameIndex) const; int FindAvailableFrameBuffer(bool allowReady) const; int FindNewestCompletedFrame(unsigned excludeFrameIndex) const; int FindAvailableOwnerQueue(unsigned preferredIndex) const; unsigned CountOwnerDeliveries(uint32_t clientID) const; bool HasMatchingOwnerDelivery(uint32_t clientID, unsigned frameIndex, uint64_t token) const; SharedFramePostResult PostSharedFrame(unsigned frameIndex, uint32_t excludeClientID, uint64_t now); bool PostSharedOwnerFrame(unsigned frameIndex, const CFrameScheduler::Schedule& schedule); CLGMPFrameTransport(CLGMPHost& host, CIVSHMEM& ivshmem); bool Initialize(); void SealMemoryLayout(); bool Setup(size_t alignSize); void DeInit(); FrameMemoryLimits GetMemoryLimits() const; SubscriberSnapshot SnapshotSubscribers() const; void FinalizeSubscribers( const SubscriberSnapshot& snapshot, uint64_t now); bool UpdateSchedule(uint32_t sourceClientID, 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) override; bool HasPublishedFrame() const override { return m_readyFrameIndex.load(std::memory_order_acquire) >= 0; } 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) override; bool PublishFrameBuffer(unsigned frameIndex, const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner) override; bool RepublishFrameBuffer( const CFrameScheduler::Schedule& schedule) override; bool TryFrameSubmitted(unsigned frameIndex, const CFrameScheduler::Schedule& schedule) override; void CommitFrameBuffer(unsigned frameIndex, const CFrameScheduler::Schedule& schedule, bool periodic, 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) override; void WriteFrameBuffer(unsigned frameIndex, void * src, size_t offset, 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 override; void FinalizeFrameBuffer(unsigned frameIndex) const override; void ObserveFrame(uint64_t now) override; void ForceFrame() override; bool GetPublishTarget(uint64_t now, uint64_t& target, CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish) override; void FrameMissed(const CFrameScheduler::Schedule& schedule, uint64_t now, bool periodic) override; void FrameSuperseded() override; HANDLE GetFrameScheduleEvent() const override { return m_frameScheduler.GetWakeEvent(); } void TryRecordFrameTiming(uint64_t duration) override; };