[idd] transport: add frame sink boundary

This commit is contained in:
Geoffrey McRae
2026-08-12 16:45:01 +10:00
parent 801a1e58f8
commit bd4d31f0bd
21 changed files with 634 additions and 81 deletions

View File

@@ -61,6 +61,7 @@
<ClCompile Include="postprocess\effect\CHDR16to10Effect.cpp" />
<ClCompile Include="postprocess\effect\CRGB24Effect.cpp" />
<ClCompile Include="transport\CControlHub.cpp" />
<ClCompile Include="transport\CFrameHub.cpp" />
<ClCompile Include="transport\CTransportManager.cpp" />
<ClCompile Include="transport\TransportFactory.cpp" />
<ClCompile Include="transport\lgmp\CIVSHMEM.cpp" />
@@ -109,10 +110,12 @@
<ClInclude Include="postprocess\effect\CHDR16to10Effect.h" />
<ClInclude Include="postprocess\effect\CRGB24Effect.h" />
<ClInclude Include="transport\CControlHub.h" />
<ClInclude Include="transport\CFrameHub.h" />
<ClInclude Include="transport\CTransportManager.h" />
<ClInclude Include="transport\DirectFrameBufferMemory.h" />
<ClInclude Include="transport\FrameMemoryLimits.h" />
<ClInclude Include="transport\IControlTransport.h" />
<ClInclude Include="transport\IFrameSink.h" />
<ClInclude Include="transport\IFrameTransport.h" />
<ClInclude Include="transport\IInputTransport.h" />
<ClInclude Include="transport\ITransport.h" />

View File

@@ -157,6 +157,9 @@
<ClInclude Include="transport\CControlHub.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\CFrameHub.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\CTransportManager.h">
<Filter>Transport</Filter>
</ClInclude>
@@ -169,6 +172,9 @@
<ClInclude Include="transport\IControlTransport.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\IFrameSink.h">
<Filter>Transport</Filter>
</ClInclude>
<ClInclude Include="transport\IFrameTransport.h">
<Filter>Transport</Filter>
</ClInclude>
@@ -300,6 +306,9 @@
<ClCompile Include="transport\CControlHub.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\CFrameHub.cpp">
<Filter>Transport</Filter>
</ClCompile>
<ClCompile Include="transport\CTransportManager.cpp">
<Filter>Transport</Filter>
</ClCompile>

View File

@@ -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;

View File

@@ -25,11 +25,9 @@
#include <cstring>
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;

View File

@@ -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,11 +61,11 @@ 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; }
const FrameToken& GetToken() const { return m_token; }
size_t GetFrameSize() { return m_frameSize; }
void * GetMap() { return m_map; }

View File

@@ -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;
{

View File

@@ -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;
}
}

View File

@@ -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<LONG> m_initInProgress = 0;
std::unique_ptr<ITransport> m_transport;
std::unique_ptr<CTransportManager> 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;
}

View File

@@ -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);
}

View File

@@ -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 <vector>
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<Slot> 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;
};

View File

@@ -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()

View File

@@ -21,15 +21,19 @@
#pragma once
#include "transport/CControlHub.h"
#include "transport/CFrameHub.h"
#include "transport/ITransport.h"
#include <memory>
#include <vector>
class CTransportManager final : public ITransport
class CTransportManager final
{
public:
using CreateFn = std::unique_ptr<ITransport> (*)();
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<std::unique_ptr<Entry>> 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();
};

View File

@@ -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 <Windows.h>
#include <stddef.h>
#include <stdint.h>
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;
};

View File

@@ -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;
};

View File

@@ -27,7 +27,7 @@
#include <stdint.h>
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; }
};

View File

@@ -22,9 +22,26 @@
#include <stdint.h>
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;

View File

@@ -30,11 +30,11 @@ static std::unique_ptr<ITransport> CreateLGMP()
return std::unique_ptr<ITransport>(new (std::nothrow) CLGMPTransport());
}
std::unique_ptr<ITransport> CreateTransport()
std::unique_ptr<CTransportManager> CreateTransport()
{
std::unique_ptr<CTransportManager> manager(
new (std::nothrow) CTransportManager());
if (!manager || !manager->Add(1, "LGMP", true, true, CreateLGMP))
return std::unique_ptr<ITransport>();
return std::unique_ptr<ITransport>(manager.release());
return std::unique_ptr<CTransportManager>();
return manager;
}

View File

@@ -20,8 +20,8 @@
#pragma once
#include "transport/ITransport.h"
#include <memory>
std::unique_ptr<ITransport> CreateTransport();
class CTransportManager;
std::unique_ptr<CTransportManager> CreateTransport();

View File

@@ -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<uintptr_t>(fb->data) -
reinterpret_cast<uintptr_t>(m_ivshmem.GetMem());

View File

@@ -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,

View File

@@ -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; }
};