[idd] capture: version frame graph changes

This commit is contained in:
Geoffrey McRae
2026-08-13 22:47:42 +10:00
parent 70bc66b3f1
commit 1dcab65ab9
7 changed files with 243 additions and 79 deletions

View File

@@ -33,6 +33,8 @@
namespace
{
static const uint64_t ROUTE_RETRY_DELAY_MS = 500;
template<typename T, std::size_t N>
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<ITransport> 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<CTexSet> 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<CTexSet>& 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<CTexSet>& 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<CTexSet>& 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);

View File

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

View File

@@ -30,8 +30,10 @@
#include <new>
#include <utility>
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<uint64_t> 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<ITransport> 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;

View File

@@ -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<const FrameCaps> 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();