diff --git a/idd/LGIdd/LGIdd.vcxproj b/idd/LGIdd/LGIdd.vcxproj index 7569d02e..b83e345b 100644 --- a/idd/LGIdd/LGIdd.vcxproj +++ b/idd/LGIdd/LGIdd.vcxproj @@ -61,6 +61,7 @@ + @@ -109,10 +110,12 @@ + + diff --git a/idd/LGIdd/LGIdd.vcxproj.filters b/idd/LGIdd/LGIdd.vcxproj.filters index ef899d4d..01466c65 100644 --- a/idd/LGIdd/LGIdd.vcxproj.filters +++ b/idd/LGIdd/LGIdd.vcxproj.filters @@ -157,6 +157,9 @@ Transport + + Transport + Transport @@ -169,6 +172,9 @@ Transport + + Transport + Transport @@ -300,6 +306,9 @@ Transport + + Transport + Transport diff --git a/idd/LGIdd/capture/CFrameBufferPool.cpp b/idd/LGIdd/capture/CFrameBufferPool.cpp index 12d48570..d63d62f7 100644 --- a/idd/LGIdd/capture/CFrameBufferPool.cpp +++ b/idd/LGIdd/capture/CFrameBufferPool.cpp @@ -39,11 +39,11 @@ void CFrameBufferPool::Reset() CFrameBufferResource * CFrameBufferPool::Get( const PreparedFrameBuffer& buffer, size_t minSize) { - if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1) + if (buffer.resourceSlot > ARRAYSIZE(m_buffers) - 1) return nullptr; - CFrameBufferResource * fbr = &m_buffers[buffer.frameIndex]; - if (!fbr->Init(m_dx12, buffer.frameIndex, buffer.mem, + CFrameBufferResource * fbr = &m_buffers[buffer.resourceSlot]; + if (!fbr->Init(m_dx12, buffer.token, buffer.mem, buffer.heapOffset, minSize, m_transport->GetMaxFrameSize())) return nullptr; diff --git a/idd/LGIdd/capture/CFrameBufferResource.cpp b/idd/LGIdd/capture/CFrameBufferResource.cpp index db6c6b79..912cd9fd 100644 --- a/idd/LGIdd/capture/CFrameBufferResource.cpp +++ b/idd/LGIdd/capture/CFrameBufferResource.cpp @@ -25,11 +25,9 @@ #include bool CFrameBufferResource::Init(CD3D12Device * dx12, - unsigned frameIndex, uint8_t * base, uint64_t heapOffset, size_t size, + const FrameToken& token, uint8_t * base, uint64_t heapOffset, size_t size, size_t maxFrameSize) { - m_frameIndex = frameIndex; - if (size > maxFrameSize) { DEBUG_ERROR("Frame size of %llu is too large for transport memory", @@ -59,6 +57,7 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, // Nothing to do if the resource already represents this allocation. if (m_base == base && m_size >= size) { + m_token = token; m_frameSize = size; return true; } @@ -137,6 +136,7 @@ bool CFrameBufferResource::Init(CD3D12Device * dx12, m_res->SetName(resName); + m_token = token; m_base = base; m_size = size; m_frameSize = size; @@ -151,6 +151,7 @@ void CFrameBufferResource::Reset() m_map = NULL; } + m_token = {}; m_base = nullptr; m_size = 0; m_frameSize = 0; diff --git a/idd/LGIdd/capture/CFrameBufferResource.h b/idd/LGIdd/capture/CFrameBufferResource.h index 5bb2880b..4106e7af 100644 --- a/idd/LGIdd/capture/CFrameBufferResource.h +++ b/idd/LGIdd/capture/CFrameBufferResource.h @@ -29,6 +29,7 @@ #include "capture/CFrameScheduler.h" #include "d3d/CInteropResource.h" +#include "transport/PreparedFrameBuffer.h" struct CD3D12Device; @@ -37,7 +38,7 @@ using namespace Microsoft::WRL; class CFrameBufferResource { private: - unsigned m_frameIndex = 0; + FrameToken m_token = {}; uint8_t * m_base = nullptr; size_t m_size = 0; size_t m_frameSize = 0; @@ -60,13 +61,13 @@ class CFrameBufferResource void * m_map = nullptr; public: - bool Init(CD3D12Device * dx12, unsigned frameIndex, uint8_t * base, + bool Init(CD3D12Device * dx12, const FrameToken& token, uint8_t * base, uint64_t heapOffset, size_t size, size_t maxFrameSize); void Reset(); - unsigned GetFrameIndex() { return m_frameIndex; } - size_t GetFrameSize() { return m_frameSize; } - void * GetMap() { return m_map; } + const FrameToken& GetToken() const { return m_token; } + size_t GetFrameSize() { return m_frameSize; } + void * GetMap() { return m_map; } void SetTiming(uint64_t captureTime, uint64_t postProcessStart, uint64_t copyStart) diff --git a/idd/LGIdd/capture/CHardwareFrameProcessor.cpp b/idd/LGIdd/capture/CHardwareFrameProcessor.cpp index d05def87..766bb2e6 100644 --- a/idd/LGIdd/capture/CHardwareFrameProcessor.cpp +++ b/idd/LGIdd/capture/CHardwareFrameProcessor.cpp @@ -346,7 +346,7 @@ void CHardwareFrameProcessor::CompletionFunction( if (!result) { - processor->m_transport->FailFrameBuffer(fbRes->GetFrameIndex()); + processor->m_transport->FailFrameBuffer(fbRes->GetToken()); processor->SetFullDamage(); processor->m_transport->ForceFrame(); processor->ReleaseCandidate(candidateIndex); @@ -379,7 +379,7 @@ void CHardwareFrameProcessor::CompletionFunction( { const uint64_t indirectCopyStart = CFrameScheduler::Nanotime(); processor->m_transport->WriteFrameBuffer( - fbRes->GetFrameIndex(), fbRes->GetMap(), 0, + fbRes->GetToken(), fbRes->GetMap(), 0, fbRes->GetFrameSize(), false); indirectCopyTime = CFrameScheduler::Nanotime() - indirectCopyStart; } @@ -405,7 +405,7 @@ void CHardwareFrameProcessor::CompletionFunction( const uint64_t copyTime = prepareCopyTime + publishCopyTime; - processor->m_transport->FinalizeFrameBuffer(fbRes->GetFrameIndex()); + processor->m_transport->FinalizeFrameBuffer(fbRes->GetToken()); const uint64_t publishedAt = CFrameScheduler::Nanotime(); const uint64_t prepareElapsed = prepareReady >= postProcessStart ? prepareReady - postProcessStart : 0; @@ -420,10 +420,11 @@ void CHardwareFrameProcessor::CompletionFunction( const uint64_t holdTime = publishStart >= prepareReady ? publishStart - prepareReady : 0; - processor->m_transport->SetFrameTiming(fbRes->GetFrameIndex(), + processor->m_transport->SetFrameTiming(fbRes->GetToken(), fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime, holdTime, fbRes->GetSchedule(), publishedAt); - processor->m_transport->TryRecordFrameTiming(publishedAt - publishStart); + processor->m_transport->TryRecordFrameTiming( + fbRes->GetToken(), publishedAt - publishStart); const uint64_t timingToken = fbRes->GetTimingToken(); if (timingToken && timingStart && prepareReady >= timingStart && @@ -436,7 +437,7 @@ void CHardwareFrameProcessor::CompletionFunction( fbRes->IsFullCopy(), totalTime); } - processor->m_transport->CompleteFrameBuffer(fbRes->GetFrameIndex(), true); + processor->m_transport->CompleteFrameBuffer(fbRes->GetToken(), true); processor->ReleaseCandidate(candidateIndex); } @@ -512,7 +513,7 @@ bool CHardwareFrameProcessor::Publish( m_frameBuffers.Get(buffer, candidate.frameSize); if (!fbRes) { - m_transport->AbortFrameBuffer(buffer.frameIndex); + m_transport->AbortFrameBuffer(buffer.token); restoreCandidate(); DEBUG_ERROR("Failed to get a CFrameBufferResource from the pool"); SetFullDamage(); @@ -522,7 +523,7 @@ bool CHardwareFrameProcessor::Publish( CD3D12CommandSlot * copySlot = m_dx12->GetCopySlot(candidateIndex); if (!copySlot) { - m_transport->AbortFrameBuffer(buffer.frameIndex); + m_transport->AbortFrameBuffer(buffer.token); restoreCandidate(); DEBUG_ERROR("Failed to get a copy CommandSlot for publication"); SetFullDamage(); @@ -557,16 +558,16 @@ bool CHardwareFrameProcessor::Publish( bool deliveredToOwner; if (!m_transport->PublishFrameBuffer( - buffer.frameIndex, schedule, deliveredToOwner)) + buffer.token, schedule, deliveredToOwner)) { copySlot->Cancel(); - m_transport->AbortFrameBuffer(buffer.frameIndex); + m_transport->AbortFrameBuffer(buffer.token); restoreCandidate(); return false; } CFrameScheduler::Schedule frameSchedule = schedule; if (!deliveredToOwner || - !m_transport->TryFrameSubmitted(buffer.frameIndex, schedule)) + !m_transport->TryFrameSubmitted(buffer.token, schedule)) frameSchedule.phaseEligible = false; fbRes->SetSchedule(frameSchedule); @@ -601,7 +602,7 @@ bool CHardwareFrameProcessor::Publish( } if (callbackPending && !copySlot->HasSubmittedWork()) { - m_transport->FailFrameBuffer(buffer.frameIndex); + m_transport->FailFrameBuffer(buffer.token); ReleaseCandidate(candidateIndex); } m_transport->ForceFrame(); @@ -610,7 +611,7 @@ bool CHardwareFrameProcessor::Publish( } m_transport->CommitFrameBuffer( - buffer.frameIndex, schedule, periodic, deliveredToOwner); + buffer.token, schedule, periodic, deliveredToOwner); unsigned superseded = 0; { diff --git a/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp b/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp index 3cbc3b1d..5bb683a4 100644 --- a/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp +++ b/idd/LGIdd/capture/CSoftwareFrameProcessor.cpp @@ -45,7 +45,7 @@ void CSoftwareFrameProcessor::CompletionFunction( if (!result) { - processor->m_transport->FailFrameBuffer(fbRes->GetFrameIndex()); + processor->m_transport->FailFrameBuffer(fbRes->GetToken()); processor->SetFullDamage(); processor->m_transport->ForceFrame(); return; @@ -56,7 +56,7 @@ void CSoftwareFrameProcessor::CompletionFunction( { const uint64_t indirectCopyStart = CFrameScheduler::Nanotime(); if (fbRes->IsFullCopy()) - processor->m_transport->WriteFrameBuffer(fbRes->GetFrameIndex(), + processor->m_transport->WriteFrameBuffer(fbRes->GetToken(), fbRes->GetMap(), 0, fbRes->GetFrameSize(), false); else { @@ -71,7 +71,7 @@ void CSoftwareFrameProcessor::CompletionFunction( (size_t)rect->left * bytesPerPixel; const size_t rowBytes = (size_t)(rect->right - rect->left) * bytesPerPixel; - processor->m_transport->WriteFrameBufferRows(fbRes->GetFrameIndex(), + processor->m_transport->WriteFrameBufferRows(fbRes->GetToken(), fbRes->GetMap(), rowOffset, rowBytes, pitch, (unsigned)(rect->bottom - rect->top)); } @@ -84,7 +84,7 @@ void CSoftwareFrameProcessor::CompletionFunction( const uint64_t copyReady = CFrameScheduler::Nanotime(); const bool gpuTimingValid = slot->GetGPUTimes(gpuStart, gpuEnd); - processor->m_transport->FinalizeFrameBuffer(fbRes->GetFrameIndex()); + processor->m_transport->FinalizeFrameBuffer(fbRes->GetToken()); const uint64_t publishedAt = CFrameScheduler::Nanotime(); const uint64_t postProcessStart = fbRes->GetPostProcessStart(); const uint64_t copyStart = fbRes->GetCopyStart(); @@ -104,10 +104,10 @@ void CSoftwareFrameProcessor::CompletionFunction( const uint64_t measured = postProcessTime + copyTime; const uint64_t readyTime = elapsed > measured ? elapsed - measured : 0; - processor->m_transport->SetFrameTiming(fbRes->GetFrameIndex(), + processor->m_transport->SetFrameTiming(fbRes->GetToken(), fbRes->GetCaptureTime(), postProcessTime, copyTime, readyTime, 0, fbRes->GetSchedule(), publishedAt); - processor->m_transport->CompleteFrameBuffer(fbRes->GetFrameIndex(), true); + processor->m_transport->CompleteFrameBuffer(fbRes->GetToken(), true); } bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) @@ -209,7 +209,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) if (!fbRes) { copySlot->Cancel(); - m_transport->AbortFrameBuffer(buffer.frameIndex); + m_transport->AbortFrameBuffer(buffer.token); RestorePendingDamage( currentDirtyRects, nbDirtyRects, hasDamage); DEBUG_ERROR("Failed to get a framebuffer for software capture"); @@ -221,7 +221,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) !submission.source->Sync(*copySlot)) { copySlot->Cancel(); - m_transport->AbortFrameBuffer(buffer.frameIndex); + m_transport->AbortFrameBuffer(buffer.token); RestorePendingDamage( currentDirtyRects, nbDirtyRects, hasDamage); SetFullDamage(); @@ -260,10 +260,10 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) bool deliveredToOwner; if (!m_transport->PublishFrameBuffer( - buffer.frameIndex, deliverySchedule, deliveredToOwner)) + buffer.token, deliverySchedule, deliveredToOwner)) { copySlot->Cancel(); - m_transport->AbortFrameBuffer(buffer.frameIndex); + m_transport->AbortFrameBuffer(buffer.token); RestorePendingDamage( currentDirtyRects, nbDirtyRects, hasDamage); if (!submission.noImageUpdate) @@ -283,7 +283,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) const bool submittedWork = copySlot->HasSubmittedWork(); const bool completionHandled = fbRes->CompletionHandled(); if (!submittedWork && !completionHandled) - m_transport->FailFrameBuffer(buffer.frameIndex); + m_transport->FailFrameBuffer(buffer.token); RestorePendingDamage( currentDirtyRects, nbDirtyRects, hasDamage); if (!submittedWork && !completionHandled) @@ -295,7 +295,7 @@ bool CSoftwareFrameProcessor::Submit(const FrameSubmission& submission) } m_transport->CommitFrameBuffer( - buffer.frameIndex, commitSchedule, false, deliveredToOwner); + buffer.token, commitSchedule, false, deliveredToOwner); return true; } } diff --git a/idd/LGIdd/display/CDeviceContext.h b/idd/LGIdd/display/CDeviceContext.h index 48ea40b5..d2c0a416 100644 --- a/idd/LGIdd/display/CDeviceContext.h +++ b/idd/LGIdd/display/CDeviceContext.h @@ -31,7 +31,7 @@ #include "display/CDisplayConfiguration.h" #include "display/CMonitorManager.h" -#include "transport/ITransport.h" +#include "transport/CTransportManager.h" class CDeviceContext : private ITransportEvents { @@ -47,7 +47,7 @@ private: bool m_transportOpened = false; std::atomic m_initInProgress = 0; - std::unique_ptr m_transport; + std::unique_ptr m_transport; CDisplayConfiguration m_displayConfiguration; CMonitorManager m_monitorManager; @@ -98,7 +98,7 @@ public: bool CanProcessFP16 () const { return m_canProcessFP16; } bool IsSoftwareMode () const { return m_softwareMode; } - ITransport& GetTransport() + CTransportManager& GetTransport() { return *m_transport; } diff --git a/idd/LGIdd/transport/CFrameHub.cpp b/idd/LGIdd/transport/CFrameHub.cpp new file mode 100644 index 00000000..201b39e0 --- /dev/null +++ b/idd/LGIdd/transport/CFrameHub.cpp @@ -0,0 +1,307 @@ +/** + * 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/CFrameHub.h" + +bool CFrameHub::Bind( + BackendId backend, uint32_t epoch, IFrameSink& sink) +{ + if (!backend || !epoch) + return false; + + CSRWExclusiveLock lock(m_lock); + if (m_sink) + return false; + m_sink = &sink; + m_backend = backend; + m_epoch = epoch; + m_slots.clear(); + return true; +} + +void CFrameHub::Unbind(BackendId backend, uint32_t epoch) +{ + CSRWExclusiveLock lock(m_lock); + if (m_backend != backend || m_epoch != epoch) + return; + m_sink = nullptr; + m_backend = 0; + m_epoch = 0; + m_slots.clear(); +} + +bool CFrameHub::Valid(const FrameToken& token) const +{ + return m_sink && token.sink == m_backend && token.epoch == m_epoch && + token.slot < m_slots.size() && token.serial && + m_slots[token.slot].serial == token.serial; +} + +void CFrameHub::Invalidate(const FrameToken& token) +{ + if (Valid(token)) + m_slots[token.slot] = {}; +} + +size_t CFrameHub::GetMaxFrameSize() const +{ + CSRWSharedLock lock(m_lock); + return m_sink ? m_sink->GetMaxFrameSize() : 0; +} + +bool CFrameHub::FrameBufferAvailable( + const CFrameScheduler::Schedule& schedule, bool allowReadyReplacement) +{ + CSRWSharedLock lock(m_lock); + return m_sink && + m_sink->FrameBufferAvailable(schedule, allowReadyReplacement); +} + +bool CFrameHub::HasPublishedFrame() const +{ + CSRWSharedLock lock(m_lock); + return m_sink && m_sink->HasPublishedFrame(); +} + +void CFrameHub::ProcessDeliveries() +{ + CSRWSharedLock lock(m_lock); + if (m_sink) + m_sink->ProcessDeliveries(); +} + +bool CFrameHub::GetPendingDeliveryTarget(uint64_t now, uint64_t& target) +{ + CSRWSharedLock lock(m_lock); + return m_sink && m_sink->GetPendingDeliveryTarget(now, target); +} + +bool CFrameHub::RetryPendingDelivery(uint64_t now, bool& retry) +{ + CSRWSharedLock lock(m_lock); + return m_sink && m_sink->RetryPendingDelivery(now, retry); +} + +PreparedFrameBuffer CFrameHub::PrepareFrameBuffer(unsigned pitch, + const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat, + const RECT * dirtyRects, unsigned nbDirtyRects, + const CFrameScheduler::Schedule& schedule, bool allowReadyReplacement) +{ + PreparedFrameBuffer result = {}; + CSRWExclusiveLock lock(m_lock); + if (!m_sink) + return result; + + const SinkTarget target = m_sink->PrepareFrameBuffer( + pitch, srcFormat, dstFormat, dirtyRects, nbDirtyRects, schedule, + allowReadyReplacement); + if (!target.mem || target.slot >= MAX_SLOTS) + { + if (target.mem) + m_sink->AbortFrameBuffer(target.slot); + return result; + } + + if (target.slot >= m_slots.size()) + m_slots.resize(target.slot + 1); + if (++m_nextSerial == 0) + ++m_nextSerial; + m_slots[target.slot].serial = m_nextSerial; + + result.token.sink = m_backend; + result.token.epoch = m_epoch; + result.token.slot = target.slot; + result.token.serial = m_nextSerial; + result.resourceSlot = target.slot; + result.mem = target.mem; + result.heapOffset = target.heapOffset; + result.fullCopy = target.fullCopy; + return result; +} + +bool CFrameHub::PublishFrameBuffer(const FrameToken& token, + const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner) +{ + CSRWSharedLock lock(m_lock); + return Valid(token) && + m_sink->PublishFrameBuffer(token.slot, schedule, deliveredToOwner); +} + +bool CFrameHub::RepublishFrameBuffer( + const CFrameScheduler::Schedule& schedule) +{ + CSRWSharedLock lock(m_lock); + return m_sink && m_sink->RepublishFrameBuffer(schedule); +} + +bool CFrameHub::TryFrameSubmitted(const FrameToken& token, + const CFrameScheduler::Schedule& schedule) +{ + CSRWSharedLock lock(m_lock); + return Valid(token) && + m_sink->TryFrameSubmitted(token.slot, schedule); +} + +void CFrameHub::CommitFrameBuffer(const FrameToken& token, + const CFrameScheduler::Schedule& schedule, bool periodic, + bool deliveredToOwner) +{ + CSRWExclusiveLock lock(m_lock); + if (Valid(token)) + { + m_sink->CommitFrameBuffer( + token.slot, schedule, periodic, deliveredToOwner); + Slot& slot = m_slots[token.slot]; + slot.committed = true; + if (slot.completionPending) + { + m_sink->CompleteFrameBuffer( + token.slot, slot.completionSucceeded); + Invalidate(token); + } + } +} + +void CFrameHub::AbortFrameBuffer(const FrameToken& token) +{ + CSRWExclusiveLock lock(m_lock); + if (Valid(token)) + { + m_sink->AbortFrameBuffer(token.slot); + Invalidate(token); + } +} + +void CFrameHub::FailFrameBuffer(const FrameToken& token) +{ + CSRWExclusiveLock lock(m_lock); + if (Valid(token)) + { + m_sink->FailFrameBuffer(token.slot); + Invalidate(token); + } +} + +void CFrameHub::CompleteFrameBuffer( + const FrameToken& token, bool succeeded) +{ + CSRWExclusiveLock lock(m_lock); + if (Valid(token)) + { + Slot& slot = m_slots[token.slot]; + if (slot.committed) + { + m_sink->CompleteFrameBuffer(token.slot, succeeded); + Invalidate(token); + } + else + { + slot.completionPending = true; + slot.completionSucceeded = succeeded; + } + } +} + +void CFrameHub::SetFrameTiming(const FrameToken& token, + uint64_t captureTime, uint64_t postProcessTime, uint64_t copyTime, + uint64_t readyTime, uint64_t holdTime, + const CFrameScheduler::Schedule& schedule, uint64_t completedAt) +{ + CSRWSharedLock lock(m_lock); + if (Valid(token)) + m_sink->SetFrameTiming(token.slot, captureTime, postProcessTime, + copyTime, readyTime, holdTime, schedule, completedAt); +} + +void CFrameHub::WriteFrameBuffer(const FrameToken& token, void * src, + size_t offset, size_t len, bool setWritePos) const +{ + CSRWSharedLock lock(m_lock); + if (Valid(token)) + m_sink->WriteFrameBuffer( + token.slot, src, offset, len, setWritePos); +} + +void CFrameHub::WriteFrameBufferRows(const FrameToken& token, void * src, + size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const +{ + CSRWSharedLock lock(m_lock); + if (Valid(token)) + m_sink->WriteFrameBufferRows( + token.slot, src, offset, rowBytes, pitch, rows); +} + +void CFrameHub::FinalizeFrameBuffer(const FrameToken& token) const +{ + CSRWSharedLock lock(m_lock); + if (Valid(token)) + m_sink->FinalizeFrameBuffer(token.slot); +} + +void CFrameHub::ObserveFrame(uint64_t now) +{ + CSRWSharedLock lock(m_lock); + if (m_sink) + m_sink->ObserveFrame(now); +} + +void CFrameHub::ForceFrame() +{ + CSRWSharedLock lock(m_lock); + if (m_sink) + m_sink->ForceFrame(); +} + +bool CFrameHub::GetPublishTarget(uint64_t now, uint64_t& target, + CFrameScheduler::Schedule& schedule, bool& periodic, bool& republish) +{ + CSRWSharedLock lock(m_lock); + return m_sink && m_sink->GetPublishTarget( + now, target, schedule, periodic, republish); +} + +void CFrameHub::FrameMissed(const CFrameScheduler::Schedule& schedule, + uint64_t now, bool periodic) +{ + CSRWSharedLock lock(m_lock); + if (m_sink) + m_sink->FrameMissed(schedule, now, periodic); +} + +void CFrameHub::FrameSuperseded() +{ + CSRWSharedLock lock(m_lock); + if (m_sink) + m_sink->FrameSuperseded(); +} + +HANDLE CFrameHub::GetFrameScheduleEvent() const +{ + CSRWSharedLock lock(m_lock); + return m_sink ? m_sink->GetFrameScheduleEvent() : nullptr; +} + +void CFrameHub::TryRecordFrameTiming( + const FrameToken& token, uint64_t duration) +{ + CSRWSharedLock lock(m_lock); + if (Valid(token)) + m_sink->TryRecordFrameTiming(duration); +} diff --git a/idd/LGIdd/transport/CFrameHub.h b/idd/LGIdd/transport/CFrameHub.h new file mode 100644 index 00000000..6ccfeece --- /dev/null +++ b/idd/LGIdd/transport/CFrameHub.h @@ -0,0 +1,105 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#pragma once + +#include "CSRWLock.h" +#include "transport/IFrameSink.h" +#include "transport/IFrameTransport.h" +#include "transport/ITransport.h" + +#include + +class CFrameHub final : public IFrameTransport +{ +private: + static const unsigned MAX_SLOTS = 1024; + + struct Slot + { + uint64_t serial = 0; + bool committed = false; + bool completionPending = false; + bool completionSucceeded = false; + }; + + mutable CSRWLock m_lock; + IFrameSink * m_sink = nullptr; + BackendId m_backend = 0; + uint32_t m_epoch = 0; + uint64_t m_nextSerial = 0; + std::vector m_slots; + + bool Valid(const FrameToken& token) const; + void Invalidate(const FrameToken& token); + +public: + bool Bind(BackendId backend, uint32_t epoch, IFrameSink& sink); + void Unbind(BackendId backend, uint32_t epoch); + + size_t GetMaxFrameSize() const override; + bool FrameBufferAvailable(const CFrameScheduler::Schedule& schedule, + bool allowReadyReplacement = true) override; + bool HasPublishedFrame() const override; + 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(const FrameToken& token, + const CFrameScheduler::Schedule& schedule, + bool& deliveredToOwner) override; + bool RepublishFrameBuffer( + const CFrameScheduler::Schedule& schedule) override; + bool TryFrameSubmitted(const FrameToken& token, + const CFrameScheduler::Schedule& schedule) override; + void CommitFrameBuffer(const FrameToken& token, + const CFrameScheduler::Schedule& schedule, bool periodic, + bool deliveredToOwner) override; + void AbortFrameBuffer(const FrameToken& token) override; + void FailFrameBuffer(const FrameToken& token) override; + void CompleteFrameBuffer( + const FrameToken& token, bool succeeded) override; + void SetFrameTiming(const FrameToken& token, 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(const FrameToken& token, void * src, + size_t offset, size_t len, bool setWritePos) const override; + void WriteFrameBufferRows(const FrameToken& token, void * src, + size_t offset, size_t rowBytes, size_t pitch, + unsigned rows) const override; + void FinalizeFrameBuffer(const FrameToken& token) 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; + void TryRecordFrameTiming( + const FrameToken& token, uint64_t duration) override; +}; diff --git a/idd/LGIdd/transport/CTransportManager.cpp b/idd/LGIdd/transport/CTransportManager.cpp index c845e8a3..284765b5 100644 --- a/idd/LGIdd/transport/CTransportManager.cpp +++ b/idd/LGIdd/transport/CTransportManager.cpp @@ -145,6 +145,16 @@ bool CTransportManager::InitializeEntry(Entry& entry) } entry.controlAdded = true; + if (entry.primary && + !m_frames.Bind(entry.id, entry.epoch, entry.transport->FrameSink())) + { + m_control.Remove(entry.id, entry.epoch); + entry.controlAdded = false; + entry.state = State::FAILED; + return false; + } + + entry.frameAdded = entry.primary; entry.state = State::INITIALIZED; return true; } @@ -311,6 +321,12 @@ void CTransportManager::Stop() void CTransportManager::RemoveServices(Entry& entry) { + if (entry.frameAdded) + { + m_frames.Unbind(entry.id, entry.epoch); + entry.frameAdded = false; + } + if (entry.controlAdded) { m_control.Remove(entry.id, entry.epoch); @@ -356,7 +372,8 @@ DirectFrameBufferMemory CTransportManager::GetDirectMemory() const IFrameTransport& CTransportManager::Frames() { - return Primary().Frames(); + m_exposed = true; + return m_frames; } IControlTransport& CTransportManager::Control() diff --git a/idd/LGIdd/transport/CTransportManager.h b/idd/LGIdd/transport/CTransportManager.h index 3ce3395e..b0ea3e5d 100644 --- a/idd/LGIdd/transport/CTransportManager.h +++ b/idd/LGIdd/transport/CTransportManager.h @@ -21,15 +21,19 @@ #pragma once #include "transport/CControlHub.h" +#include "transport/CFrameHub.h" #include "transport/ITransport.h" #include #include -class CTransportManager final : public ITransport +class CTransportManager final { public: using CreateFn = std::unique_ptr (*)(); + using OpenResult = ITransport::OpenResult; + using ProcessResult = ITransport::ProcessResult; + using Recovery = ITransport::Recovery; private: enum class State @@ -55,10 +59,12 @@ private: uint32_t epoch = 1; uint64_t retryAt = 0; bool controlAdded = false; + bool frameAdded = false; }; std::vector> m_entries; CControlHub m_control; + CFrameHub m_frames; Entry * m_primary = nullptr; bool m_initialized = false; bool m_setup = false; @@ -76,7 +82,7 @@ private: public: CTransportManager() = default; - ~CTransportManager() override; + ~CTransportManager(); CTransportManager(const CTransportManager&) = delete; CTransportManager& operator=(const CTransportManager&) = delete; @@ -84,19 +90,19 @@ public: bool Add(BackendId id, const char * name, bool required, bool primary, CreateFn create); - OpenResult Open() override; - bool Initialize() override; - bool Setup(size_t alignment) override; - ProcessResult Process(ITransportEvents& events) override; - void Stop() override; - void SyncRecovery() override; + OpenResult Open(); + bool Initialize(); + bool Setup(size_t alignment); + ProcessResult Process(ITransportEvents& events); + void Stop(); + void SyncRecovery(); void RecoveryStatus(uint64_t session, uint32_t serial, bool active, - Recovery state, uint32_t error) override; + Recovery state, uint32_t error); - FrameMemoryLimits GetMemoryLimits() const override; - DirectFrameBufferMemory GetDirectMemory() const override; + FrameMemoryLimits GetMemoryLimits() const; + DirectFrameBufferMemory GetDirectMemory() const; - IFrameTransport& Frames() override; - IControlTransport& Control() override; - IInputTransport * Input() override; + IFrameTransport& Frames(); + IControlTransport& Control(); + IInputTransport * Input(); }; diff --git a/idd/LGIdd/transport/IFrameSink.h b/idd/LGIdd/transport/IFrameSink.h new file mode 100644 index 00000000..4f53be75 --- /dev/null +++ b/idd/LGIdd/transport/IFrameSink.h @@ -0,0 +1,85 @@ +/** + * 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 "capture/CFrameScheduler.h" +#include "transport/PreparedFrameBuffer.h" + +#include +#include +#include + +class IFrameSink +{ +public: + virtual ~IFrameSink() = 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 SinkTarget 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 slot, + const CFrameScheduler::Schedule& schedule, + bool& deliveredToOwner) = 0; + virtual bool RepublishFrameBuffer( + const CFrameScheduler::Schedule& schedule) = 0; + virtual bool TryFrameSubmitted(unsigned slot, + const CFrameScheduler::Schedule& schedule) = 0; + virtual void CommitFrameBuffer(unsigned slot, + const CFrameScheduler::Schedule& schedule, bool periodic, + bool deliveredToOwner) = 0; + virtual void AbortFrameBuffer(unsigned slot) = 0; + virtual void FailFrameBuffer(unsigned slot) = 0; + virtual void CompleteFrameBuffer(unsigned slot, bool succeeded) = 0; + virtual void SetFrameTiming(unsigned slot, 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 slot, void * src, + size_t offset, size_t len, bool setWritePos) const = 0; + virtual void WriteFrameBufferRows(unsigned slot, void * src, + size_t offset, size_t rowBytes, size_t pitch, + unsigned rows) const = 0; + virtual void FinalizeFrameBuffer(unsigned slot) 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; +}; diff --git a/idd/LGIdd/transport/IFrameTransport.h b/idd/LGIdd/transport/IFrameTransport.h index dc016c7a..752a586e 100644 --- a/idd/LGIdd/transport/IFrameTransport.h +++ b/idd/LGIdd/transport/IFrameTransport.h @@ -48,30 +48,30 @@ public: const RECT * dirtyRects, unsigned nbDirtyRects, const CFrameScheduler::Schedule& schedule, bool allowReadyReplacement = true) = 0; - virtual bool PublishFrameBuffer(unsigned frameIndex, + virtual bool PublishFrameBuffer(const FrameToken& token, const CFrameScheduler::Schedule& schedule, bool& deliveredToOwner) = 0; virtual bool RepublishFrameBuffer( const CFrameScheduler::Schedule& schedule) = 0; - virtual bool TryFrameSubmitted(unsigned frameIndex, + virtual bool TryFrameSubmitted(const FrameToken& token, const CFrameScheduler::Schedule& schedule) = 0; - virtual void CommitFrameBuffer(unsigned frameIndex, + virtual void CommitFrameBuffer(const FrameToken& token, const CFrameScheduler::Schedule& schedule, bool periodic, bool deliveredToOwner) = 0; - virtual void AbortFrameBuffer(unsigned frameIndex) = 0; - virtual void FailFrameBuffer(unsigned frameIndex) = 0; + virtual void AbortFrameBuffer(const FrameToken& token) = 0; + virtual void FailFrameBuffer(const FrameToken& token) = 0; virtual void CompleteFrameBuffer( - unsigned frameIndex, bool succeeded) = 0; - virtual void SetFrameTiming(unsigned frameIndex, uint64_t captureTime, + const FrameToken& token, bool succeeded) = 0; + virtual void SetFrameTiming(const FrameToken& token, 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, + virtual void WriteFrameBuffer(const FrameToken& token, void * src, size_t offset, size_t len, bool setWritePos) const = 0; - virtual void WriteFrameBufferRows(unsigned frameIndex, void * src, + virtual void WriteFrameBufferRows(const FrameToken& token, void * src, size_t offset, size_t rowBytes, size_t pitch, unsigned rows) const = 0; - virtual void FinalizeFrameBuffer(unsigned frameIndex) const = 0; + virtual void FinalizeFrameBuffer(const FrameToken& token) const = 0; virtual void ObserveFrame(uint64_t now) = 0; virtual void ForceFrame() = 0; @@ -82,5 +82,6 @@ public: uint64_t now, bool periodic) = 0; virtual void FrameSuperseded() = 0; virtual HANDLE GetFrameScheduleEvent() const = 0; - virtual void TryRecordFrameTiming(uint64_t duration) = 0; + virtual void TryRecordFrameTiming( + const FrameToken& token, uint64_t duration) = 0; }; diff --git a/idd/LGIdd/transport/ITransport.h b/idd/LGIdd/transport/ITransport.h index 70b2485c..26d6167d 100644 --- a/idd/LGIdd/transport/ITransport.h +++ b/idd/LGIdd/transport/ITransport.h @@ -27,7 +27,7 @@ #include class IControlTransport; -class IFrameTransport; +class IFrameSink; class IInputTransport; using BackendId = uint32_t; @@ -93,7 +93,7 @@ public: virtual FrameMemoryLimits GetMemoryLimits() const = 0; virtual DirectFrameBufferMemory GetDirectMemory() const = 0; - virtual IFrameTransport& Frames() = 0; + virtual IFrameSink& FrameSink() = 0; virtual IControlTransport& Control() = 0; virtual IInputTransport * Input() { return nullptr; } }; diff --git a/idd/LGIdd/transport/PreparedFrameBuffer.h b/idd/LGIdd/transport/PreparedFrameBuffer.h index c8dcfd71..a1d96fb7 100644 --- a/idd/LGIdd/transport/PreparedFrameBuffer.h +++ b/idd/LGIdd/transport/PreparedFrameBuffer.h @@ -22,9 +22,26 @@ #include +struct FrameToken +{ + uint32_t sink = 0; + uint32_t epoch = 0; + uint32_t slot = 0; + uint64_t serial = 0; +}; + struct PreparedFrameBuffer { - unsigned frameIndex; + FrameToken token; + unsigned resourceSlot; + uint8_t * mem; + uint64_t heapOffset; + bool fullCopy; +}; + +struct SinkTarget +{ + unsigned slot; uint8_t * mem; uint64_t heapOffset; bool fullCopy; diff --git a/idd/LGIdd/transport/TransportFactory.cpp b/idd/LGIdd/transport/TransportFactory.cpp index b0e7b5d9..0dd5d73b 100644 --- a/idd/LGIdd/transport/TransportFactory.cpp +++ b/idd/LGIdd/transport/TransportFactory.cpp @@ -30,11 +30,11 @@ static std::unique_ptr CreateLGMP() return std::unique_ptr(new (std::nothrow) CLGMPTransport()); } -std::unique_ptr CreateTransport() +std::unique_ptr CreateTransport() { std::unique_ptr manager( new (std::nothrow) CTransportManager()); if (!manager || !manager->Add(1, "LGMP", true, true, CreateLGMP)) - return std::unique_ptr(); - return std::unique_ptr(manager.release()); + return std::unique_ptr(); + return manager; } diff --git a/idd/LGIdd/transport/TransportFactory.h b/idd/LGIdd/transport/TransportFactory.h index b11671e9..cfd705d3 100644 --- a/idd/LGIdd/transport/TransportFactory.h +++ b/idd/LGIdd/transport/TransportFactory.h @@ -20,8 +20,8 @@ #pragma once -#include "transport/ITransport.h" - #include -std::unique_ptr CreateTransport(); +class CTransportManager; + +std::unique_ptr CreateTransport(); diff --git a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp index 9cf1dbc6..19737bbb 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp +++ b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.cpp @@ -717,13 +717,13 @@ bool CLGMPFrameTransport::RetryPendingDelivery(uint64_t now, bool& retry) return result == SHARED_FRAME_POSTED; } -PreparedFrameBuffer CLGMPFrameTransport::PrepareFrameBuffer( +SinkTarget CLGMPFrameTransport::PrepareFrameBuffer( unsigned pitch, const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat, const RECT * dirtyRects, unsigned nbDirtyRects, const CFrameScheduler::Schedule& schedule, bool allowReadyReplacement) { - PreparedFrameBuffer result = {}; + SinkTarget result = {}; const unsigned dataWidth = dstFormat.dataWidth ? dstFormat.dataWidth : (unsigned)dstFormat.desc.Width; @@ -913,7 +913,7 @@ PreparedFrameBuffer CLGMPFrameTransport::PrepareFrameBuffer( LGMPBuffer * fb = m_frameBuffer[frameIndex]; fb->wp = 0; - result.frameIndex = frameIndex; + result.slot = frameIndex; result.mem = fb->data; result.heapOffset = reinterpret_cast(fb->data) - reinterpret_cast(m_ivshmem.GetMem()); diff --git a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h index d68f694b..2672cb1c 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h +++ b/idd/LGIdd/transport/lgmp/CLGMPFrameTransport.h @@ -34,14 +34,14 @@ extern "C" { #include "capture/CFrameScheduler.h" #include "capture/FramePipeline.h" #include "transport/FrameMemoryLimits.h" -#include "transport/IFrameTransport.h" +#include "transport/IFrameSink.h" class CIVSHMEM; class CLGMPHost; class CLGMPTransport; struct LGMPBuffer; -class CLGMPFrameTransport final : public IFrameTransport +class CLGMPFrameTransport final : public IFrameSink { private: friend class CLGMPTransport; @@ -172,7 +172,7 @@ public: bool GetPendingDeliveryTarget( uint64_t now, uint64_t& target) override; bool RetryPendingDelivery(uint64_t now, bool& retry) override; - PreparedFrameBuffer PrepareFrameBuffer(unsigned pitch, + SinkTarget PrepareFrameBuffer(unsigned pitch, const D12FrameFormat& srcFormat, const D12FrameFormat& dstFormat, const RECT * dirtyRects, unsigned nbDirtyRects, const CFrameScheduler::Schedule& schedule, diff --git a/idd/LGIdd/transport/lgmp/CLGMPTransport.h b/idd/LGIdd/transport/lgmp/CLGMPTransport.h index 36f3656e..a51b98b9 100644 --- a/idd/LGIdd/transport/lgmp/CLGMPTransport.h +++ b/idd/LGIdd/transport/lgmp/CLGMPTransport.h @@ -63,7 +63,7 @@ public: FrameMemoryLimits GetMemoryLimits() const override; DirectFrameBufferMemory GetDirectMemory() const override; - IFrameTransport& Frames() override { return m_frames; } + IFrameSink& FrameSink() override { return m_frames; } IControlTransport& Control() override { return m_control; } IInputTransport * Input() override { return &m_input; } };