From 1dcab65ab918e0174c23246782179f3f75117f73 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Thu, 13 Aug 2026 22:47:42 +1000 Subject: [PATCH] [idd] capture: version frame graph changes --- idd/LGIdd/capture/CFrameExec.cpp | 30 +++++-- idd/LGIdd/capture/CFrameGraph.cpp | 58 +++++++++----- idd/LGIdd/capture/CFrameGraph.h | 21 +++-- idd/LGIdd/transport/CTexHub.cpp | 97 +++++++++++++++++++---- idd/LGIdd/transport/CTexHub.h | 12 ++- idd/LGIdd/transport/CTransportManager.cpp | 71 ++++++++++++++--- idd/LGIdd/transport/CTransportManager.h | 33 ++++---- 7 files changed, 243 insertions(+), 79 deletions(-) diff --git a/idd/LGIdd/capture/CFrameExec.cpp b/idd/LGIdd/capture/CFrameExec.cpp index 7ee77f3f..da6ecfcc 100644 --- a/idd/LGIdd/capture/CFrameExec.cpp +++ b/idd/LGIdd/capture/CFrameExec.cpp @@ -319,7 +319,8 @@ struct CFrameExec::Core : std::enable_shared_from_this CfgResult Init(const CFrameGraph& source, const std::shared_ptr& d11Device, - const std::shared_ptr& d12Device, CTexHub& texHub) + const std::shared_ptr& d12Device, CTexHub& texHub, + bool inheritedFull) { d11 = d11Device; d12 = d12Device; @@ -329,14 +330,15 @@ struct CFrameExec::Core : std::enable_shared_from_this if (!d11 || !d12 || !device || !graph.Generation()) return CfgResult::FAILED; - const GraphNode * nodes = graph.Nodes(nodeCount); + const GraphNode * nodes = graph.Nodes(nodeCount); const GraphLeaf * leaves = graph.Leaves(leafCount); if (!nodes || !leaves || !nodeCount || nodeCount > FRAME_GRAPH_MAX_NODES || leafCount > TRANSPORT_MAX_INSTANCES) return CfgResult::REJECTED; - unsigned texLeaves = 0; + unsigned texLeaves = 0; + bool continuous = true; for (unsigned leaf = 0; leaf < leafCount; ++leaf) if (leaves[leaf].tex) { @@ -347,11 +349,14 @@ struct CFrameExec::Core : std::enable_shared_from_this d11Node[leaves[leaf].node] |= leaves[leaf].cfg.profile.storage == FrameStorage::D3D11_TEXTURE; + continuous &= leaves[leaf].continuous; ++texLeaves; } if (!texLeaves) return CfgResult::ACCEPTED; + Atomic::Store(forceFull, inheritedFull || !continuous, + std::memory_order_relaxed); if (!nodes[0].texRefs || !queue.Init(device.Get(), D3D12_COMMAND_LIST_TYPE_DIRECT, L"Frame Graph", CD3D12CommandSlot::FAST, EXEC_LANES, false, true)) @@ -413,6 +418,12 @@ struct CFrameExec::Core : std::enable_shared_from_this return CfgResult::ACCEPTED; } + bool NeedsFull() + { + CSRWExclusiveLock lock(runLock); + return Atomic::Load(forceFull, std::memory_order_acquire); + } + FrameContentRef Content(uint64_t serial, const D12FrameFormat& format, uint64_t captureTime, bool full, FrameDamage damage, const RECT * rects, unsigned count) @@ -689,14 +700,16 @@ CfgResult CFrameExec::Prep(const CFrameGraph& graph) noexcept { std::shared_ptr d11; std::shared_ptr d12; - CTexHub * hub = nullptr; + std::shared_ptr active; + CTexHub * hub = nullptr; { CSRWSharedLock lock(m_lock); if (m_pending) return CfgResult::REJECTED; - d11 = m_d11; - d12 = m_d12; - hub = m_hub; + d11 = m_d11; + d12 = m_d12; + active = m_active; + hub = m_hub; } if (!d11 || !d12 || !hub) return CfgResult::FAILED; @@ -716,7 +729,8 @@ CfgResult CFrameExec::Prep(const CFrameGraph& graph) noexcept CfgResult result = CfgResult::FAILED; try { - result = next->Init(graph, d11, d12, *hub); + result = next->Init(graph, d11, d12, *hub, + !active || active->NeedsFull()); } catch (...) { diff --git a/idd/LGIdd/capture/CFrameGraph.cpp b/idd/LGIdd/capture/CFrameGraph.cpp index 0e06f0de..a98b66d5 100644 --- a/idd/LGIdd/capture/CFrameGraph.cpp +++ b/idd/LGIdd/capture/CFrameGraph.cpp @@ -241,7 +241,7 @@ unsigned CFrameGraph::Checkpoint(const FrameProfile& requested) } bool CFrameGraph::Add(BackendId id, uint32_t epoch, bool required, - bool primary, bool tex, const FrameCfg& cfg) + bool primary, bool tex, bool continuous, const FrameCfg& cfg) { if (!id || !epoch || !Can(cfg) || m_leafCount == TRANSPORT_MAX_INSTANCES) @@ -260,14 +260,15 @@ bool CFrameGraph::Add(BackendId id, uint32_t epoch, bool required, } GraphLeaf& leaf = m_leaves[m_leafCount++]; - leaf = GraphLeaf {}; - leaf.id = id; - leaf.epoch = epoch; - leaf.node = node; - leaf.required = required; - leaf.primary = primary; - leaf.tex = tex; - leaf.cfg = cfg; + leaf = GraphLeaf {}; + leaf.id = id; + leaf.epoch = epoch; + leaf.node = node; + leaf.required = required; + leaf.primary = primary; + leaf.tex = tex; + leaf.continuous = continuous; + leaf.cfg = cfg; for (unsigned current = node; current != FRAME_GRAPH_ROOT; current = m_nodes[current].parent) @@ -299,7 +300,8 @@ bool CFrameGraph::Seal() bool CFrameGraph::Stamp(uint64_t generation) { - if (!m_sealed || m_generation || !generation) + if (!m_sealed || m_generation || !generation || + !m_leafCount || !m_nodeCount) return false; m_generation = generation; return true; @@ -307,12 +309,29 @@ bool CFrameGraph::Stamp(uint64_t generation) bool CFrameGraph::Same(const GraphCfg& cfg) const { - return m_sealed && Frame::Same(m_cfg, cfg); + return Ready() && Frame::Same(m_cfg, cfg); +} + +const GraphLeaf * CFrameGraph::FindLeaf(BackendId id, uint32_t epoch, + bool tex, const FrameCfg& frame) const +{ + if (!Ready()) + return nullptr; + for (unsigned i = 0; i < m_leafCount; ++i) + { + const GraphLeaf& leaf = m_leaves[i]; + if (leaf.id == id && + leaf.epoch == epoch && + leaf.tex == tex && + Frame::Same(leaf.cfg, frame)) + return &leaf; + } + return nullptr; } bool CFrameGraph::Need(FrameOp op) const { - if (!m_sealed || op == FrameOp::SRC) + if (!Ready() || op == FrameOp::SRC) return false; for (unsigned i = 1; i < m_nodeCount; ++i) if (m_nodes[i].op == op && m_nodes[i].refs) @@ -322,7 +341,7 @@ bool CFrameGraph::Need(FrameOp op) const bool CFrameGraph::Want(FrameSignal signal) const { - if (!m_sealed) + if (!Ready()) return false; for (unsigned i = 0; i < m_leafCount; ++i) if (m_leaves[i].cfg.profile.signal == signal) @@ -332,7 +351,7 @@ bool CFrameGraph::Want(FrameSignal signal) const bool CFrameGraph::Shared(unsigned node) const { - if (!m_sealed || !node || node >= m_nodeCount || + if (!Ready() || !node || node >= m_nodeCount || !m_nodes[node].texRefs) return false; for (unsigned i = 0; i < m_leafCount; ++i) @@ -346,7 +365,7 @@ bool CFrameGraph::Shared(unsigned node) const bool CFrameGraph::Desc(unsigned nodeIndex, const FrameContentRef& content, FrameDesc& desc) const { - if (!content) + if (!Ready() || !content) return false; const D12FrameFormat& source = content->format; @@ -358,8 +377,7 @@ bool CFrameGraph::Desc(unsigned nodeIndex, if (!validProfile || !Frame::Same(sourceProfile, m_cfg.src)) return false; - if (!m_sealed || - !nodeIndex || + if (!nodeIndex || nodeIndex >= m_nodeCount || !content->serial || source.width != m_cfg.srcWidth || @@ -412,7 +430,7 @@ bool CFrameGraph::Desc(unsigned nodeIndex, bool CFrameGraph::Desc(unsigned leaf, const FrameContentRef& content, LeafDesc& desc) const { - if (!m_sealed || leaf >= m_leafCount) + if (!Ready() || leaf >= m_leafCount) return false; const GraphLeaf& route = m_leaves[leaf]; @@ -430,12 +448,12 @@ bool CFrameGraph::Desc(unsigned leaf, const FrameContentRef& content, const GraphNode * CFrameGraph::Nodes(unsigned& count) const { - count = m_sealed ? m_nodeCount : 0; + count = Ready() ? m_nodeCount : 0; return count ? m_nodes : nullptr; } const GraphLeaf * CFrameGraph::Leaves(unsigned& count) const { - count = m_sealed ? m_leafCount : 0; + count = Ready() ? m_leafCount : 0; return count ? m_leaves : nullptr; } diff --git a/idd/LGIdd/capture/CFrameGraph.h b/idd/LGIdd/capture/CFrameGraph.h index 7adb2567..7c5ce39e 100644 --- a/idd/LGIdd/capture/CFrameGraph.h +++ b/idd/LGIdd/capture/CFrameGraph.h @@ -85,12 +85,14 @@ struct GraphNode struct GraphLeaf { - BackendId id = 0; - uint32_t epoch = 0; - unsigned node = 0; - bool required = false; - bool primary = false; - bool tex = false; + BackendId id = 0; + uint32_t epoch = 0; + unsigned node = 0; + bool required = false; + bool primary = false; + bool tex = false; + // Partial damage remains valid across the preceding graph generation. + bool continuous = false; FrameCfg cfg; }; @@ -152,10 +154,13 @@ public: bool Begin(const GraphCfg& cfg); bool Can(const FrameCfg& cfg) const; bool Add(BackendId id, uint32_t epoch, bool required, bool primary, - bool tex, const FrameCfg& cfg); + bool tex, bool continuous, const FrameCfg& cfg); bool Seal(); bool Stamp(uint64_t generation); + bool Ready() const { return m_sealed && m_generation; } bool Same(const GraphCfg& cfg) const; + const GraphLeaf * FindLeaf(BackendId id, uint32_t epoch, bool tex, + const FrameCfg& frame) const; bool Want(FrameSignal signal) const; bool Need(FrameOp op) const; bool Shared(unsigned node) const; @@ -165,7 +170,7 @@ public: LeafDesc& desc) const; const GraphCfg& Cfg() const { return m_cfg; } - uint64_t Generation() const { return m_generation; } + uint64_t Generation() const { return Ready() ? m_generation : 0; } const GraphNode * Nodes(unsigned& count) const; const GraphLeaf * Leaves(unsigned& count) const; }; diff --git a/idd/LGIdd/transport/CTexHub.cpp b/idd/LGIdd/transport/CTexHub.cpp index 68c8de45..5f8e9944 100644 --- a/idd/LGIdd/transport/CTexHub.cpp +++ b/idd/LGIdd/transport/CTexHub.cpp @@ -33,6 +33,8 @@ namespace { + static const uint64_t ROUTE_RETRY_DELAY_MS = 500; + template T& Append(T (&items)[N], unsigned& count) { @@ -50,6 +52,13 @@ struct CTexSet { struct Route { + enum class State : uint8_t + { + ACTIVE, + FAULTED, + DROPPED, + }; + std::shared_ptr owner; ITexSink * sink = nullptr; BackendId id = 0; @@ -60,8 +69,7 @@ struct CTexSet FrameCfg cfg; bool required = false; bool primary = false; - bool active = false; - bool dropped = false; + State state = State::ACTIVE; CfgResult fault = CfgResult::ACCEPTED; unsigned calls = 0; HANDLE idle = nullptr; @@ -184,7 +192,8 @@ bool CTexHub::Enter(const FrameIn& frame, bool lease, CSRWExclusiveLock lock(set->lock); route = Find(*set, frame); - if (route >= set->count || !set->routes[route].active || !lease || + if (route >= set->count || + set->routes[route].state != CTexSet::Route::State::ACTIVE || !lease || frame.graph != set->generation) return false; @@ -228,6 +237,30 @@ CfgResult CTexHub::Faulted(BackendId id, uint32_t epoch, return CfgResult::ACCEPTED; } +bool CTexHub::Active(uint64_t generation, BackendId id, + uint32_t epoch, const FrameCfg& cfg) const +{ + std::shared_ptr set; + { + CSRWSharedLock lock(m_lock); + set = m_active; + } + if (!set || !generation || set->generation != generation) + return false; + + CSRWSharedLock lock(set->lock); + for (unsigned i = 0; i < set->count; ++i) + { + const CTexSet::Route& route = set->routes[i]; + if (route.id == id && + route.epoch == epoch && + route.state == CTexSet::Route::State::ACTIVE && + Frame::Same(route.cfg, cfg)) + return true; + } + return false; +} + void CTexHub::Rebind(BackendId id, uint32_t epoch) { if (!id || !epoch) @@ -243,6 +276,39 @@ void CTexHub::Rebind(BackendId id, uint32_t epoch) m_faults[i] = m_faults[--m_faultCount]; m_faults[m_faultCount] = FaultRec {}; } + for (unsigned i = 0; i < m_failureCount;) + { + if (m_failures[i].id != id || m_failures[i].epoch != epoch) + { + ++i; + continue; + } + m_failures[i] = m_failures[--m_failureCount]; + m_failures[m_failureCount] = Failure {}; + } +} + +void CTexHub::Retry(uint64_t now) +{ + bool wake = false; + { + CSRWExclusiveLock lock(m_lock); + for (unsigned i = 0; i < m_faultCount;) + { + const FaultRec& fault = m_faults[i]; + if (fault.result != CfgResult::REJECTED || !fault.retryAt || + now < fault.retryAt) + { + ++i; + continue; + } + m_faults[i] = m_faults[--m_faultCount]; + m_faults[m_faultCount] = FaultRec {}; + wake = true; + } + } + if (wake) + Bump(); } void CTexHub::Leave( @@ -267,13 +333,14 @@ void CTexHub::Disable(const std::shared_ptr& set, CTexSet::Route failed; { CSRWExclusiveLock lock(set->lock); - if (route < set->count && !set->routes[route].dropped && - (set->routes[route].active || + if (route < set->count && + set->routes[route].state != CTexSet::Route::State::DROPPED && + (set->routes[route].state == CTexSet::Route::State::ACTIVE || (result == PushResult::FAILED && set->routes[route].fault == CfgResult::REJECTED))) { CTexSet::Route& target = set->routes[route]; - target.active = false; + target.state = CTexSet::Route::State::FAULTED; target.fault = result == PushResult::FAILED ? CfgResult::FAILED : CfgResult::REJECTED; failed.id = target.id; @@ -286,11 +353,13 @@ void CTexHub::Disable(const std::shared_ptr& set, return; FaultRec record; - record.id = failed.id; - record.epoch = failed.epoch; - record.cfg = failed.cfg; - record.result = result == PushResult::FAILED ? + record.id = failed.id; + record.epoch = failed.epoch; + record.cfg = failed.cfg; + record.result = result == PushResult::FAILED ? CfgResult::FAILED : CfgResult::REJECTED; + record.retryAt = result == PushResult::REJECTED ? + GetTickCount64() + ROUTE_RETRY_DELAY_MS : 0; bool wake = false; { @@ -303,6 +372,7 @@ void CTexHub::Disable(const std::shared_ptr& set, m_faults[i].result != CfgResult::FAILED) { m_faults[i].result = CfgResult::FAILED; + m_faults[i].retryAt = 0; wake = true; } if (result != PushResult::FAILED) @@ -438,7 +508,7 @@ CfgResult CTexHub::Prep(const CFrameGraph& graph, target.cfg = route.cfg; target.required = route.required; target.primary = route.primary; - target.active = true; + target.state = CTexSet::Route::State::ACTIVE; target.idle = CreateEvent(nullptr, TRUE, TRUE, nullptr); if (!target.idle) return CfgResult::FAILED; @@ -616,9 +686,8 @@ void CTexHub::Drop(BackendId id, uint32_t epoch) if (set->routes[i].id == id && set->routes[i].epoch == epoch) { CTexSet::Route& target = set->routes[i]; - changed |= target.active; - target.active = false; - target.dropped = true; + changed |= target.state == CTexSet::Route::State::ACTIVE; + target.state = CTexSet::Route::State::DROPPED; if (target.calls) { ResetEvent(target.idle); diff --git a/idd/LGIdd/transport/CTexHub.h b/idd/LGIdd/transport/CTexHub.h index 9f2735f8..4542ce67 100644 --- a/idd/LGIdd/transport/CTexHub.h +++ b/idd/LGIdd/transport/CTexHub.h @@ -71,10 +71,11 @@ private: struct FaultRec { - BackendId id = 0; - uint32_t epoch = 0; - FrameCfg cfg; - CfgResult result = CfgResult::ACCEPTED; + BackendId id = 0; + uint32_t epoch = 0; + FrameCfg cfg; + CfgResult result = CfgResult::ACCEPTED; + uint64_t retryAt = 0; }; struct Failure @@ -125,6 +126,9 @@ private: CfgResult Faulted(BackendId id, uint32_t epoch, const FrameCfg& cfg) const; + bool Active(uint64_t generation, BackendId id, uint32_t epoch, + const FrameCfg& cfg) const; + void Retry(uint64_t now); bool TakeFailure(BackendId& id, uint32_t& epoch); void Rebind(BackendId id, uint32_t epoch); diff --git a/idd/LGIdd/transport/CTransportManager.cpp b/idd/LGIdd/transport/CTransportManager.cpp index 45df40fc..5b75063f 100644 --- a/idd/LGIdd/transport/CTransportManager.cpp +++ b/idd/LGIdd/transport/CTransportManager.cpp @@ -30,8 +30,10 @@ #include #include -static const uint64_t RETRY_DELAY_MS = 500; -static const uint64_t SERVICE_RETRY_DELAY_MS = 250; +static const uint64_t RETRY_DELAY_MS = 500; +static const uint64_t FRAME_RETRY_DELAY_MS = 500; +static const uint64_t SERVICE_RETRY_DELAY_MS = 250; +static std::atomic s_graphGeneration = 0; class CSourceEvents final : public ITransportEvents { @@ -135,9 +137,25 @@ void CTransportManager::BumpFrameRev() Atomic::Next(m_frameRev, std::memory_order_release); } -uint64_t CTransportManager::NextGraph() +void CTransportManager::ScheduleFrameRetry(Entry& entry) { - return Seq::Inc(m_graphSerial); + CSRWExclusiveLock entryLock(entry.lock); + if (!entry.frameRetryAt) + entry.frameRetryAt = GetTickCount64() + FRAME_RETRY_DELAY_MS; +} + +void CTransportManager::RetryFrame(Entry& entry, uint64_t now) +{ + bool retry = false; + { + CSRWExclusiveLock entryLock(entry.lock); + if (!entry.frameRetryAt || now < entry.frameRetryAt) + return; + entry.frameRetryAt = 0; + retry = entry.state == State::READY && entry.frameAdded; + } + if (retry) + BumpFrameRev(); } CTransportManager::~CTransportManager() @@ -771,6 +789,7 @@ void CTransportManager::RemoveServices(Entry& entry) entry.texSink = nullptr; entry.controlAdded = false; entry.inputAdded = false; + entry.frameRetryAt = 0; } m_input.RevokeInteraction(id, epoch); @@ -1098,13 +1117,14 @@ CfgResult CTransportManager::Cfg(const GraphCfg& cfg, struct Route { - Entry * entry = nullptr; + Entry * entry = nullptr; std::shared_ptr transport; BackendId id = 0; uint32_t epoch = 0; bool required = false; bool primary = false; bool prepared = false; + bool selected = false; bool eligible = false; ITexSink * texSink = nullptr; FrameProfile profiles[FRAME_PROFILE_MAX] = {}; @@ -1238,9 +1258,30 @@ CfgResult CTransportManager::Cfg(const GraphCfg& cfg, } } + const bool tex = route.texSink != nullptr; + const GraphLeaf * previous = graph.FindLeaf( + route.id, route.epoch, tex, candidate); + if (previous && previous->required == route.required && + previous->primary == route.primary && + (!tex || m_tex.Active(graph.Generation(), + route.id, route.epoch, candidate))) + { + if (!next.Add(route.id, route.epoch, route.required, + route.primary, tex, graph.Same(cfg), candidate)) + { + routeResult = CfgResult::FAILED; + break; + } + routeResult = CfgResult::ACCEPTED; + route.selected = true; + break; + } + routeResult = route.transport->Probe(candidate); if (routeResult == CfgResult::NEXT) continue; + if (routeResult == CfgResult::REJECTED) + ScheduleFrameRetry(*route.entry); if (routeResult != CfgResult::ACCEPTED) break; @@ -1250,21 +1291,24 @@ CfgResult CTransportManager::Cfg(const GraphCfg& cfg, route.transport->Abort(); continue; } + if (routeResult == CfgResult::REJECTED) + ScheduleFrameRetry(*route.entry); if (routeResult != CfgResult::ACCEPTED) break; if (!next.Add(route.id, route.epoch, route.required, - route.primary, route.texSink != nullptr, candidate)) + route.primary, tex, false, candidate)) { route.transport->Abort(); routeResult = CfgResult::FAILED; break; } route.prepared = true; + route.selected = true; break; } - if (route.prepared) + if (route.selected) continue; route.transport->Abort(); @@ -1285,14 +1329,14 @@ CfgResult CTransportManager::Cfg(const GraphCfg& cfg, result = CfgResult::FAILED; if (result == CfgResult::ACCEPTED && - !next.Stamp(NextGraph())) + !next.Stamp(Atomic::Next(s_graphGeneration))) result = CfgResult::FAILED; CTexHub::Bind binds[FRAME_MAX_SINKS]; unsigned bindCount = 0; if (result == CfgResult::ACCEPTED) for (unsigned i = 0; i < routeCount; ++i) - if (routes[i].prepared) + if (routes[i].selected) { CTexHub::Bind& bind = binds[bindCount++]; bind.owner = routes[i].transport; @@ -1315,8 +1359,15 @@ CfgResult CTransportManager::Cfg(const GraphCfg& cfg, if (result == CfgResult::ACCEPTED) { for (unsigned i = 0; i < routeCount; ++i) + { if (routes[i].prepared) routes[i].transport->Commit(); + if (routes[i].selected) + { + CSRWExclusiveLock entryLock(routes[i].entry->lock); + routes[i].entry->frameRetryAt = 0; + } + } graph = next; m_tex.Commit(texStage); if (activationReady) @@ -1360,12 +1411,14 @@ ITransport::ProcessResult CTransportManager::Process( const uint64_t now = GetTickCount64(); m_recovery.Tick(now); + m_tex.Retry(now); HandleServiceFailures(); Entry * entries[FRAME_MAX_SINKS] = {}; const unsigned count = Entries(entries); for (unsigned i = 0; i < count; ++i) { Entry& entry = *entries[i]; + RetryFrame(entry, now); if (!BeginCall(entry, Call::PROCESS, false)) continue; diff --git a/idd/LGIdd/transport/CTransportManager.h b/idd/LGIdd/transport/CTransportManager.h index 385bdee9..cead9fd4 100644 --- a/idd/LGIdd/transport/CTransportManager.h +++ b/idd/LGIdd/transport/CTransportManager.h @@ -103,20 +103,21 @@ private: State state = State::CLOSED; uint32_t epoch = 1; uint64_t retryAt = 0; - bool controlAdded = false; - bool controlFailed = false; - bool controlAbsent = false; - bool inputAdded = false; - bool inputFailed = false; - bool inputAbsent = false; - bool frameAdded = false; - bool frameLegacy = false; - ITexSink * texSink = nullptr; - bool frameAbsent = false; - uint64_t serviceRetryAt = 0; - bool exposed = false; - bool setupDone = false; - bool syncPending = false; + bool controlAdded = false; + bool controlFailed = false; + bool controlAbsent = false; + bool inputAdded = false; + bool inputFailed = false; + bool inputAbsent = false; + bool frameAdded = false; + bool frameLegacy = false; + ITexSink * texSink = nullptr; + bool frameAbsent = false; + uint64_t frameRetryAt = 0; + uint64_t serviceRetryAt = 0; + bool exposed = false; + bool setupDone = false; + bool syncPending = false; bool recoveryAttached = false; std::shared_ptr frameCaps; DirectFrameBufferMemory directMemory; @@ -143,7 +144,6 @@ private: bool m_started = false; bool m_stopping = false; bool m_stopped = false; - uint64_t m_graphSerial = 0; unsigned Entries(Entry * entries[FRAME_MAX_SINKS]) const; Entry * Primary() const; @@ -169,7 +169,8 @@ private: void RemoveServices(Entry& entry); void Expose(Entry& entry); void BumpFrameRev(); - uint64_t NextGraph(); + void ScheduleFrameRetry(Entry& entry); + void RetryFrame(Entry& entry, uint64_t now); public: CTransportManager();