/** * 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 "Atomic.h" #include "CSRWLock.h" #include "transport/IFrameSink.h" #include "transport/IFrameTransport.h" #include "transport/ITransport.h" class CFrameHub final : public IFrameTransport, public IFrameEvents { private: struct Sink { struct ResourceLane { enum Phase { IDLE, FILLING, PENDING, CANCELING, COMPLETING, }; uint8_t * mem = nullptr; uint64_t heapOffset = 0; unsigned localSlot = 0; bool direct = false; bool valid = false; bool busy = false; FrameToken token = {}; CFrameScheduler::Schedule schedule = {}; uint64_t content = 0; uint64_t captureTime = 0; uint64_t postProcessTime = 0; uint64_t copyTime = 0; uint64_t readyTime = 0; uint64_t holdTime = 0; uint64_t filledAt = 0; uint64_t workStart = 0; unsigned pitch = 0; unsigned width = 0; unsigned height = 0; DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; FrameType frameType = FRAME_TYPE_INVALID; Phase phase = IDLE; FrameDone pendingResult = FrameDone::FAILED; uint64_t pendingReadyAt = 0; bool timingValid = false; bool resultPending = false; bool callActive = false; bool cancelRequested = false; }; CSRWLock callLock; CSRWLock laneLock; IFrameSink * target = nullptr; HANDLE drained = nullptr; std::atomic outstanding = 0; std::atomic_bool active = false; std::atomic backend = 0; std::atomic epoch = 0; bool reserved = false; bool primary = false; bool needsFullCopy = true; uint64_t lastContent = 0; uint64_t lastTerminalContent = 0; uint64_t blockedContent = 0; size_t blockedFrameSize = 0; bool blockedAllocation = false; unsigned pitch = 0; unsigned width = 0; unsigned height = 0; DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; FrameType frameType = FRAME_TYPE_INVALID; ResourceLane lanes[FRAME_SINK_BUFFERS] = {}; }; struct BatchTarget { Sink * sink = nullptr; BackendId backend = 0; uint32_t epoch = 0; unsigned localSlot = 0; unsigned resourceLane = 0; CFrameScheduler::Schedule schedule = {}; CFrameScheduler::Schedule deliverySchedule = {}; uint64_t content = 0; unsigned pitch = 0; unsigned width = 0; unsigned height = 0; DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN; FrameType frameType = FRAME_TYPE_INVALID; bool periodic = false; bool published = false; bool delivered = false; bool submitted = false; bool committed = false; bool completionPending = false; bool completionSucceeded = false; bool releasePending = false; uint64_t captureTime = 0; uint64_t postProcessTime = 0; uint64_t copyTime = 0; uint64_t readyTime = 0; uint64_t holdTime = 0; uint64_t filledAt = 0; uint64_t workStart = 0; bool timingValid = false; bool active = false; }; struct Batch { CSRWLock lock; uint64_t serial = 0; unsigned count = 0; bool active = false; BatchTarget targets[FRAME_MAX_SINKS] = {}; }; struct SinkRef { Sink * sink; unsigned index; bool primary; }; mutable CSRWLock m_listLock; Sink m_sinks[FRAME_MAX_SINKS]; Batch m_batches[FRAME_BATCHES]; std::atomic m_nextSerial = 0; std::atomic m_nextContent = 0; std::atomic m_newestContent = 0; HANDLE m_wakeEvent = nullptr; unsigned Snapshot(SinkRef refs[FRAME_MAX_SINKS]) const; bool BatchValid(const Batch& batch, const FrameBatchToken& token) const; void ReleaseTarget(Batch& batch, BatchTarget& target); bool DetachTarget(Batch& batch, BatchTarget& target, FrameToken& token); void FillLane(Sink& sink, unsigned laneIndex, const FrameToken& token); void CancelLane(Sink& sink, unsigned laneIndex, const FrameToken& token); void CompleteLane(Sink& sink, unsigned laneIndex, const FrameToken& token, FrameDone result, uint64_t readyAt); public: CFrameHub(); ~CFrameHub() override; CFrameHub(const CFrameHub&) = delete; CFrameHub& operator=(const CFrameHub&) = delete; bool Bind(BackendId backend, uint32_t epoch, bool primary, IFrameSink& sink); void Unbind(BackendId backend, uint32_t epoch); void OnFrameDone(const FrameToken& token, FrameDone result, uint64_t readyAt) override; size_t GetMaxFrameSize() const override; uint64_t NextContentSerial() override; void FrameProductReady(uint64_t contentSerial) override; bool NeedsFrame() const override; bool GetFramePlan( uint64_t now, bool productReady, FramePlan& plan) override; bool GetImmediatePrimaryFramePlan( uint64_t now, FramePlan& plan) override; void MissFramePlan(const FramePlan& plan, uint64_t now) override; bool PrepareFrameBatch(const FramePlan& plan, uint64_t contentSerial, unsigned pitch, size_t frameSize, const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat, const RECT * dirtyRects, unsigned nbDirtyRects, bool allowReadyReplacement, PreparedFrameBatch& batch) override; uint32_t PublishFrameBatch(const FrameBatchToken& token) override; void CommitFrameBatch(const FrameBatchToken& token) override; void AbortFrameBatch(const FrameBatchToken& token) override; void FailFrameBatch(const FrameBatchToken& token) override; void WriteFrameTarget(const FrameBatchToken& token, unsigned target, void * src, size_t offset, size_t len, bool setWritePos) const override; void WriteFrameTargetRows(const FrameBatchToken& token, unsigned target, void * src, size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const override; void FinalizeFrameTarget(const FrameBatchToken& token, unsigned target) const override; void SetFrameTargetTiming(const FrameBatchToken& token, unsigned target, uint64_t captureTime, uint64_t postProcessTime, uint64_t copyTime, uint64_t readyTime, uint64_t holdTime, uint64_t completedAt) override; void TryRecordFrameTiming(const FrameBatchToken& token, unsigned target, uint64_t duration) override; void CompleteFrameTarget(const FrameBatchToken& token, unsigned target, bool succeeded) override; void ObserveFrame(uint64_t now) override; void ForceFrame() override; void FrameSuperseded() override; HANDLE GetFrameScheduleEvent() const override { return m_wakeEvent; } };