mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[idd] transport: route immutable texture frames
This commit is contained in:
@@ -68,6 +68,7 @@
|
|||||||
<ClCompile Include="transport\CFrameHub.cpp" />
|
<ClCompile Include="transport\CFrameHub.cpp" />
|
||||||
<ClCompile Include="transport\CInputHub.cpp" />
|
<ClCompile Include="transport\CInputHub.cpp" />
|
||||||
<ClCompile Include="transport\CRecoveryHub.cpp" />
|
<ClCompile Include="transport\CRecoveryHub.cpp" />
|
||||||
|
<ClCompile Include="transport\CTexHub.cpp" />
|
||||||
<ClCompile Include="transport\CTransportManager.cpp" />
|
<ClCompile Include="transport\CTransportManager.cpp" />
|
||||||
<ClCompile Include="transport\TransportConfig.cpp" />
|
<ClCompile Include="transport\TransportConfig.cpp" />
|
||||||
<ClCompile Include="transport\TransportFactory.cpp" />
|
<ClCompile Include="transport\TransportFactory.cpp" />
|
||||||
@@ -124,10 +125,12 @@
|
|||||||
<ClInclude Include="transport\CFrameHub.h" />
|
<ClInclude Include="transport\CFrameHub.h" />
|
||||||
<ClInclude Include="transport\CInputHub.h" />
|
<ClInclude Include="transport\CInputHub.h" />
|
||||||
<ClInclude Include="transport\CRecoveryHub.h" />
|
<ClInclude Include="transport\CRecoveryHub.h" />
|
||||||
|
<ClInclude Include="transport\CTexHub.h" />
|
||||||
<ClInclude Include="transport\CTransportManager.h" />
|
<ClInclude Include="transport\CTransportManager.h" />
|
||||||
<ClInclude Include="transport\DirectFrameBufferMemory.h" />
|
<ClInclude Include="transport\DirectFrameBufferMemory.h" />
|
||||||
<ClInclude Include="transport\FrameCaps.h" />
|
<ClInclude Include="transport\FrameCaps.h" />
|
||||||
<ClInclude Include="transport\FrameProfile.h" />
|
<ClInclude Include="transport\FrameProfile.h" />
|
||||||
|
<ClInclude Include="transport\FrameIn.h" />
|
||||||
<ClInclude Include="transport\IControlSink.h" />
|
<ClInclude Include="transport\IControlSink.h" />
|
||||||
<ClInclude Include="transport\IControlTransport.h" />
|
<ClInclude Include="transport\IControlTransport.h" />
|
||||||
<ClInclude Include="transport\IFrameSink.h" />
|
<ClInclude Include="transport\IFrameSink.h" />
|
||||||
@@ -135,6 +138,8 @@
|
|||||||
<ClInclude Include="transport\IInputTransport.h" />
|
<ClInclude Include="transport\IInputTransport.h" />
|
||||||
<ClInclude Include="transport\IInputSource.h" />
|
<ClInclude Include="transport\IInputSource.h" />
|
||||||
<ClInclude Include="transport\ITransport.h" />
|
<ClInclude Include="transport\ITransport.h" />
|
||||||
|
<ClInclude Include="transport\ITexSink.h" />
|
||||||
|
<ClInclude Include="transport\ITexStage.h" />
|
||||||
<ClInclude Include="transport\TransportConfig.h" />
|
<ClInclude Include="transport\TransportConfig.h" />
|
||||||
<ClInclude Include="transport\PreparedFrameBuffer.h" />
|
<ClInclude Include="transport\PreparedFrameBuffer.h" />
|
||||||
<ClInclude Include="transport\TransportFactory.h" />
|
<ClInclude Include="transport\TransportFactory.h" />
|
||||||
|
|||||||
@@ -175,6 +175,9 @@
|
|||||||
<ClInclude Include="transport\CRecoveryHub.h">
|
<ClInclude Include="transport\CRecoveryHub.h">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="transport\CTexHub.h">
|
||||||
|
<Filter>Transport</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="transport\CTransportManager.h">
|
<ClInclude Include="transport\CTransportManager.h">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -187,6 +190,9 @@
|
|||||||
<ClInclude Include="transport\FrameProfile.h">
|
<ClInclude Include="transport\FrameProfile.h">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="transport\FrameIn.h">
|
||||||
|
<Filter>Transport</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="transport\IControlSink.h">
|
<ClInclude Include="transport\IControlSink.h">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -208,6 +214,12 @@
|
|||||||
<ClInclude Include="transport\ITransport.h">
|
<ClInclude Include="transport\ITransport.h">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="transport\ITexSink.h">
|
||||||
|
<Filter>Transport</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="transport\ITexStage.h">
|
||||||
|
<Filter>Transport</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="transport\TransportConfig.h">
|
<ClInclude Include="transport\TransportConfig.h">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -357,6 +369,9 @@
|
|||||||
<ClCompile Include="transport\CRecoveryHub.cpp">
|
<ClCompile Include="transport\CRecoveryHub.cpp">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="transport\CTexHub.cpp">
|
||||||
|
<Filter>Transport</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="transport\CTransportManager.cpp">
|
<ClCompile Include="transport\CTransportManager.cpp">
|
||||||
<Filter>Transport</Filter>
|
<Filter>Transport</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ bool Frame::Valid(FrameDamage damage, const RECT * rects, unsigned count,
|
|||||||
void CFrameGraph::Reset()
|
void CFrameGraph::Reset()
|
||||||
{
|
{
|
||||||
m_cfg = GraphCfg {};
|
m_cfg = GraphCfg {};
|
||||||
|
m_generation = 0;
|
||||||
m_nodeCount = 0;
|
m_nodeCount = 0;
|
||||||
m_leafCount = 0;
|
m_leafCount = 0;
|
||||||
m_begun = false;
|
m_begun = false;
|
||||||
@@ -288,6 +289,14 @@ bool CFrameGraph::Seal()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CFrameGraph::Stamp(uint64_t generation)
|
||||||
|
{
|
||||||
|
if (!m_sealed || m_generation || !generation)
|
||||||
|
return false;
|
||||||
|
m_generation = generation;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CFrameGraph::Same(const GraphCfg& cfg) const
|
bool CFrameGraph::Same(const GraphCfg& cfg) const
|
||||||
{
|
{
|
||||||
return m_sealed && Frame::Same(m_cfg, cfg);
|
return m_sealed && Frame::Same(m_cfg, cfg);
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ private:
|
|||||||
GraphCfg m_cfg;
|
GraphCfg m_cfg;
|
||||||
GraphNode m_nodes[FRAME_GRAPH_MAX_NODES] = {};
|
GraphNode m_nodes[FRAME_GRAPH_MAX_NODES] = {};
|
||||||
GraphLeaf m_leaves[TRANSPORT_MAX_INSTANCES] = {};
|
GraphLeaf m_leaves[TRANSPORT_MAX_INSTANCES] = {};
|
||||||
|
uint64_t m_generation = 0;
|
||||||
unsigned m_nodeCount = 0;
|
unsigned m_nodeCount = 0;
|
||||||
unsigned m_leafCount = 0;
|
unsigned m_leafCount = 0;
|
||||||
bool m_begun = false;
|
bool m_begun = false;
|
||||||
@@ -150,6 +151,7 @@ public:
|
|||||||
bool Add(BackendId id, uint32_t epoch, bool required, bool primary,
|
bool Add(BackendId id, uint32_t epoch, bool required, bool primary,
|
||||||
const FrameCfg& cfg);
|
const FrameCfg& cfg);
|
||||||
bool Seal();
|
bool Seal();
|
||||||
|
bool Stamp(uint64_t generation);
|
||||||
bool Same(const GraphCfg& cfg) const;
|
bool Same(const GraphCfg& cfg) const;
|
||||||
bool Want(FrameSignal signal) const;
|
bool Want(FrameSignal signal) const;
|
||||||
bool Need(FrameOp op) const;
|
bool Need(FrameOp op) const;
|
||||||
@@ -158,6 +160,7 @@ public:
|
|||||||
LeafDesc& desc) const;
|
LeafDesc& desc) const;
|
||||||
|
|
||||||
const GraphCfg& Cfg() const { return m_cfg; }
|
const GraphCfg& Cfg() const { return m_cfg; }
|
||||||
|
uint64_t Generation() const { return m_generation; }
|
||||||
const GraphNode * Nodes(unsigned& count) const;
|
const GraphNode * Nodes(unsigned& count) const;
|
||||||
const GraphLeaf * Leaves(unsigned& count) const;
|
const GraphLeaf * Leaves(unsigned& count) const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -48,6 +48,22 @@ static uint64_t NextPool()
|
|||||||
return Atomic::Next(next);
|
return Atomic::Next(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ValidTime(const FrameTime& time)
|
||||||
|
{
|
||||||
|
if (!time.postStart ||
|
||||||
|
(!time.gpuValid && (time.gpuStart || time.gpuEnd)) ||
|
||||||
|
(!time.readyValid && time.readyAt))
|
||||||
|
return false;
|
||||||
|
if (time.gpuValid && (!time.gpuStart ||
|
||||||
|
time.gpuStart < time.postStart ||
|
||||||
|
time.gpuEnd < time.gpuStart))
|
||||||
|
return false;
|
||||||
|
if (time.readyValid && (!time.readyAt ||
|
||||||
|
time.readyAt < (time.gpuValid ? time.gpuEnd : time.postStart)))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ValidFrame(const FrameProfile& profile,
|
static bool ValidFrame(const FrameProfile& profile,
|
||||||
const D3D12_RESOURCE_DESC& resource, const FrameDesc& frame)
|
const D3D12_RESOURCE_DESC& resource, const FrameDesc& frame)
|
||||||
{
|
{
|
||||||
@@ -279,7 +295,8 @@ struct CTexCore
|
|||||||
CFrameTex::CFrameTex(uint64_t graphValue, uint64_t poolValue,
|
CFrameTex::CFrameTex(uint64_t graphValue, uint64_t poolValue,
|
||||||
unsigned nodeValue, unsigned slotValue, uint64_t versionValue,
|
unsigned nodeValue, unsigned slotValue, uint64_t versionValue,
|
||||||
const FrameProfile& profileValue,
|
const FrameProfile& profileValue,
|
||||||
const FrameDesc& frameValue, const ComPtr<ID3D12Resource>& res,
|
const FrameDesc& frameValue, const FrameTime& timeValue,
|
||||||
|
const ComPtr<ID3D12Resource>& res,
|
||||||
const D12Sync& sync, D3D12_RESOURCE_STATES state, bool sharedValue) :
|
const D12Sync& sync, D3D12_RESOURCE_STATES state, bool sharedValue) :
|
||||||
m_res(res),
|
m_res(res),
|
||||||
m_sync(sync),
|
m_sync(sync),
|
||||||
@@ -291,7 +308,8 @@ CFrameTex::CFrameTex(uint64_t graphValue, uint64_t poolValue,
|
|||||||
slot(slotValue),
|
slot(slotValue),
|
||||||
version(versionValue),
|
version(versionValue),
|
||||||
profile(profileValue),
|
profile(profileValue),
|
||||||
frame(frameValue)
|
frame(frameValue),
|
||||||
|
time(timeValue)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,13 +377,19 @@ ID3D12Resource * TexWrite::Get() const
|
|||||||
resource.Get() : nullptr;
|
resource.Get() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TexWrite::Seal(const FrameDesc& frame, const D12Sync& sync,
|
bool TexWrite::Seal(const FrameDesc& frame, const FrameTime& time,
|
||||||
TexLease& lease)
|
const D12Sync& sync, TexLease& lease)
|
||||||
{
|
{
|
||||||
if (!m_core || !sync.Valid())
|
if (!m_core || !sync.Valid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const std::shared_ptr<CTexCore> core = m_core;
|
const std::shared_ptr<CTexCore> core = m_core;
|
||||||
|
if (!ValidTime(time))
|
||||||
|
{
|
||||||
|
core->Fail(m_index, m_version, sync);
|
||||||
|
Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
ComPtr<ID3D12Resource> resource;
|
ComPtr<ID3D12Resource> resource;
|
||||||
FrameDesc desc = frame;
|
FrameDesc desc = frame;
|
||||||
if (!core->Info(m_index, m_version, resource) ||
|
if (!core->Info(m_index, m_version, resource) ||
|
||||||
@@ -390,8 +414,8 @@ bool TexWrite::Seal(const FrameDesc& frame, const D12Sync& sync,
|
|||||||
}
|
}
|
||||||
|
|
||||||
CFrameTex * raw = new (std::nothrow) CFrameTex(core->graph, core->id,
|
CFrameTex * raw = new (std::nothrow) CFrameTex(core->graph, core->id,
|
||||||
core->node, m_index, m_version, core->profile, desc, resource, sync,
|
core->node, m_index, m_version, core->profile, desc, time, resource,
|
||||||
core->read, core->shared);
|
sync, core->read, core->shared);
|
||||||
if (!raw)
|
if (!raw)
|
||||||
{
|
{
|
||||||
core->Drop(index, generation);
|
core->Drop(index, generation);
|
||||||
|
|||||||
@@ -37,6 +37,18 @@ enum class TexResult : uint8_t
|
|||||||
FAILED,
|
FAILED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Immutable producer boundaries for one node product. A transport adds its
|
||||||
|
// own transfer or encode timing without rewriting these common values.
|
||||||
|
struct FrameTime
|
||||||
|
{
|
||||||
|
uint64_t postStart = 0;
|
||||||
|
uint64_t gpuStart = 0;
|
||||||
|
uint64_t gpuEnd = 0;
|
||||||
|
uint64_t readyAt = 0;
|
||||||
|
bool gpuValid = false;
|
||||||
|
bool readyValid = false;
|
||||||
|
};
|
||||||
|
|
||||||
// An immutable, pool-owned D3D12 graph product. Node zero is deliberately
|
// An immutable, pool-owned D3D12 graph product. Node zero is deliberately
|
||||||
// excluded because it aliases the acquired IddCx surface.
|
// excluded because it aliases the acquired IddCx surface.
|
||||||
class CFrameTex final
|
class CFrameTex final
|
||||||
@@ -51,7 +63,8 @@ private:
|
|||||||
|
|
||||||
CFrameTex(uint64_t graph, uint64_t pool, unsigned node, unsigned slot,
|
CFrameTex(uint64_t graph, uint64_t pool, unsigned node, unsigned slot,
|
||||||
uint64_t version, const FrameProfile& profile, const FrameDesc& frame,
|
uint64_t version, const FrameProfile& profile, const FrameDesc& frame,
|
||||||
const ComPtr<ID3D12Resource>& res, const D12Sync& sync,
|
const FrameTime& time, const ComPtr<ID3D12Resource>& res,
|
||||||
|
const D12Sync& sync,
|
||||||
D3D12_RESOURCE_STATES state, bool shared);
|
D3D12_RESOURCE_STATES state, bool shared);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -65,6 +78,7 @@ public:
|
|||||||
const uint64_t version;
|
const uint64_t version;
|
||||||
const FrameProfile profile;
|
const FrameProfile profile;
|
||||||
const FrameDesc frame;
|
const FrameDesc frame;
|
||||||
|
const FrameTime time;
|
||||||
|
|
||||||
ID3D12Resource * Get() const { return m_res.Get(); }
|
ID3D12Resource * Get() const { return m_res.Get(); }
|
||||||
const D12Sync& Sync() const { return m_sync; }
|
const D12Sync& Sync() const { return m_sync; }
|
||||||
@@ -126,7 +140,8 @@ public:
|
|||||||
ID3D12Resource * Get() const;
|
ID3D12Resource * Get() const;
|
||||||
// Seal only after producer commands restore the texture to the pool's
|
// Seal only after producer commands restore the texture to the pool's
|
||||||
// configured immutable state and Execute returns this exact sync point.
|
// configured immutable state and Execute returns this exact sync point.
|
||||||
bool Seal(const FrameDesc& frame, const D12Sync& sync, TexLease& lease);
|
bool Seal(const FrameDesc& frame, const FrameTime& time,
|
||||||
|
const D12Sync& sync, TexLease& lease);
|
||||||
void Cancel();
|
void Cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -533,17 +533,29 @@ void CSwapChainProcessor::QueueGraph(const D12FrameFormat& source,
|
|||||||
source, checkpoint, m_dx11Device->IsSoftware(),
|
source, checkpoint, m_dx11Device->IsSoftware(),
|
||||||
m_renderAdapter, cfg))
|
m_renderAdapter, cfg))
|
||||||
return;
|
return;
|
||||||
if (m_haveGraphCfg && Frame::Same(m_graphCfg, cfg))
|
CTransportManager& transport = m_devContext->GetTransport();
|
||||||
|
const uint64_t revision = transport.FrameRev();
|
||||||
|
if (m_haveGraphCfg && m_graphRev == revision &&
|
||||||
|
Frame::Same(m_graphCfg, cfg))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_graphCfg = cfg;
|
m_graphCfg = cfg;
|
||||||
m_haveGraphCfg = true;
|
m_haveGraphCfg = true;
|
||||||
m_graphPending = true;
|
m_graphPending = true;
|
||||||
|
m_graphRev = revision;
|
||||||
m_graphRetryAt = 0;
|
m_graphRetryAt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSwapChainProcessor::CfgGraph()
|
void CSwapChainProcessor::CfgGraph()
|
||||||
{
|
{
|
||||||
|
CTransportManager& transport = m_devContext->GetTransport();
|
||||||
|
const uint64_t revision = transport.FrameRev();
|
||||||
|
if (m_haveGraphCfg && revision != m_graphRev)
|
||||||
|
{
|
||||||
|
m_graphRev = revision;
|
||||||
|
m_graphPending = true;
|
||||||
|
m_graphRetryAt = 0;
|
||||||
|
}
|
||||||
if (!m_graphPending)
|
if (!m_graphPending)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -553,7 +565,7 @@ void CSwapChainProcessor::CfgGraph()
|
|||||||
|
|
||||||
m_graphPending = false;
|
m_graphPending = false;
|
||||||
const CfgResult result =
|
const CfgResult result =
|
||||||
m_devContext->GetTransport().Cfg(m_graphCfg, m_graph);
|
transport.Cfg(m_graphCfg, m_graphRev, m_graph);
|
||||||
if (result == CfgResult::ACCEPTED)
|
if (result == CfgResult::ACCEPTED)
|
||||||
{
|
{
|
||||||
m_graphRetryAt = 0;
|
m_graphRetryAt = 0;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ private:
|
|||||||
GraphCfg m_graphCfg;
|
GraphCfg m_graphCfg;
|
||||||
bool m_haveGraphCfg = false;
|
bool m_haveGraphCfg = false;
|
||||||
bool m_graphPending = false;
|
bool m_graphPending = false;
|
||||||
|
uint64_t m_graphRev = 0;
|
||||||
uint64_t m_graphRetryAt = 0;
|
uint64_t m_graphRetryAt = 0;
|
||||||
CPostProcessor m_postProcessors[CAPTURE_PIPELINE_SLOTS];
|
CPostProcessor m_postProcessors[CAPTURE_PIPELINE_SLOTS];
|
||||||
std::unique_ptr<CFrameProcessor> m_frameProcessor;
|
std::unique_ptr<CFrameProcessor> m_frameProcessor;
|
||||||
|
|||||||
644
idd/LGIdd/transport/CTexHub.cpp
Normal file
644
idd/LGIdd/transport/CTexHub.cpp
Normal file
@@ -0,0 +1,644 @@
|
|||||||
|
/**
|
||||||
|
* 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/CTexHub.h"
|
||||||
|
|
||||||
|
#include "capture/CFrameGraph.h"
|
||||||
|
#include "capture/CFrameTex.h"
|
||||||
|
#include "d3d/CInteropPool.h"
|
||||||
|
#include "transport/ITexSink.h"
|
||||||
|
#include "transport/ITransport.h"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <new>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template<typename T, std::size_t N>
|
||||||
|
T& Append(T (&items)[N], unsigned& count)
|
||||||
|
{
|
||||||
|
if (count == N)
|
||||||
|
{
|
||||||
|
for (unsigned i = 1; i < count; ++i)
|
||||||
|
items[i - 1] = items[i];
|
||||||
|
--count;
|
||||||
|
}
|
||||||
|
return items[count++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CTexSet
|
||||||
|
{
|
||||||
|
struct Route
|
||||||
|
{
|
||||||
|
std::shared_ptr<ITransport> owner;
|
||||||
|
ITexSink * sink = nullptr;
|
||||||
|
BackendId id = 0;
|
||||||
|
uint32_t epoch = 0;
|
||||||
|
unsigned node = 0;
|
||||||
|
unsigned leaf = 0;
|
||||||
|
FrameProfile profile;
|
||||||
|
FrameCfg cfg;
|
||||||
|
bool required = false;
|
||||||
|
bool primary = false;
|
||||||
|
bool active = false;
|
||||||
|
bool dropped = false;
|
||||||
|
CfgResult fault = CfgResult::ACCEPTED;
|
||||||
|
unsigned calls = 0;
|
||||||
|
HANDLE idle = nullptr;
|
||||||
|
|
||||||
|
~Route()
|
||||||
|
{
|
||||||
|
if (idle)
|
||||||
|
CloseHandle(idle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CSRWLock lock;
|
||||||
|
CFrameGraph graph;
|
||||||
|
uint64_t generation = 0;
|
||||||
|
unsigned count = 0;
|
||||||
|
Route routes[TRANSPORT_MAX_INSTANCES];
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool SameFrame(const FrameDesc& left, const FrameDesc& right,
|
||||||
|
bool ignoreFlags = false)
|
||||||
|
{
|
||||||
|
if (left.content != right.content ||
|
||||||
|
left.damage != right.damage ||
|
||||||
|
left.count != right.count ||
|
||||||
|
left.count > FRAME_DAMAGE_MAX ||
|
||||||
|
!D12::Same(left.format, right.format, ignoreFlags ?
|
||||||
|
D12::FormatCmp::NO_FLAGS : D12::FormatCmp::EXACT))
|
||||||
|
return false;
|
||||||
|
return !left.count || memcmp(left.rects, right.rects,
|
||||||
|
left.count * sizeof(*left.rects)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned Find(const CTexSet& set, const FrameIn& frame)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < set.count; ++i)
|
||||||
|
{
|
||||||
|
const CTexSet::Route& route = set.routes[i];
|
||||||
|
if (route.id == frame.desc.id &&
|
||||||
|
route.epoch == frame.desc.epoch &&
|
||||||
|
route.node == frame.desc.node &&
|
||||||
|
Frame::Same(route.profile, frame.desc.profile))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return TRANSPORT_MAX_INSTANCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Valid(const CTexSet& set, const CTexSet::Route& route,
|
||||||
|
const FrameIn& frame, const CFrameTex& tex, FrameStorage storage)
|
||||||
|
{
|
||||||
|
const FrameProfile product =
|
||||||
|
Frame::Store(route.profile, FrameStorage::D3D12_TEXTURE);
|
||||||
|
LeafDesc expected;
|
||||||
|
if (!set.graph.Desc(route.leaf, frame.desc.frame.content, expected) ||
|
||||||
|
expected.id != frame.desc.id ||
|
||||||
|
expected.epoch != frame.desc.epoch ||
|
||||||
|
expected.node != frame.desc.node ||
|
||||||
|
!Frame::Same(expected.profile, frame.desc.profile) ||
|
||||||
|
!SameFrame(expected.frame, frame.desc.frame) ||
|
||||||
|
!SameFrame(expected.frame, tex.frame, true))
|
||||||
|
return false;
|
||||||
|
return
|
||||||
|
frame.graph != 0 &&
|
||||||
|
frame.desc.node != 0 &&
|
||||||
|
frame.desc.frame.content &&
|
||||||
|
route.profile.storage == storage &&
|
||||||
|
tex.graph == frame.graph &&
|
||||||
|
tex.node == route.node &&
|
||||||
|
Frame::Same(tex.profile, product);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTexStage::CTexStage(CTexStage&& other) noexcept :
|
||||||
|
m_owner(other.m_owner), m_next(std::move(other.m_next))
|
||||||
|
{
|
||||||
|
other.m_owner = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTexStage& CTexStage::operator=(CTexStage&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
if (m_owner)
|
||||||
|
m_owner->Abort(*this);
|
||||||
|
m_owner = other.m_owner;
|
||||||
|
m_next = std::move(other.m_next);
|
||||||
|
other.m_owner = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTexStage::~CTexStage()
|
||||||
|
{
|
||||||
|
if (m_owner)
|
||||||
|
m_owner->Abort(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTexHub::CTexHub(std::atomic<uint64_t>& rev) : m_rev(rev)
|
||||||
|
{
|
||||||
|
m_idle = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTexHub::~CTexHub()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
if (m_idle)
|
||||||
|
CloseHandle(m_idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CTexHub::Enter(const FrameIn& frame, bool lease,
|
||||||
|
std::shared_ptr<CTexSet>& set, unsigned& route, ITexSink *& sink)
|
||||||
|
{
|
||||||
|
route = TRANSPORT_MAX_INSTANCES;
|
||||||
|
sink = nullptr;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (!m_open || m_stopped || !m_active)
|
||||||
|
return false;
|
||||||
|
set = m_active;
|
||||||
|
if (!m_calls++)
|
||||||
|
ResetEvent(m_idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSRWExclusiveLock lock(set->lock);
|
||||||
|
route = Find(*set, frame);
|
||||||
|
if (route >= set->count || !set->routes[route].active || !lease ||
|
||||||
|
frame.graph != set->generation)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CTexSet::Route& target = set->routes[route];
|
||||||
|
if (!target.calls++)
|
||||||
|
ResetEvent(target.idle);
|
||||||
|
sink = target.sink;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Finish(const std::shared_ptr<CTexSet>& set,
|
||||||
|
unsigned route, PushResult result)
|
||||||
|
{
|
||||||
|
if (result == PushResult::REJECTED || result == PushResult::FAILED)
|
||||||
|
Disable(set, route, result);
|
||||||
|
Leave(set, route);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Bump()
|
||||||
|
{
|
||||||
|
Atomic::Next(m_rev, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CTexHub::Match(const FaultRec& fault, BackendId id, uint32_t epoch,
|
||||||
|
const FrameCfg& cfg, bool anyCfg)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
fault.id == id &&
|
||||||
|
fault.epoch == epoch &&
|
||||||
|
(anyCfg || fault.result == CfgResult::FAILED ||
|
||||||
|
Frame::Same(fault.cfg, cfg));
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgResult CTexHub::Faulted(BackendId id, uint32_t epoch,
|
||||||
|
const FrameCfg& cfg) const
|
||||||
|
{
|
||||||
|
CSRWSharedLock lock(m_lock);
|
||||||
|
for (unsigned i = 0; i < m_faultCount; ++i)
|
||||||
|
if (Match(m_faults[i], id, epoch, cfg))
|
||||||
|
return m_faults[i].result;
|
||||||
|
return CfgResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Rebind(BackendId id, uint32_t epoch)
|
||||||
|
{
|
||||||
|
if (!id || !epoch)
|
||||||
|
return;
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
for (unsigned i = 0; i < m_faultCount;)
|
||||||
|
{
|
||||||
|
if (m_faults[i].id != id || m_faults[i].epoch != epoch)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_faults[i] = m_faults[--m_faultCount];
|
||||||
|
m_faults[m_faultCount] = FaultRec {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Leave(
|
||||||
|
const std::shared_ptr<CTexSet>& set, unsigned route)
|
||||||
|
{
|
||||||
|
if (set && route < set->count)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock setLock(set->lock);
|
||||||
|
CTexSet::Route& target = set->routes[route];
|
||||||
|
if (target.calls && !--target.calls && target.idle)
|
||||||
|
SetEvent(target.idle);
|
||||||
|
}
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (m_calls && !--m_calls)
|
||||||
|
SetEvent(m_idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Disable(const std::shared_ptr<CTexSet>& set,
|
||||||
|
unsigned route, PushResult result)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
CTexSet::Route failed;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(set->lock);
|
||||||
|
if (route < set->count && !set->routes[route].dropped &&
|
||||||
|
(set->routes[route].active ||
|
||||||
|
(result == PushResult::FAILED &&
|
||||||
|
set->routes[route].fault == CfgResult::REJECTED)))
|
||||||
|
{
|
||||||
|
CTexSet::Route& target = set->routes[route];
|
||||||
|
target.active = false;
|
||||||
|
target.fault = result == PushResult::FAILED ?
|
||||||
|
CfgResult::FAILED : CfgResult::REJECTED;
|
||||||
|
failed.id = target.id;
|
||||||
|
failed.epoch = target.epoch;
|
||||||
|
failed.cfg = target.cfg;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FaultRec record;
|
||||||
|
record.id = failed.id;
|
||||||
|
record.epoch = failed.epoch;
|
||||||
|
record.cfg = failed.cfg;
|
||||||
|
record.result = result == PushResult::FAILED ?
|
||||||
|
CfgResult::FAILED : CfgResult::REJECTED;
|
||||||
|
|
||||||
|
bool wake = false;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
for (unsigned i = 0; i < m_faultCount; ++i)
|
||||||
|
if (Match(m_faults[i], failed.id, failed.epoch, failed.cfg,
|
||||||
|
result == PushResult::FAILED))
|
||||||
|
{
|
||||||
|
if (result == PushResult::FAILED &&
|
||||||
|
m_faults[i].result != CfgResult::FAILED)
|
||||||
|
{
|
||||||
|
m_faults[i].result = CfgResult::FAILED;
|
||||||
|
wake = true;
|
||||||
|
}
|
||||||
|
if (result != PushResult::FAILED)
|
||||||
|
{
|
||||||
|
lock.Unlock();
|
||||||
|
if (wake)
|
||||||
|
Bump();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bool faulted = false;
|
||||||
|
for (unsigned i = 0; i < m_faultCount; ++i)
|
||||||
|
if (Match(m_faults[i], failed.id, failed.epoch, failed.cfg))
|
||||||
|
{
|
||||||
|
faulted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!faulted)
|
||||||
|
{
|
||||||
|
Append(m_faults, m_faultCount) = record;
|
||||||
|
wake = true;
|
||||||
|
}
|
||||||
|
if (result == PushResult::FAILED)
|
||||||
|
{
|
||||||
|
bool queued = false;
|
||||||
|
for (unsigned i = 0; i < m_failureCount; ++i)
|
||||||
|
if (m_failures[i].id == failed.id &&
|
||||||
|
m_failures[i].epoch == failed.epoch)
|
||||||
|
{
|
||||||
|
queued = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!queued)
|
||||||
|
{
|
||||||
|
Failure& source = Append(m_failures, m_failureCount);
|
||||||
|
source.id = failed.id;
|
||||||
|
source.epoch = failed.epoch;
|
||||||
|
}
|
||||||
|
wake = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wake)
|
||||||
|
Bump();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CTexHub::TakeFailure(BackendId& id, uint32_t& epoch)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (!m_failureCount)
|
||||||
|
return false;
|
||||||
|
id = m_failures[0].id;
|
||||||
|
epoch = m_failures[0].epoch;
|
||||||
|
for (unsigned i = 1; i < m_failureCount; ++i)
|
||||||
|
m_failures[i - 1] = m_failures[i];
|
||||||
|
m_failures[--m_failureCount] = Failure {};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgResult CTexHub::Hold(CTexStage& stage)
|
||||||
|
{
|
||||||
|
if (stage.m_owner)
|
||||||
|
return CfgResult::REJECTED;
|
||||||
|
if (!m_idle)
|
||||||
|
return CfgResult::FAILED;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (m_stopped || m_changing || m_pending)
|
||||||
|
return CfgResult::RETRY;
|
||||||
|
m_changing = true;
|
||||||
|
m_open = false;
|
||||||
|
stage.m_owner = this;
|
||||||
|
if (m_calls)
|
||||||
|
ResetEvent(m_idle);
|
||||||
|
else
|
||||||
|
SetEvent(m_idle);
|
||||||
|
}
|
||||||
|
return WaitForSingleObject(m_idle, INFINITE) == WAIT_OBJECT_0 ?
|
||||||
|
CfgResult::ACCEPTED : CfgResult::FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgResult CTexHub::Prep(const CFrameGraph& graph,
|
||||||
|
const Bind * binds, unsigned count, CTexStage& stage)
|
||||||
|
{
|
||||||
|
if (stage.m_owner != this || stage.m_next || !graph.Generation() ||
|
||||||
|
count > TRANSPORT_MAX_INSTANCES || (count && !binds))
|
||||||
|
return CfgResult::REJECTED;
|
||||||
|
|
||||||
|
unsigned leafCount = 0;
|
||||||
|
const GraphLeaf * leaves = graph.Leaves(leafCount);
|
||||||
|
if (!leaves || leafCount != count)
|
||||||
|
return CfgResult::REJECTED;
|
||||||
|
|
||||||
|
std::shared_ptr<CTexSet> next;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
next = std::make_shared<CTexSet>();
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc&)
|
||||||
|
{
|
||||||
|
return CfgResult::FAILED;
|
||||||
|
}
|
||||||
|
next->generation = graph.Generation();
|
||||||
|
next->graph = graph;
|
||||||
|
|
||||||
|
for (unsigned leaf = 0; leaf < leafCount; ++leaf)
|
||||||
|
{
|
||||||
|
const GraphLeaf& route = leaves[leaf];
|
||||||
|
const Bind * binding = nullptr;
|
||||||
|
for (unsigned bind = 0; bind < count; ++bind)
|
||||||
|
if (binds[bind].id == route.id && binds[bind].epoch == route.epoch)
|
||||||
|
{
|
||||||
|
if (binding)
|
||||||
|
return CfgResult::FAILED;
|
||||||
|
binding = &binds[bind];
|
||||||
|
}
|
||||||
|
if (!binding || !binding->owner)
|
||||||
|
return CfgResult::FAILED;
|
||||||
|
|
||||||
|
// Legacy transports deliberately have no texture sink yet. Their normal
|
||||||
|
// frame service remains active while this route table stays dormant.
|
||||||
|
if (!binding->sink)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CTexSet::Route& target = next->routes[next->count++];
|
||||||
|
target.owner = binding->owner;
|
||||||
|
target.sink = binding->sink;
|
||||||
|
target.id = route.id;
|
||||||
|
target.epoch = route.epoch;
|
||||||
|
target.node = route.node;
|
||||||
|
target.leaf = leaf;
|
||||||
|
target.profile = route.cfg.profile;
|
||||||
|
target.cfg = route.cfg;
|
||||||
|
target.required = route.required;
|
||||||
|
target.primary = route.primary;
|
||||||
|
target.active = true;
|
||||||
|
target.idle = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
||||||
|
if (!target.idle)
|
||||||
|
return CfgResult::FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (m_stopped || !m_changing || m_open || m_pending)
|
||||||
|
return CfgResult::RETRY;
|
||||||
|
m_pending = next;
|
||||||
|
stage.m_next = next;
|
||||||
|
}
|
||||||
|
return CfgResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Commit(CTexStage& stage) noexcept
|
||||||
|
{
|
||||||
|
std::shared_ptr<CTexSet> old;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (stage.m_owner != this || stage.m_next != m_pending)
|
||||||
|
return;
|
||||||
|
old = std::move(m_active);
|
||||||
|
m_active = std::move(m_pending);
|
||||||
|
m_open = !m_stopped;
|
||||||
|
m_changing = false;
|
||||||
|
stage.m_owner = nullptr;
|
||||||
|
stage.m_next.reset();
|
||||||
|
}
|
||||||
|
old.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Abort(CTexStage& stage) noexcept
|
||||||
|
{
|
||||||
|
std::shared_ptr<CTexSet> drop;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (stage.m_owner != this)
|
||||||
|
return;
|
||||||
|
if (stage.m_next == m_pending)
|
||||||
|
{
|
||||||
|
drop = std::move(m_pending);
|
||||||
|
m_open = !m_stopped;
|
||||||
|
m_changing = false;
|
||||||
|
}
|
||||||
|
stage.m_owner = nullptr;
|
||||||
|
stage.m_next.reset();
|
||||||
|
}
|
||||||
|
drop.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
PushResult CTexHub::Push(FrameIn frame, TexLease lease) noexcept
|
||||||
|
{
|
||||||
|
std::shared_ptr<CTexSet> set;
|
||||||
|
unsigned index;
|
||||||
|
ITexSink * sink = nullptr;
|
||||||
|
if (!Enter(frame, !!lease, set, index, sink))
|
||||||
|
{
|
||||||
|
if (set)
|
||||||
|
Leave(set, TRANSPORT_MAX_INSTANCES);
|
||||||
|
return PushResult::STALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const D12SyncState state = lease->Status();
|
||||||
|
bool valid;
|
||||||
|
{
|
||||||
|
CSRWSharedLock lock(set->lock);
|
||||||
|
valid = sink &&
|
||||||
|
Valid(*set, set->routes[index], frame, *lease.Get(),
|
||||||
|
FrameStorage::D3D12_TEXTURE) &&
|
||||||
|
state != D12SyncState::FAILED &&
|
||||||
|
(!lease->time.readyValid || state == D12SyncState::READY);
|
||||||
|
}
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
const PushResult result = state == D12SyncState::FAILED && sink ?
|
||||||
|
PushResult::FAILED : PushResult::STALE;
|
||||||
|
Finish(set, index, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PushResult result = sink->Push(std::move(frame), std::move(lease));
|
||||||
|
Finish(set, index, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushResult CTexHub::Push(FrameIn frame, D11Lease lease) noexcept
|
||||||
|
{
|
||||||
|
std::shared_ptr<CTexSet> set;
|
||||||
|
unsigned index;
|
||||||
|
ITexSink * sink = nullptr;
|
||||||
|
if (!Enter(frame, !!lease, set, index, sink))
|
||||||
|
{
|
||||||
|
if (set)
|
||||||
|
Leave(set, TRANSPORT_MAX_INSTANCES);
|
||||||
|
return PushResult::STALE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const D12SyncState state = lease.Status();
|
||||||
|
const CFrameTex * source = lease.Frame();
|
||||||
|
const D12SyncState sourceState = source ?
|
||||||
|
source->Status() : D12SyncState::FAILED;
|
||||||
|
bool valid;
|
||||||
|
{
|
||||||
|
CSRWSharedLock lock(set->lock);
|
||||||
|
const CTexSet::Route& route = set->routes[index];
|
||||||
|
valid = sink &&
|
||||||
|
source &&
|
||||||
|
Frame::Same(lease.Profile(), route.profile) &&
|
||||||
|
Valid(*set, route, frame, *source,
|
||||||
|
FrameStorage::D3D11_TEXTURE) &&
|
||||||
|
state != D12SyncState::FAILED &&
|
||||||
|
sourceState != D12SyncState::FAILED &&
|
||||||
|
(!source->time.readyValid ||
|
||||||
|
(state == D12SyncState::READY &&
|
||||||
|
sourceState == D12SyncState::READY));
|
||||||
|
}
|
||||||
|
if (!valid)
|
||||||
|
{
|
||||||
|
const PushResult result = sink &&
|
||||||
|
(state == D12SyncState::FAILED ||
|
||||||
|
sourceState == D12SyncState::FAILED) ?
|
||||||
|
PushResult::FAILED : PushResult::STALE;
|
||||||
|
Finish(set, index, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PushResult result = sink->Push(std::move(frame), std::move(lease));
|
||||||
|
Finish(set, index, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Drop(BackendId id, uint32_t epoch)
|
||||||
|
{
|
||||||
|
if (!id || !epoch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::shared_ptr<CTexSet> set;
|
||||||
|
{
|
||||||
|
CSRWSharedLock lock(m_lock);
|
||||||
|
if (m_stopped)
|
||||||
|
return;
|
||||||
|
set = m_active;
|
||||||
|
}
|
||||||
|
if (!set)
|
||||||
|
{
|
||||||
|
Rebind(id, epoch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE waits[TRANSPORT_MAX_INSTANCES] = {};
|
||||||
|
unsigned waitCount = 0;
|
||||||
|
bool changed = false;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(set->lock);
|
||||||
|
for (unsigned i = 0; i < set->count; ++i)
|
||||||
|
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;
|
||||||
|
if (target.calls)
|
||||||
|
{
|
||||||
|
ResetEvent(target.idle);
|
||||||
|
waits[waitCount++] = target.idle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < waitCount; ++i)
|
||||||
|
WaitForSingleObject(waits[i], INFINITE);
|
||||||
|
Rebind(id, epoch);
|
||||||
|
if (changed)
|
||||||
|
Bump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexHub::Stop()
|
||||||
|
{
|
||||||
|
std::shared_ptr<CTexSet> active;
|
||||||
|
std::shared_ptr<CTexSet> pending;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
if (m_stopped)
|
||||||
|
return;
|
||||||
|
m_stopped = true;
|
||||||
|
m_changing = true;
|
||||||
|
m_open = false;
|
||||||
|
if (m_calls)
|
||||||
|
ResetEvent(m_idle);
|
||||||
|
else if (m_idle)
|
||||||
|
SetEvent(m_idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_idle)
|
||||||
|
WaitForSingleObject(m_idle, INFINITE);
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock lock(m_lock);
|
||||||
|
active = std::move(m_active);
|
||||||
|
pending = std::move(m_pending);
|
||||||
|
}
|
||||||
|
pending.reset();
|
||||||
|
active.reset();
|
||||||
|
}
|
||||||
140
idd/LGIdd/transport/CTexHub.h
Normal file
140
idd/LGIdd/transport/CTexHub.h
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2026 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Atomic.h"
|
||||||
|
#include "CSRWLock.h"
|
||||||
|
#include "transport/FrameIn.h"
|
||||||
|
#include "transport/FrameProfile.h"
|
||||||
|
#include "transport/TransportConfig.h"
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class CFrameGraph;
|
||||||
|
class CTransportManager;
|
||||||
|
class D11Lease;
|
||||||
|
class ITransport;
|
||||||
|
class ITexSink;
|
||||||
|
class TexLease;
|
||||||
|
class CTexHub;
|
||||||
|
struct CTexSet;
|
||||||
|
|
||||||
|
class CTexStage
|
||||||
|
{
|
||||||
|
friend class CTexHub;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CTexHub * m_owner = nullptr;
|
||||||
|
std::shared_ptr<CTexSet> m_next;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CTexStage() = default;
|
||||||
|
CTexStage(const CTexStage&) = delete;
|
||||||
|
CTexStage& operator=(const CTexStage&) = delete;
|
||||||
|
CTexStage(CTexStage&& other) noexcept;
|
||||||
|
CTexStage& operator=(CTexStage&& other) noexcept;
|
||||||
|
~CTexStage();
|
||||||
|
};
|
||||||
|
|
||||||
|
class CTexHub
|
||||||
|
{
|
||||||
|
friend class CTransportManager;
|
||||||
|
friend class CTexStage;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Bind
|
||||||
|
{
|
||||||
|
std::shared_ptr<ITransport> owner;
|
||||||
|
BackendId id = 0;
|
||||||
|
uint32_t epoch = 0;
|
||||||
|
ITexSink * sink = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FaultRec
|
||||||
|
{
|
||||||
|
BackendId id = 0;
|
||||||
|
uint32_t epoch = 0;
|
||||||
|
FrameCfg cfg;
|
||||||
|
CfgResult result = CfgResult::ACCEPTED;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Failure
|
||||||
|
{
|
||||||
|
BackendId id = 0;
|
||||||
|
uint32_t epoch = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned MAX_FAULTS =
|
||||||
|
TRANSPORT_MAX_INSTANCES * FRAME_PROFILE_MAX;
|
||||||
|
|
||||||
|
mutable CSRWLock m_lock;
|
||||||
|
std::shared_ptr<CTexSet> m_active;
|
||||||
|
std::shared_ptr<CTexSet> m_pending;
|
||||||
|
std::atomic<uint64_t> & m_rev;
|
||||||
|
HANDLE m_idle = nullptr;
|
||||||
|
unsigned m_calls = 0;
|
||||||
|
bool m_open = true;
|
||||||
|
bool m_changing = false;
|
||||||
|
bool m_stopped = false;
|
||||||
|
FaultRec m_faults[MAX_FAULTS] = {};
|
||||||
|
unsigned m_faultCount = 0;
|
||||||
|
Failure m_failures[TRANSPORT_MAX_INSTANCES] = {};
|
||||||
|
unsigned m_failureCount = 0;
|
||||||
|
|
||||||
|
static bool Match(const FaultRec& fault, BackendId id, uint32_t epoch,
|
||||||
|
const FrameCfg& cfg, bool anyCfg = false);
|
||||||
|
|
||||||
|
bool Enter(const FrameIn& frame, bool lease,
|
||||||
|
std::shared_ptr<CTexSet>& set, unsigned& route, ITexSink *& sink);
|
||||||
|
void Finish(const std::shared_ptr<CTexSet>& set, unsigned route,
|
||||||
|
PushResult result);
|
||||||
|
void Leave(const std::shared_ptr<CTexSet>& set, unsigned route);
|
||||||
|
void Disable(const std::shared_ptr<CTexSet>& set, unsigned route,
|
||||||
|
PushResult result);
|
||||||
|
void Bump();
|
||||||
|
|
||||||
|
explicit CTexHub(std::atomic<uint64_t>& rev);
|
||||||
|
~CTexHub();
|
||||||
|
|
||||||
|
CTexHub(const CTexHub&) = delete;
|
||||||
|
CTexHub& operator=(const CTexHub&) = delete;
|
||||||
|
|
||||||
|
CfgResult Hold(CTexStage& stage);
|
||||||
|
CfgResult Prep(const CFrameGraph& graph,
|
||||||
|
const Bind * binds, unsigned count, CTexStage& stage);
|
||||||
|
void Commit(CTexStage& stage) noexcept;
|
||||||
|
void Abort(CTexStage& stage) noexcept;
|
||||||
|
|
||||||
|
CfgResult Faulted(BackendId id, uint32_t epoch,
|
||||||
|
const FrameCfg& cfg) const;
|
||||||
|
bool TakeFailure(BackendId& id, uint32_t& epoch);
|
||||||
|
void Rebind(BackendId id, uint32_t epoch);
|
||||||
|
|
||||||
|
// Drop closes only this route and drains its calls before the transport can
|
||||||
|
// stop. Accepted asynchronous leases remain the transport's responsibility.
|
||||||
|
void Drop(BackendId id, uint32_t epoch);
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
public:
|
||||||
|
PushResult Push(FrameIn frame, TexLease lease) noexcept;
|
||||||
|
PushResult Push(FrameIn frame, D11Lease lease) noexcept;
|
||||||
|
};
|
||||||
@@ -20,9 +20,11 @@
|
|||||||
|
|
||||||
#include "transport/CTransportManager.h"
|
#include "transport/CTransportManager.h"
|
||||||
|
|
||||||
|
#include "Atomic.h"
|
||||||
#include "capture/CFrameGraph.h"
|
#include "capture/CFrameGraph.h"
|
||||||
#include "CDebug.h"
|
#include "CDebug.h"
|
||||||
#include "Seq.h"
|
#include "Seq.h"
|
||||||
|
#include "transport/ITexStage.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <new>
|
#include <new>
|
||||||
@@ -117,12 +119,27 @@ CTransportManager::Entry::~Entry()
|
|||||||
CloseHandle(idleEvent);
|
CloseHandle(idleEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransportManager::CTransportManager()
|
CTransportManager::CTransportManager() : m_tex(m_frameRev)
|
||||||
{
|
{
|
||||||
m_phaseIdle = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
m_phaseIdle = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
||||||
m_stoppedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
m_stoppedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t CTransportManager::FrameRev() const
|
||||||
|
{
|
||||||
|
return Atomic::Load(m_frameRev, std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTransportManager::BumpFrameRev()
|
||||||
|
{
|
||||||
|
Atomic::Next(m_frameRev, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t CTransportManager::NextGraph()
|
||||||
|
{
|
||||||
|
return Seq::Inc(m_graphSerial);
|
||||||
|
}
|
||||||
|
|
||||||
CTransportManager::~CTransportManager()
|
CTransportManager::~CTransportManager()
|
||||||
{
|
{
|
||||||
Stop();
|
Stop();
|
||||||
@@ -469,6 +486,7 @@ bool CTransportManager::AddServices(Entry& entry)
|
|||||||
bool inputFailed = false;
|
bool inputFailed = false;
|
||||||
bool inputAbsent = false;
|
bool inputAbsent = false;
|
||||||
bool frameAdded = false;
|
bool frameAdded = false;
|
||||||
|
bool frameBound = false;
|
||||||
bool frameAbsent = false;
|
bool frameAbsent = false;
|
||||||
uint64_t retryAt = 0;
|
uint64_t retryAt = 0;
|
||||||
{
|
{
|
||||||
@@ -524,11 +542,24 @@ bool CTransportManager::AddServices(Entry& entry)
|
|||||||
else if (attach && !frameAdded && !frameAbsent)
|
else if (attach && !frameAdded && !frameAbsent)
|
||||||
{
|
{
|
||||||
IFrameSink * frame = transport->FrameSink();
|
IFrameSink * frame = transport->FrameSink();
|
||||||
|
ITexSink * tex = transport->TexSink();
|
||||||
if (frame && m_frames.Bind(id, epoch, primary, *frame))
|
if (frame && m_frames.Bind(id, epoch, primary, *frame))
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock entryLock(entry.lock);
|
CSRWExclusiveLock entryLock(entry.lock);
|
||||||
entry.frameAdded = true;
|
entry.frameAdded = true;
|
||||||
|
entry.frameLegacy = true;
|
||||||
|
entry.texSink = nullptr;
|
||||||
frameAdded = true;
|
frameAdded = true;
|
||||||
|
frameBound = true;
|
||||||
|
}
|
||||||
|
else if (!frame && tex)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock entryLock(entry.lock);
|
||||||
|
entry.frameAdded = true;
|
||||||
|
entry.frameLegacy = false;
|
||||||
|
entry.texSink = tex;
|
||||||
|
frameAdded = true;
|
||||||
|
frameBound = true;
|
||||||
}
|
}
|
||||||
else if (frame || primary)
|
else if (frame || primary)
|
||||||
frameRetry = true;
|
frameRetry = true;
|
||||||
@@ -567,6 +598,12 @@ bool CTransportManager::AddServices(Entry& entry)
|
|||||||
entry.serviceRetryAt = now + SERVICE_RETRY_DELAY_MS;
|
entry.serviceRetryAt = now + SERVICE_RETRY_DELAY_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frameBound)
|
||||||
|
{
|
||||||
|
m_tex.Rebind(id, epoch);
|
||||||
|
BumpFrameRev();
|
||||||
|
}
|
||||||
|
|
||||||
const bool servicesReady =
|
const bool servicesReady =
|
||||||
(!(services & TRANSPORT_SERVICE_FRAME) || frameAdded) &&
|
(!(services & TRANSPORT_SERVICE_FRAME) || frameAdded) &&
|
||||||
(!(services & TRANSPORT_SERVICE_CONTROL) || controlAdded) &&
|
(!(services & TRANSPORT_SERVICE_CONTROL) || controlAdded) &&
|
||||||
@@ -576,6 +613,37 @@ bool CTransportManager::AddServices(Entry& entry)
|
|||||||
|
|
||||||
void CTransportManager::HandleServiceFailures()
|
void CTransportManager::HandleServiceFailures()
|
||||||
{
|
{
|
||||||
|
BackendId frameId = 0;
|
||||||
|
uint32_t frameEpoch = 0;
|
||||||
|
while (m_tex.TakeFailure(frameId, frameEpoch))
|
||||||
|
{
|
||||||
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
||||||
|
const unsigned count = Entries(entries);
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
Entry& entry = *entries[i];
|
||||||
|
bool restart = false;
|
||||||
|
{
|
||||||
|
CSRWSharedLock entryLock(entry.lock);
|
||||||
|
if (entry.id != frameId || entry.epoch != frameEpoch ||
|
||||||
|
!entry.frameAdded || !entry.texSink)
|
||||||
|
continue;
|
||||||
|
restart = !entry.exposed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DetachRecovery(entry);
|
||||||
|
RemoveServices(entry);
|
||||||
|
if (restart)
|
||||||
|
ScheduleRetry(entry);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock entryLock(entry.lock);
|
||||||
|
entry.state = State::FAILED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControlToken token;
|
ControlToken token;
|
||||||
while (m_control.TakeFailure(token))
|
while (m_control.TakeFailure(token))
|
||||||
{
|
{
|
||||||
@@ -687,6 +755,7 @@ void CTransportManager::RemoveServices(Entry& entry)
|
|||||||
BackendId id = 0;
|
BackendId id = 0;
|
||||||
uint32_t epoch = 0;
|
uint32_t epoch = 0;
|
||||||
bool frameAdded = false;
|
bool frameAdded = false;
|
||||||
|
bool frameLegacy = false;
|
||||||
bool controlAdded = false;
|
bool controlAdded = false;
|
||||||
bool inputAdded = false;
|
bool inputAdded = false;
|
||||||
{
|
{
|
||||||
@@ -694,20 +763,28 @@ void CTransportManager::RemoveServices(Entry& entry)
|
|||||||
id = entry.id;
|
id = entry.id;
|
||||||
epoch = entry.epoch;
|
epoch = entry.epoch;
|
||||||
frameAdded = entry.frameAdded;
|
frameAdded = entry.frameAdded;
|
||||||
|
frameLegacy = entry.frameLegacy;
|
||||||
controlAdded = entry.controlAdded;
|
controlAdded = entry.controlAdded;
|
||||||
inputAdded = entry.inputAdded;
|
inputAdded = entry.inputAdded;
|
||||||
entry.frameAdded = false;
|
entry.frameAdded = false;
|
||||||
|
entry.frameLegacy = false;
|
||||||
|
entry.texSink = nullptr;
|
||||||
entry.controlAdded = false;
|
entry.controlAdded = false;
|
||||||
entry.inputAdded = false;
|
entry.inputAdded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_input.RevokeInteraction(id, epoch);
|
m_input.RevokeInteraction(id, epoch);
|
||||||
|
m_tex.Drop(id, epoch);
|
||||||
if (inputAdded)
|
if (inputAdded)
|
||||||
m_input.Unbind(id, epoch);
|
m_input.Unbind(id, epoch);
|
||||||
if (controlAdded)
|
if (controlAdded)
|
||||||
m_control.Remove(id, epoch);
|
m_control.Remove(id, epoch);
|
||||||
if (frameAdded)
|
if (frameLegacy)
|
||||||
m_frames.Unbind(id, epoch);
|
m_frames.Unbind(id, epoch);
|
||||||
|
if (frameAdded)
|
||||||
|
{
|
||||||
|
BumpFrameRev();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTransportManager::RetryEntry(Entry& entry, uint64_t now,
|
void CTransportManager::RetryEntry(Entry& entry, uint64_t now,
|
||||||
@@ -729,6 +806,8 @@ void CTransportManager::RetryEntry(Entry& entry, uint64_t now,
|
|||||||
|
|
||||||
{
|
{
|
||||||
CSRWExclusiveLock entryLock(entry.lock);
|
CSRWExclusiveLock entryLock(entry.lock);
|
||||||
|
const bool frameService =
|
||||||
|
(entry.config.services & TRANSPORT_SERVICE_FRAME) != 0;
|
||||||
entry.transport.reset();
|
entry.transport.reset();
|
||||||
entry.directMemory = DirectFrameBufferMemory {};
|
entry.directMemory = DirectFrameBufferMemory {};
|
||||||
entry.directMemoryValid = false;
|
entry.directMemoryValid = false;
|
||||||
@@ -740,6 +819,8 @@ void CTransportManager::RetryEntry(Entry& entry, uint64_t now,
|
|||||||
entry.frameAbsent = false;
|
entry.frameAbsent = false;
|
||||||
entry.serviceRetryAt = 0;
|
entry.serviceRetryAt = 0;
|
||||||
Seq::Inc(entry.epoch);
|
Seq::Inc(entry.epoch);
|
||||||
|
if (frameService)
|
||||||
|
BumpFrameRev();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OpenEntry(entry) != OpenResult::SUCCESS)
|
if (OpenEntry(entry) != OpenResult::SUCCESS)
|
||||||
@@ -994,15 +1075,27 @@ bool CTransportManager::Setup(size_t alignment)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
CfgResult CTransportManager::Cfg(
|
CfgResult CTransportManager::Cfg(const GraphCfg& cfg,
|
||||||
const GraphCfg& cfg, CFrameGraph& graph)
|
uint64_t revision, CFrameGraph& graph, ITexStage * activation)
|
||||||
{
|
{
|
||||||
|
if (!revision || revision != FrameRev())
|
||||||
|
return CfgResult::RETRY;
|
||||||
|
|
||||||
CFrameGraph next;
|
CFrameGraph next;
|
||||||
if (!next.Begin(cfg))
|
if (!next.Begin(cfg))
|
||||||
return CfgResult::REJECTED;
|
return CfgResult::REJECTED;
|
||||||
if (!BeginPhase(Phase::CFG, true))
|
if (!BeginPhase(Phase::CFG, true))
|
||||||
return CfgResult::RETRY;
|
return CfgResult::RETRY;
|
||||||
|
|
||||||
|
CTexStage texStage;
|
||||||
|
CfgResult held = m_tex.Hold(texStage);
|
||||||
|
if (held != CfgResult::ACCEPTED)
|
||||||
|
{
|
||||||
|
m_tex.Abort(texStage);
|
||||||
|
EndPhase();
|
||||||
|
return held;
|
||||||
|
}
|
||||||
|
|
||||||
struct Route
|
struct Route
|
||||||
{
|
{
|
||||||
Entry * entry = nullptr;
|
Entry * entry = nullptr;
|
||||||
@@ -1013,13 +1106,15 @@ CfgResult CTransportManager::Cfg(
|
|||||||
bool primary = false;
|
bool primary = false;
|
||||||
bool prepared = false;
|
bool prepared = false;
|
||||||
bool eligible = false;
|
bool eligible = false;
|
||||||
|
ITexSink * texSink = nullptr;
|
||||||
FrameProfile profiles[FRAME_PROFILE_MAX] = {};
|
FrameProfile profiles[FRAME_PROFILE_MAX] = {};
|
||||||
unsigned profileCount = 0;
|
unsigned profileCount = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Route routes[FRAME_MAX_SINKS];
|
Route routes[FRAME_MAX_SINKS];
|
||||||
unsigned routeCount = 0;
|
unsigned routeCount = 0;
|
||||||
CfgResult result = CfgResult::ACCEPTED;
|
CfgResult result = revision == FrameRev() ?
|
||||||
|
CfgResult::ACCEPTED : CfgResult::RETRY;
|
||||||
|
|
||||||
Entry * entries[FRAME_MAX_SINKS] = {};
|
Entry * entries[FRAME_MAX_SINKS] = {};
|
||||||
const unsigned count = Entries(entries);
|
const unsigned count = Entries(entries);
|
||||||
@@ -1051,6 +1146,7 @@ CfgResult CTransportManager::Cfg(
|
|||||||
route.entry = &entry;
|
route.entry = &entry;
|
||||||
State state;
|
State state;
|
||||||
bool frameAbsent = false;
|
bool frameAbsent = false;
|
||||||
|
bool frameAdded = false;
|
||||||
{
|
{
|
||||||
CSRWSharedLock entryLock(entry.lock);
|
CSRWSharedLock entryLock(entry.lock);
|
||||||
route.transport = entry.transport;
|
route.transport = entry.transport;
|
||||||
@@ -1058,12 +1154,14 @@ CfgResult CTransportManager::Cfg(
|
|||||||
route.epoch = entry.epoch;
|
route.epoch = entry.epoch;
|
||||||
route.required = entry.required;
|
route.required = entry.required;
|
||||||
route.primary = entry.primary;
|
route.primary = entry.primary;
|
||||||
|
route.texSink = entry.texSink;
|
||||||
state = entry.state;
|
state = entry.state;
|
||||||
frameAbsent = entry.frameAbsent;
|
frameAbsent = entry.frameAbsent;
|
||||||
|
frameAdded = entry.frameAdded;
|
||||||
}
|
}
|
||||||
|
|
||||||
route.eligible = route.transport && !frameAbsent &&
|
route.eligible = route.transport && !frameAbsent &&
|
||||||
(state == State::INITIALIZED || state == State::READY);
|
frameAdded && state == State::READY;
|
||||||
if (!route.eligible)
|
if (!route.eligible)
|
||||||
{
|
{
|
||||||
if (!route.required &&
|
if (!route.required &&
|
||||||
@@ -1073,6 +1171,16 @@ CfgResult CTransportManager::Cfg(
|
|||||||
(state == State::FAILED ? CfgResult::FAILED : CfgResult::RETRY);
|
(state == State::FAILED ? CfgResult::FAILED : CfgResult::RETRY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (route.texSink && !activation)
|
||||||
|
{
|
||||||
|
route.eligible = false;
|
||||||
|
if (route.required)
|
||||||
|
{
|
||||||
|
result = CfgResult::REJECTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned profileCount = 0;
|
unsigned profileCount = 0;
|
||||||
const FrameProfile * profiles =
|
const FrameProfile * profiles =
|
||||||
@@ -1114,6 +1222,22 @@ CfgResult CTransportManager::Cfg(
|
|||||||
if (!next.Can(candidate))
|
if (!next.Can(candidate))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (route.texSink)
|
||||||
|
{
|
||||||
|
const CfgResult fault =
|
||||||
|
m_tex.Faulted(route.id, route.epoch, candidate);
|
||||||
|
if (fault == CfgResult::FAILED)
|
||||||
|
{
|
||||||
|
routeResult = fault;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (fault == CfgResult::REJECTED)
|
||||||
|
{
|
||||||
|
routeResult = fault;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
routeResult = route.transport->Probe(candidate);
|
routeResult = route.transport->Probe(candidate);
|
||||||
if (routeResult == CfgResult::NEXT)
|
if (routeResult == CfgResult::NEXT)
|
||||||
continue;
|
continue;
|
||||||
@@ -1160,17 +1284,53 @@ CfgResult CTransportManager::Cfg(
|
|||||||
if (result == CfgResult::ACCEPTED && !next.Seal())
|
if (result == CfgResult::ACCEPTED && !next.Seal())
|
||||||
result = CfgResult::FAILED;
|
result = CfgResult::FAILED;
|
||||||
|
|
||||||
|
if (result == CfgResult::ACCEPTED &&
|
||||||
|
!next.Stamp(NextGraph()))
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
CTexHub::Bind& bind = binds[bindCount++];
|
||||||
|
bind.owner = routes[i].transport;
|
||||||
|
bind.id = routes[i].id;
|
||||||
|
bind.epoch = routes[i].epoch;
|
||||||
|
bind.sink = routes[i].texSink;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool activationReady = false;
|
||||||
|
if (result == CfgResult::ACCEPTED && activation)
|
||||||
|
{
|
||||||
|
result = activation->Prep(next);
|
||||||
|
activationReady = result == CfgResult::ACCEPTED;
|
||||||
|
}
|
||||||
|
if (result == CfgResult::ACCEPTED)
|
||||||
|
result = m_tex.Prep(next, binds, bindCount, texStage);
|
||||||
|
if (result == CfgResult::ACCEPTED && revision != FrameRev())
|
||||||
|
result = CfgResult::RETRY;
|
||||||
|
|
||||||
if (result == CfgResult::ACCEPTED)
|
if (result == CfgResult::ACCEPTED)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < routeCount; ++i)
|
for (unsigned i = 0; i < routeCount; ++i)
|
||||||
if (routes[i].prepared)
|
if (routes[i].prepared)
|
||||||
routes[i].transport->Commit();
|
routes[i].transport->Commit();
|
||||||
graph = next;
|
graph = next;
|
||||||
|
m_tex.Commit(texStage);
|
||||||
|
if (activationReady)
|
||||||
|
activation->Commit();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
for (unsigned i = routeCount; i > 0; --i)
|
for (unsigned i = routeCount; i > 0; --i)
|
||||||
if (routes[i - 1].prepared)
|
if (routes[i - 1].prepared)
|
||||||
routes[i - 1].transport->Abort();
|
routes[i - 1].transport->Abort();
|
||||||
|
m_tex.Abort(texStage);
|
||||||
|
if (activationReady)
|
||||||
|
activation->Abort();
|
||||||
|
}
|
||||||
|
|
||||||
for (unsigned i = routeCount; i > 0; --i)
|
for (unsigned i = routeCount; i > 0; --i)
|
||||||
EndCall(*routes[i - 1].entry, routes[i - 1].transport);
|
EndCall(*routes[i - 1].entry, routes[i - 1].transport);
|
||||||
@@ -1343,6 +1503,8 @@ void CTransportManager::Stop()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_tex.Stop();
|
||||||
|
|
||||||
for (unsigned i = count; i > 0; --i)
|
for (unsigned i = count; i > 0; --i)
|
||||||
{
|
{
|
||||||
DetachRecovery(*entries[i - 1]);
|
DetachRecovery(*entries[i - 1]);
|
||||||
|
|||||||
@@ -20,11 +20,13 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Atomic.h"
|
||||||
#include "CSRWLock.h"
|
#include "CSRWLock.h"
|
||||||
#include "transport/CControlHub.h"
|
#include "transport/CControlHub.h"
|
||||||
#include "transport/CFrameHub.h"
|
#include "transport/CFrameHub.h"
|
||||||
#include "transport/CInputHub.h"
|
#include "transport/CInputHub.h"
|
||||||
#include "transport/CRecoveryHub.h"
|
#include "transport/CRecoveryHub.h"
|
||||||
|
#include "transport/CTexHub.h"
|
||||||
#include "transport/ITransport.h"
|
#include "transport/ITransport.h"
|
||||||
#include "transport/TransportConfig.h"
|
#include "transport/TransportConfig.h"
|
||||||
|
|
||||||
@@ -34,6 +36,7 @@ static_assert(TRANSPORT_MAX_INSTANCES == FRAME_MAX_SINKS,
|
|||||||
"The transport and frame limits must match");
|
"The transport and frame limits must match");
|
||||||
|
|
||||||
class CFrameGraph;
|
class CFrameGraph;
|
||||||
|
class ITexStage;
|
||||||
struct GraphCfg;
|
struct GraphCfg;
|
||||||
|
|
||||||
class CTransportManager final : public FrameCaps
|
class CTransportManager final : public FrameCaps
|
||||||
@@ -107,6 +110,8 @@ private:
|
|||||||
bool inputFailed = false;
|
bool inputFailed = false;
|
||||||
bool inputAbsent = false;
|
bool inputAbsent = false;
|
||||||
bool frameAdded = false;
|
bool frameAdded = false;
|
||||||
|
bool frameLegacy = false;
|
||||||
|
ITexSink * texSink = nullptr;
|
||||||
bool frameAbsent = false;
|
bool frameAbsent = false;
|
||||||
uint64_t serviceRetryAt = 0;
|
uint64_t serviceRetryAt = 0;
|
||||||
bool exposed = false;
|
bool exposed = false;
|
||||||
@@ -121,8 +126,10 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<Entry> m_entries[FRAME_MAX_SINKS];
|
std::unique_ptr<Entry> m_entries[FRAME_MAX_SINKS];
|
||||||
unsigned m_entryCount = 0;
|
unsigned m_entryCount = 0;
|
||||||
|
std::atomic<uint64_t> m_frameRev = 1;
|
||||||
CControlHub m_control;
|
CControlHub m_control;
|
||||||
CFrameHub m_frames;
|
CFrameHub m_frames;
|
||||||
|
CTexHub m_tex;
|
||||||
CInputHub m_input;
|
CInputHub m_input;
|
||||||
CRecoveryHub m_recovery;
|
CRecoveryHub m_recovery;
|
||||||
Entry * m_primary = nullptr;
|
Entry * m_primary = nullptr;
|
||||||
@@ -136,6 +143,7 @@ private:
|
|||||||
bool m_started = false;
|
bool m_started = false;
|
||||||
bool m_stopping = false;
|
bool m_stopping = false;
|
||||||
bool m_stopped = false;
|
bool m_stopped = false;
|
||||||
|
uint64_t m_graphSerial = 0;
|
||||||
|
|
||||||
unsigned Entries(Entry * entries[FRAME_MAX_SINKS]) const;
|
unsigned Entries(Entry * entries[FRAME_MAX_SINKS]) const;
|
||||||
Entry * Primary() const;
|
Entry * Primary() const;
|
||||||
@@ -160,6 +168,8 @@ private:
|
|||||||
void ScheduleRetry(Entry& entry);
|
void ScheduleRetry(Entry& entry);
|
||||||
void RemoveServices(Entry& entry);
|
void RemoveServices(Entry& entry);
|
||||||
void Expose(Entry& entry);
|
void Expose(Entry& entry);
|
||||||
|
void BumpFrameRev();
|
||||||
|
uint64_t NextGraph();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CTransportManager();
|
CTransportManager();
|
||||||
@@ -173,7 +183,8 @@ public:
|
|||||||
OpenResult Open();
|
OpenResult Open();
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
bool Setup(size_t alignment);
|
bool Setup(size_t alignment);
|
||||||
CfgResult Cfg(const GraphCfg& cfg, CFrameGraph& graph);
|
CfgResult Cfg(const GraphCfg& cfg, uint64_t revision,
|
||||||
|
CFrameGraph& graph, ITexStage * activation = nullptr);
|
||||||
ProcessResult Process(ITransportActions& actions);
|
ProcessResult Process(ITransportActions& actions);
|
||||||
void Stop();
|
void Stop();
|
||||||
void SyncRecovery();
|
void SyncRecovery();
|
||||||
@@ -186,6 +197,8 @@ public:
|
|||||||
DirectFrameBufferMemory GetDirectMemory() const;
|
DirectFrameBufferMemory GetDirectMemory() const;
|
||||||
|
|
||||||
IFrameTransport& Frames();
|
IFrameTransport& Frames();
|
||||||
|
CTexHub& Tex() { return m_tex; }
|
||||||
|
uint64_t FrameRev() const;
|
||||||
IControlTransport& Control();
|
IControlTransport& Control();
|
||||||
IInputTransport& Input();
|
IInputTransport& Input();
|
||||||
};
|
};
|
||||||
|
|||||||
42
idd/LGIdd/transport/FrameIn.h
Normal file
42
idd/LGIdd/transport/FrameIn.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* 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 "capture/CFrameGraph.h"
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum class PushResult : uint8_t
|
||||||
|
{
|
||||||
|
ACCEPTED,
|
||||||
|
BUSY,
|
||||||
|
STALE,
|
||||||
|
REJECTED,
|
||||||
|
FAILED,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The texture is supplied separately as exactly one typed lease. Node zero is
|
||||||
|
// never valid because it is the borrowed IddCx acquisition surface.
|
||||||
|
struct FrameIn
|
||||||
|
{
|
||||||
|
uint64_t graph = 0;
|
||||||
|
LeafDesc desc;
|
||||||
|
};
|
||||||
40
idd/LGIdd/transport/ITexSink.h
Normal file
40
idd/LGIdd/transport/ITexSink.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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 "capture/CFrameTex.h"
|
||||||
|
#include "d3d/CInteropPool.h"
|
||||||
|
#include "transport/FrameIn.h"
|
||||||
|
|
||||||
|
class ITexSink
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ITexSink() = default;
|
||||||
|
|
||||||
|
// Push is bounded and nonblocking. ACCEPTED transfers the self-contained
|
||||||
|
// frame and lease to the transport; both remain retained until its
|
||||||
|
// asynchronous GPU use completes. Other results retain neither object and
|
||||||
|
// leave sibling routes and providers untouched. A later executor must make
|
||||||
|
// the next accepted product full-damage after BUSY or another dropped
|
||||||
|
// partial update.
|
||||||
|
virtual PushResult Push(FrameIn frame, TexLease lease) noexcept = 0;
|
||||||
|
virtual PushResult Push(FrameIn frame, D11Lease lease) noexcept = 0;
|
||||||
|
};
|
||||||
40
idd/LGIdd/transport/ITexStage.h
Normal file
40
idd/LGIdd/transport/ITexStage.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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 "transport/FrameProfile.h"
|
||||||
|
|
||||||
|
class CFrameGraph;
|
||||||
|
|
||||||
|
// The future graph executor stages every fallible pool and interop resource
|
||||||
|
// here. Commit and Abort are infallible so transport configuration and graph
|
||||||
|
// activation remain one transaction. A non-ACCEPTED Prep leaves no pending
|
||||||
|
// state; an ACCEPTED Prep remains dormant until Commit. Commit publishes the
|
||||||
|
// new producer only after texture admission has opened. Abort may resume the
|
||||||
|
// old producer because the old admission set is reopened first.
|
||||||
|
class ITexStage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ITexStage() = default;
|
||||||
|
virtual CfgResult Prep(const CFrameGraph& graph) noexcept = 0;
|
||||||
|
virtual void Commit() noexcept = 0;
|
||||||
|
virtual void Abort() noexcept = 0;
|
||||||
|
};
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
class IControlSink;
|
class IControlSink;
|
||||||
class IFrameSink;
|
class IFrameSink;
|
||||||
class IInputSource;
|
class IInputSource;
|
||||||
|
class ITexSink;
|
||||||
|
|
||||||
struct SourceKey
|
struct SourceKey
|
||||||
{
|
{
|
||||||
@@ -151,8 +152,11 @@ public:
|
|||||||
// Profiles is an immutable, ordered preference list of at most
|
// Profiles is an immutable, ordered preference list of at most
|
||||||
// FRAME_PROFILE_MAX entries whose pointer remains valid for the lifetime
|
// FRAME_PROFILE_MAX entries whose pointer remains valid for the lifetime
|
||||||
// of this instance.
|
// of this instance.
|
||||||
// Probe has no side effects. Prepare changes pending state only; Commit
|
// Probe has no side effects. Prepare changes pending state only and is safe
|
||||||
// promotes it without failure, while Abort preserves the active route.
|
// while the active texture sink is admitting frames. Commit promotes it
|
||||||
|
// without failure, while Abort preserves the active route. A texture
|
||||||
|
// transport retains old-generation resources until every accepted lease
|
||||||
|
// completes; Stop drains all accepted leases before destroying its sink.
|
||||||
virtual const FrameProfile * Profiles(unsigned& count) const = 0;
|
virtual const FrameProfile * Profiles(unsigned& count) const = 0;
|
||||||
virtual CfgResult Probe(const FrameCfg& cfg) const = 0;
|
virtual CfgResult Probe(const FrameCfg& cfg) const = 0;
|
||||||
virtual CfgResult Prepare(const FrameCfg& cfg) = 0;
|
virtual CfgResult Prepare(const FrameCfg& cfg) = 0;
|
||||||
@@ -171,6 +175,7 @@ public:
|
|||||||
|
|
||||||
// Component pointers are fixed after Setup and remain valid until Stop.
|
// Component pointers are fixed after Setup and remain valid until Stop.
|
||||||
virtual IFrameSink * FrameSink() { return nullptr; }
|
virtual IFrameSink * FrameSink() { return nullptr; }
|
||||||
|
virtual ITexSink * TexSink() { return nullptr; }
|
||||||
virtual IControlSink * Control() { return nullptr; }
|
virtual IControlSink * Control() { return nullptr; }
|
||||||
virtual IInputSource * Input() { return nullptr; }
|
virtual IInputSource * Input() { return nullptr; }
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user