mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[idd] capture: define immutable texture products
This commit is contained in:
@@ -44,6 +44,7 @@
|
|||||||
<ClCompile Include="capture\CFrameBufferPool.cpp" />
|
<ClCompile Include="capture\CFrameBufferPool.cpp" />
|
||||||
<ClCompile Include="capture\CFrameBufferResource.cpp" />
|
<ClCompile Include="capture\CFrameBufferResource.cpp" />
|
||||||
<ClCompile Include="capture\CFrameGraph.cpp" />
|
<ClCompile Include="capture\CFrameGraph.cpp" />
|
||||||
|
<ClCompile Include="capture\CFrameTex.cpp" />
|
||||||
<ClCompile Include="capture\CFrameProcessor.cpp" />
|
<ClCompile Include="capture\CFrameProcessor.cpp" />
|
||||||
<ClCompile Include="capture\CFrameProcessorUtil.cpp" />
|
<ClCompile Include="capture\CFrameProcessorUtil.cpp" />
|
||||||
<ClCompile Include="capture\CFrameScheduler.cpp" />
|
<ClCompile Include="capture\CFrameScheduler.cpp" />
|
||||||
@@ -97,6 +98,7 @@
|
|||||||
<ClInclude Include="capture\CFrameBufferPool.h" />
|
<ClInclude Include="capture\CFrameBufferPool.h" />
|
||||||
<ClInclude Include="capture\CFrameBufferResource.h" />
|
<ClInclude Include="capture\CFrameBufferResource.h" />
|
||||||
<ClInclude Include="capture\CFrameGraph.h" />
|
<ClInclude Include="capture\CFrameGraph.h" />
|
||||||
|
<ClInclude Include="capture\CFrameTex.h" />
|
||||||
<ClInclude Include="capture\CFrameProcessor.h" />
|
<ClInclude Include="capture\CFrameProcessor.h" />
|
||||||
<ClInclude Include="capture\CFrameProcessorUtil.h" />
|
<ClInclude Include="capture\CFrameProcessorUtil.h" />
|
||||||
<ClInclude Include="capture\CFrameScheduler.h" />
|
<ClInclude Include="capture\CFrameScheduler.h" />
|
||||||
|
|||||||
@@ -100,6 +100,9 @@
|
|||||||
<ClInclude Include="capture\CFrameGraph.h">
|
<ClInclude Include="capture\CFrameGraph.h">
|
||||||
<Filter>Capture</Filter>
|
<Filter>Capture</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="capture\CFrameTex.h">
|
||||||
|
<Filter>Capture</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="capture\CFrameProcessor.h">
|
<ClInclude Include="capture\CFrameProcessor.h">
|
||||||
<Filter>Capture</Filter>
|
<Filter>Capture</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -279,6 +282,9 @@
|
|||||||
<ClCompile Include="capture\CFrameGraph.cpp">
|
<ClCompile Include="capture\CFrameGraph.cpp">
|
||||||
<Filter>Capture</Filter>
|
<Filter>Capture</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="capture\CFrameTex.cpp">
|
||||||
|
<Filter>Capture</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="capture\CFrameProcessor.cpp">
|
<ClCompile Include="capture\CFrameProcessor.cpp">
|
||||||
<Filter>Capture</Filter>
|
<Filter>Capture</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "capture/CFrameGraph.h"
|
#include "capture/CFrameGraph.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
bool Frame::Same(const GraphCfg& left, const GraphCfg& right)
|
bool Frame::Same(const GraphCfg& left, const GraphCfg& right)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
@@ -311,13 +313,35 @@ bool CFrameGraph::Want(FrameSignal signal) const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CFrameGraph::Desc(unsigned leaf, const FrameDesc& frame,
|
bool CFrameGraph::Desc(unsigned leaf, const FrameContentRef& content,
|
||||||
LeafDesc& desc) const
|
LeafDesc& desc) const
|
||||||
{
|
{
|
||||||
if (!m_sealed || leaf >= m_leafCount || !frame.serial ||
|
if (!content)
|
||||||
frame.format.width != m_cfg.srcWidth ||
|
return false;
|
||||||
frame.format.height != m_cfg.srcHeight ||
|
|
||||||
!Frame::Valid(frame.damage, frame.rects, frame.count,
|
const D12FrameFormat& source = content->format;
|
||||||
|
FrameProfile sourceProfile;
|
||||||
|
const bool validProfile = D12::Profile(source,
|
||||||
|
FrameStorage::D3D12_TEXTURE, sourceProfile);
|
||||||
|
if (!validProfile || !Frame::Same(sourceProfile, m_cfg.src))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_sealed ||
|
||||||
|
leaf >= m_leafCount ||
|
||||||
|
!content->serial ||
|
||||||
|
source.width != m_cfg.srcWidth ||
|
||||||
|
source.height != m_cfg.srcHeight ||
|
||||||
|
source.dataWidth != m_cfg.srcWidth ||
|
||||||
|
source.dataHeight != m_cfg.srcHeight ||
|
||||||
|
source.pitch != 0 ||
|
||||||
|
source.desc.Dimension !=
|
||||||
|
D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
|
||||||
|
source.desc.Width != m_cfg.srcWidth ||
|
||||||
|
source.desc.Height != m_cfg.srcHeight ||
|
||||||
|
source.desc.DepthOrArraySize != 1 ||
|
||||||
|
source.desc.MipLevels != 1 ||
|
||||||
|
source.desc.SampleDesc.Count != 1 ||
|
||||||
|
!Frame::Valid(content->damage, content->rects, content->count,
|
||||||
m_cfg.srcWidth, m_cfg.srcHeight))
|
m_cfg.srcWidth, m_cfg.srcHeight))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -327,10 +351,32 @@ bool CFrameGraph::Desc(unsigned leaf, const FrameDesc& frame,
|
|||||||
desc.epoch = route.epoch;
|
desc.epoch = route.epoch;
|
||||||
desc.node = route.node;
|
desc.node = route.node;
|
||||||
desc.profile = route.cfg.profile;
|
desc.profile = route.cfg.profile;
|
||||||
desc.frame = frame;
|
|
||||||
|
desc.frame = FrameDesc {};
|
||||||
|
desc.frame.content = content;
|
||||||
|
desc.frame.damage = content->damage;
|
||||||
|
desc.frame.count = content->count;
|
||||||
|
if (content->count)
|
||||||
|
memcpy(desc.frame.rects, content->rects,
|
||||||
|
content->count * sizeof(*content->rects));
|
||||||
|
desc.frame.format = content->format;
|
||||||
|
|
||||||
|
// A scaled checkpoint has a different damage coordinate space. Until the
|
||||||
|
// executor owns exact edge transforms, preserve correctness by making any
|
||||||
|
// partial source damage a full node update.
|
||||||
|
if (desc.frame.damage == FrameDamage::RECTS &&
|
||||||
|
(node.width != m_cfg.srcWidth || node.height != m_cfg.srcHeight))
|
||||||
|
{
|
||||||
|
desc.frame.damage = FrameDamage::FULL;
|
||||||
|
desc.frame.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
D12FrameFormat& format = desc.frame.format;
|
D12FrameFormat& format = desc.frame.format;
|
||||||
return D12::Set(format, node.profile, node.width, node.height);
|
if (!D12::Set(format, node.profile, node.width, node.height))
|
||||||
|
return false;
|
||||||
|
// Calibration/LUT nodes have already consumed this transform.
|
||||||
|
format.colorTransform.reset();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const GraphNode * CFrameGraph::Nodes(unsigned& count) const
|
const GraphNode * CFrameGraph::Nodes(unsigned& count) const
|
||||||
|
|||||||
@@ -91,9 +91,9 @@ struct GraphLeaf
|
|||||||
FrameCfg cfg;
|
FrameCfg cfg;
|
||||||
};
|
};
|
||||||
|
|
||||||
// FrameDesc is resource-free. It can cross graph nodes without retaining the
|
// All products derived from one capture retain the same immutable content.
|
||||||
// acquired IddCx texture and is paired with owned texture storage separately.
|
// It contains no acquired IddCx or Direct3D resource.
|
||||||
struct FrameDesc
|
struct FrameContent
|
||||||
{
|
{
|
||||||
uint64_t serial = 0;
|
uint64_t serial = 0;
|
||||||
uint64_t captureTime = 0;
|
uint64_t captureTime = 0;
|
||||||
@@ -103,6 +103,19 @@ struct FrameDesc
|
|||||||
D12FrameFormat format = {};
|
D12FrameFormat format = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using FrameContentRef = std::shared_ptr<const FrameContent>;
|
||||||
|
|
||||||
|
// FrameDesc adds one graph node's representation and damage to the shared
|
||||||
|
// content identity. It remains resource-free.
|
||||||
|
struct FrameDesc
|
||||||
|
{
|
||||||
|
FrameContentRef content;
|
||||||
|
FrameDamage damage = FrameDamage::NONE;
|
||||||
|
RECT rects[FRAME_DAMAGE_MAX] = {};
|
||||||
|
unsigned count = 0;
|
||||||
|
D12FrameFormat format = {};
|
||||||
|
};
|
||||||
|
|
||||||
struct LeafDesc
|
struct LeafDesc
|
||||||
{
|
{
|
||||||
BackendId id = 0;
|
BackendId id = 0;
|
||||||
@@ -140,7 +153,8 @@ public:
|
|||||||
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;
|
||||||
bool Desc(unsigned leaf, const FrameDesc& frame, LeafDesc& desc) const;
|
bool Desc(unsigned leaf, const FrameContentRef& content,
|
||||||
|
LeafDesc& desc) const;
|
||||||
|
|
||||||
const GraphCfg& Cfg() const { return m_cfg; }
|
const GraphCfg& Cfg() const { return m_cfg; }
|
||||||
const GraphNode * Nodes(unsigned& count) const;
|
const GraphNode * Nodes(unsigned& count) const;
|
||||||
|
|||||||
537
idd/LGIdd/capture/CFrameTex.cpp
Normal file
537
idd/LGIdd/capture/CFrameTex.cpp
Normal file
@@ -0,0 +1,537 @@
|
|||||||
|
/**
|
||||||
|
* 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 "capture/CFrameTex.h"
|
||||||
|
|
||||||
|
#include "Atomic.h"
|
||||||
|
#include "CDebug.h"
|
||||||
|
#include "CSRWLock.h"
|
||||||
|
#include "Seq.h"
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
enum class TexState : uint8_t
|
||||||
|
{
|
||||||
|
FREE,
|
||||||
|
WRITE,
|
||||||
|
LEASED,
|
||||||
|
WAIT,
|
||||||
|
DEAD,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void WaitSync(const D12Sync& sync)
|
||||||
|
{
|
||||||
|
while (sync.State() == D12SyncState::PENDING)
|
||||||
|
Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t NextPool()
|
||||||
|
{
|
||||||
|
static std::atomic<uint64_t> next { 0 };
|
||||||
|
return Atomic::Next(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ValidFrame(const FrameProfile& profile,
|
||||||
|
const D3D12_RESOURCE_DESC& resource, const FrameDesc& frame)
|
||||||
|
{
|
||||||
|
const D12FrameFormat& format = frame.format;
|
||||||
|
FrameProfile formatProfile;
|
||||||
|
const bool validProfile = D12::Profile(format,
|
||||||
|
FrameStorage::D3D12_TEXTURE, formatProfile);
|
||||||
|
if (!validProfile || !Frame::Same(formatProfile, profile))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const bool validDamage = Frame::Valid(frame.damage, frame.rects,
|
||||||
|
frame.count, format.width, format.height);
|
||||||
|
return
|
||||||
|
frame.content &&
|
||||||
|
frame.content->serial &&
|
||||||
|
validDamage &&
|
||||||
|
resource.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D &&
|
||||||
|
resource.Width == format.width &&
|
||||||
|
resource.Height == format.height &&
|
||||||
|
resource.DepthOrArraySize == 1 &&
|
||||||
|
resource.MipLevels == 1 &&
|
||||||
|
resource.SampleDesc.Count == 1 &&
|
||||||
|
D12::Same(resource, format.desc, D12::DescCmp::NO_FLAGS) &&
|
||||||
|
format.pitch == 0 &&
|
||||||
|
format.dataWidth == resource.Width &&
|
||||||
|
format.dataHeight == resource.Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CTexCore
|
||||||
|
{
|
||||||
|
struct Slot
|
||||||
|
{
|
||||||
|
ComPtr<ID3D12Resource> res;
|
||||||
|
D12Sync sync;
|
||||||
|
TexState state = TexState::FREE;
|
||||||
|
uint64_t gen = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
mutable CSRWLock lock;
|
||||||
|
bool open = true;
|
||||||
|
bool closed = false;
|
||||||
|
bool failed = false;
|
||||||
|
uint64_t graph = 0;
|
||||||
|
uint64_t id = 0;
|
||||||
|
unsigned node = 0;
|
||||||
|
unsigned count = 0;
|
||||||
|
FrameProfile profile;
|
||||||
|
D3D12_RESOURCE_DESC desc = {};
|
||||||
|
D3D12_RESOURCE_STATES read = D3D12_RESOURCE_STATE_COMMON;
|
||||||
|
Slot slots[CTexPool::MAX_SLOTS];
|
||||||
|
|
||||||
|
TexResult Acquire(unsigned& index, uint64_t& generation)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
if (!open || failed)
|
||||||
|
return TexResult::FAILED;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
if (slots[i].state == TexState::WAIT)
|
||||||
|
{
|
||||||
|
const D12SyncState state = slots[i].sync.State();
|
||||||
|
if (state == D12SyncState::FAILED)
|
||||||
|
{
|
||||||
|
slots[i].state = TexState::DEAD;
|
||||||
|
failed = true;
|
||||||
|
return TexResult::FAILED;
|
||||||
|
}
|
||||||
|
else if (state == D12SyncState::READY)
|
||||||
|
{
|
||||||
|
slots[i].sync = D12Sync {};
|
||||||
|
slots[i].state = TexState::FREE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
if (slots[i].state == TexState::FREE)
|
||||||
|
{
|
||||||
|
Seq::Inc(slots[i].gen);
|
||||||
|
slots[i].state = TexState::WRITE;
|
||||||
|
index = i;
|
||||||
|
generation = slots[i].gen;
|
||||||
|
return TexResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
if (slots[i].state != TexState::DEAD)
|
||||||
|
return TexResult::BUSY;
|
||||||
|
return TexResult::FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Info(unsigned index, uint64_t generation,
|
||||||
|
ComPtr<ID3D12Resource>& resource) const
|
||||||
|
{
|
||||||
|
CSRWSharedLock guard(lock);
|
||||||
|
if (!open || failed || index >= count ||
|
||||||
|
slots[index].gen != generation ||
|
||||||
|
slots[index].state != TexState::WRITE)
|
||||||
|
return false;
|
||||||
|
resource = slots[index].res;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Seal(unsigned index, uint64_t generation, const D12Sync& sync)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
if (!open || failed || index >= count ||
|
||||||
|
slots[index].gen != generation ||
|
||||||
|
slots[index].state != TexState::WRITE || !sync.Valid())
|
||||||
|
return false;
|
||||||
|
slots[index].sync = sync;
|
||||||
|
slots[index].state = TexState::LEASED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fail(unsigned index, uint64_t generation, const D12Sync& sync)
|
||||||
|
{
|
||||||
|
D12Sync retired;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
if (index >= count || slots[index].gen != generation ||
|
||||||
|
slots[index].state != TexState::WRITE)
|
||||||
|
return;
|
||||||
|
slots[index].sync = sync;
|
||||||
|
if (open)
|
||||||
|
slots[index].state = TexState::WAIT;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retired = sync;
|
||||||
|
slots[index].state = TexState::DEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (retired.Valid())
|
||||||
|
WaitSync(retired);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Drop(unsigned index, uint64_t generation)
|
||||||
|
{
|
||||||
|
D12Sync retired;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
if (index >= count || slots[index].gen != generation)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Slot& slot = slots[index];
|
||||||
|
if (slot.state == TexState::WRITE)
|
||||||
|
{
|
||||||
|
slot.state = TexState::DEAD;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (slot.state != TexState::LEASED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const D12SyncState state = slot.sync.State();
|
||||||
|
if (!open)
|
||||||
|
{
|
||||||
|
retired = slot.sync;
|
||||||
|
slot.state = TexState::DEAD;
|
||||||
|
}
|
||||||
|
else if (state == D12SyncState::FAILED)
|
||||||
|
{
|
||||||
|
slot.state = TexState::DEAD;
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
else if (state == D12SyncState::READY)
|
||||||
|
{
|
||||||
|
slot.sync = D12Sync {};
|
||||||
|
slot.state = TexState::FREE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
slot.state = TexState::WAIT;
|
||||||
|
}
|
||||||
|
if (retired.Valid())
|
||||||
|
WaitSync(retired);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cancel(unsigned index, uint64_t generation)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
if (index >= count || slots[index].gen != generation ||
|
||||||
|
slots[index].state != TexState::WRITE)
|
||||||
|
return;
|
||||||
|
slots[index].state = open ? TexState::FREE : TexState::DEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Retire(unsigned index, uint64_t generation)
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
if (index < count && slots[index].gen == generation &&
|
||||||
|
slots[index].state == TexState::WRITE)
|
||||||
|
slots[index].state = TexState::DEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close()
|
||||||
|
{
|
||||||
|
D12Sync waits[CTexPool::MAX_SLOTS];
|
||||||
|
unsigned waitCount = 0;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
if (closed)
|
||||||
|
return;
|
||||||
|
open = false;
|
||||||
|
closed = true;
|
||||||
|
for (unsigned i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
if ((slots[i].state == TexState::LEASED ||
|
||||||
|
slots[i].state == TexState::WAIT) &&
|
||||||
|
slots[i].sync.Valid())
|
||||||
|
waits[waitCount++] = slots[i].sync;
|
||||||
|
if (slots[i].state != TexState::LEASED &&
|
||||||
|
slots[i].state != TexState::WRITE)
|
||||||
|
slots[i].state = TexState::DEAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset is infrequent and cannot release a texture still referenced by
|
||||||
|
// producer commands. Outstanding consumers keep their own core alive.
|
||||||
|
for (unsigned i = 0; i < waitCount; ++i)
|
||||||
|
WaitSync(waits[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop()
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(lock);
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CFrameTex::CFrameTex(uint64_t graphValue, uint64_t poolValue,
|
||||||
|
unsigned nodeValue, unsigned slotValue, uint64_t versionValue,
|
||||||
|
const FrameProfile& profileValue,
|
||||||
|
const FrameDesc& frameValue, const ComPtr<ID3D12Resource>& res,
|
||||||
|
const D12Sync& sync, D3D12_RESOURCE_STATES state) :
|
||||||
|
m_res(res),
|
||||||
|
m_sync(sync),
|
||||||
|
m_state(state),
|
||||||
|
graph(graphValue),
|
||||||
|
pool(poolValue),
|
||||||
|
node(nodeValue),
|
||||||
|
slot(slotValue),
|
||||||
|
version(versionValue),
|
||||||
|
profile(profileValue),
|
||||||
|
frame(frameValue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFrameTex::Wait(CD3D12CommandSlot& cmd) const
|
||||||
|
{
|
||||||
|
if (!m_sync.Valid())
|
||||||
|
return false;
|
||||||
|
const UINT64 completed = m_sync.fence->GetCompletedValue();
|
||||||
|
if (completed == UINT64_MAX)
|
||||||
|
return false;
|
||||||
|
if (completed >= m_sync.value)
|
||||||
|
return true;
|
||||||
|
return cmd.WaitFor(m_sync.fence.Get(), m_sync.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TexWrite::TexWrite(const std::shared_ptr<CTexCore>& core,
|
||||||
|
unsigned index, uint64_t version) :
|
||||||
|
m_core(core), m_index(index), m_version(version)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TexWrite::TexWrite(TexWrite&& other) noexcept :
|
||||||
|
m_core(std::move(other.m_core)),
|
||||||
|
m_index(other.m_index),
|
||||||
|
m_version(other.m_version)
|
||||||
|
{
|
||||||
|
other.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TexWrite& TexWrite::operator=(TexWrite&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
Retire();
|
||||||
|
m_core = std::move(other.m_core);
|
||||||
|
m_index = other.m_index;
|
||||||
|
m_version = other.m_version;
|
||||||
|
other.Clear();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TexWrite::~TexWrite()
|
||||||
|
{
|
||||||
|
Retire();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TexWrite::Retire()
|
||||||
|
{
|
||||||
|
if (m_core)
|
||||||
|
m_core->Retire(m_index, m_version);
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TexWrite::Clear()
|
||||||
|
{
|
||||||
|
m_core.reset();
|
||||||
|
m_index = 0;
|
||||||
|
m_version = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ID3D12Resource * TexWrite::Get() const
|
||||||
|
{
|
||||||
|
ComPtr<ID3D12Resource> resource;
|
||||||
|
return m_core && m_core->Info(m_index, m_version, resource) ?
|
||||||
|
resource.Get() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TexWrite::Seal(const FrameDesc& frame, const D12Sync& sync,
|
||||||
|
TexLease& lease)
|
||||||
|
{
|
||||||
|
if (!m_core || !sync.Valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::shared_ptr<CTexCore> core = m_core;
|
||||||
|
ComPtr<ID3D12Resource> resource;
|
||||||
|
FrameDesc desc = frame;
|
||||||
|
if (!core->Info(m_index, m_version, resource) ||
|
||||||
|
!ValidFrame(core->profile, resource->GetDesc(), desc))
|
||||||
|
{
|
||||||
|
core->Fail(m_index, m_version, sync);
|
||||||
|
Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The graph descriptor is resource-free and canonical. Publish the exact
|
||||||
|
// pool descriptor, including producer-only UAV creation capability.
|
||||||
|
desc.format.desc = resource->GetDesc();
|
||||||
|
|
||||||
|
const unsigned index = m_index;
|
||||||
|
const uint64_t generation = m_version;
|
||||||
|
if (!core->Seal(index, generation, sync))
|
||||||
|
{
|
||||||
|
core->Fail(index, generation, sync);
|
||||||
|
Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFrameTex * raw = new (std::nothrow) CFrameTex(core->graph, core->id,
|
||||||
|
core->node, m_index, m_version, core->profile, desc, resource, sync,
|
||||||
|
core->read);
|
||||||
|
if (!raw)
|
||||||
|
{
|
||||||
|
core->Drop(index, generation);
|
||||||
|
Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<const CFrameTex> product;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
product = std::shared_ptr<const CFrameTex>(raw,
|
||||||
|
[core, index, generation](const CFrameTex * texture)
|
||||||
|
{
|
||||||
|
delete texture;
|
||||||
|
core->Drop(index, generation);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc&)
|
||||||
|
{
|
||||||
|
// The shared_ptr constructor invokes the supplied deleter on failure.
|
||||||
|
Clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Clear();
|
||||||
|
lease = TexLease(product);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TexWrite::Cancel()
|
||||||
|
{
|
||||||
|
if (m_core)
|
||||||
|
m_core->Cancel(m_index, m_version);
|
||||||
|
Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
TexResult CTexPool::Init(ID3D12Device3 * device, uint64_t graph,
|
||||||
|
unsigned node, const FrameProfile& profile,
|
||||||
|
const D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES state,
|
||||||
|
unsigned slots)
|
||||||
|
{
|
||||||
|
if (!device || !graph || !node || !slots || slots > MAX_SLOTS ||
|
||||||
|
profile.storage != FrameStorage::D3D12_TEXTURE ||
|
||||||
|
!Frame::Valid(profile) ||
|
||||||
|
desc.Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE2D ||
|
||||||
|
!desc.Width || !desc.Height || desc.Width > UINT_MAX ||
|
||||||
|
desc.Format != D12::Dxgi(profile.pixel) ||
|
||||||
|
desc.DepthOrArraySize != 1 ||
|
||||||
|
desc.MipLevels != 1 ||
|
||||||
|
desc.SampleDesc.Count != 1 ||
|
||||||
|
desc.SampleDesc.Quality != 0 ||
|
||||||
|
desc.Alignment != 0 ||
|
||||||
|
desc.Layout != D3D12_TEXTURE_LAYOUT_UNKNOWN ||
|
||||||
|
(static_cast<UINT>(desc.Flags) &
|
||||||
|
~static_cast<UINT>(D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS |
|
||||||
|
D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS)) != 0 ||
|
||||||
|
node >= FRAME_GRAPH_MAX_NODES ||
|
||||||
|
state != D3D12_RESOURCE_STATE_COMMON)
|
||||||
|
return TexResult::REJECTED;
|
||||||
|
|
||||||
|
std::shared_ptr<CTexCore> core;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
core = std::shared_ptr<CTexCore>(new (std::nothrow) CTexCore);
|
||||||
|
}
|
||||||
|
catch (const std::bad_alloc&)
|
||||||
|
{
|
||||||
|
return TexResult::FAILED;
|
||||||
|
}
|
||||||
|
if (!core)
|
||||||
|
return TexResult::FAILED;
|
||||||
|
core->graph = graph;
|
||||||
|
core->id = NextPool();
|
||||||
|
core->node = node;
|
||||||
|
core->count = slots;
|
||||||
|
core->profile = profile;
|
||||||
|
core->desc = desc;
|
||||||
|
core->read = state;
|
||||||
|
|
||||||
|
D3D12_HEAP_PROPERTIES heap = {};
|
||||||
|
heap.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||||
|
heap.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
|
||||||
|
heap.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
|
||||||
|
heap.CreationNodeMask = 1;
|
||||||
|
heap.VisibleNodeMask = 1;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < slots; ++i)
|
||||||
|
{
|
||||||
|
const HRESULT hr = device->CreateCommittedResource(&heap,
|
||||||
|
D3D12_HEAP_FLAG_CREATE_NOT_ZEROED, &desc, state, nullptr,
|
||||||
|
IID_PPV_ARGS(&core->slots[i].res));
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR_HR(hr, "Failed to create frame texture");
|
||||||
|
core->Close();
|
||||||
|
return TexResult::FAILED;
|
||||||
|
}
|
||||||
|
core->slots[i].res->SetName(L"Frame Texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CTexCore> old;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(m_lock);
|
||||||
|
old = std::move(m_core);
|
||||||
|
if (old)
|
||||||
|
old->Stop();
|
||||||
|
m_core = core;
|
||||||
|
}
|
||||||
|
if (old)
|
||||||
|
old->Close();
|
||||||
|
return TexResult::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTexPool::Reset()
|
||||||
|
{
|
||||||
|
std::shared_ptr<CTexCore> core;
|
||||||
|
{
|
||||||
|
CSRWExclusiveLock guard(m_lock);
|
||||||
|
core = std::move(m_core);
|
||||||
|
if (core)
|
||||||
|
core->Stop();
|
||||||
|
}
|
||||||
|
if (core)
|
||||||
|
core->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
TexResult CTexPool::Try(TexWrite& write)
|
||||||
|
{
|
||||||
|
if (write)
|
||||||
|
return TexResult::REJECTED;
|
||||||
|
|
||||||
|
std::shared_ptr<CTexCore> core;
|
||||||
|
{
|
||||||
|
CSRWSharedLock guard(m_lock);
|
||||||
|
core = m_core;
|
||||||
|
}
|
||||||
|
if (!core)
|
||||||
|
return TexResult::FAILED;
|
||||||
|
unsigned index;
|
||||||
|
uint64_t generation;
|
||||||
|
const TexResult result = core->Acquire(index, generation);
|
||||||
|
if (result == TexResult::OK)
|
||||||
|
write = TexWrite(core, index, generation);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
158
idd/LGIdd/capture/CFrameTex.h
Normal file
158
idd/LGIdd/capture/CFrameTex.h
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2026 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CSRWLock.h"
|
||||||
|
#include "capture/CFrameGraph.h"
|
||||||
|
#include "d3d/CD3D12CommandQueue.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
struct CTexCore;
|
||||||
|
class TexWrite;
|
||||||
|
|
||||||
|
enum class TexResult : uint8_t
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
BUSY,
|
||||||
|
REJECTED,
|
||||||
|
FAILED,
|
||||||
|
};
|
||||||
|
|
||||||
|
// An immutable, pool-owned D3D12 graph product. Node zero is deliberately
|
||||||
|
// excluded because it aliases the acquired IddCx surface.
|
||||||
|
class CFrameTex final
|
||||||
|
{
|
||||||
|
friend class TexWrite;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ComPtr<ID3D12Resource> m_res;
|
||||||
|
D12Sync m_sync;
|
||||||
|
D3D12_RESOURCE_STATES m_state;
|
||||||
|
|
||||||
|
CFrameTex(uint64_t graph, uint64_t pool, unsigned node, unsigned slot,
|
||||||
|
uint64_t version, const FrameProfile& profile, const FrameDesc& frame,
|
||||||
|
const ComPtr<ID3D12Resource>& res, const D12Sync& sync,
|
||||||
|
D3D12_RESOURCE_STATES state);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CFrameTex(const CFrameTex&) = delete;
|
||||||
|
CFrameTex& operator=(const CFrameTex&) = delete;
|
||||||
|
|
||||||
|
const uint64_t graph;
|
||||||
|
const uint64_t pool;
|
||||||
|
const unsigned node;
|
||||||
|
const unsigned slot;
|
||||||
|
const uint64_t version;
|
||||||
|
const FrameProfile profile;
|
||||||
|
const FrameDesc frame;
|
||||||
|
|
||||||
|
ID3D12Resource * Get() const { return m_res.Get(); }
|
||||||
|
const D12Sync& Sync() const { return m_sync; }
|
||||||
|
D3D12_RESOURCE_STATES State() const { return m_state; }
|
||||||
|
bool Wait(CD3D12CommandSlot& cmd) const;
|
||||||
|
D12SyncState Status() const { return m_sync.State(); }
|
||||||
|
bool Ready() const { return m_sync.Done(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Consumers retain a lease until their own GPU use of the texture has
|
||||||
|
// completed. Dropping the CPU call's local copy is not sufficient.
|
||||||
|
class TexLease
|
||||||
|
{
|
||||||
|
friend class TexWrite;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<const CFrameTex> m_tex;
|
||||||
|
explicit TexLease(std::shared_ptr<const CFrameTex> tex) : m_tex(tex) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
TexLease() = default;
|
||||||
|
TexLease(const TexLease&) = default;
|
||||||
|
TexLease& operator=(const TexLease&) = default;
|
||||||
|
TexLease(TexLease&&) noexcept = default;
|
||||||
|
TexLease& operator=(TexLease&&) noexcept = default;
|
||||||
|
|
||||||
|
explicit operator bool() const { return !!m_tex; }
|
||||||
|
const CFrameTex * Get() const { return m_tex.get(); }
|
||||||
|
const CFrameTex * operator->() const { return m_tex.get(); }
|
||||||
|
void Reset() { m_tex.reset(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A move-only producer reservation. Abandoning a writer retires its slot;
|
||||||
|
// Cancel may be used only before any command referencing the texture submits.
|
||||||
|
class TexWrite
|
||||||
|
{
|
||||||
|
friend class CTexPool;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<CTexCore> m_core;
|
||||||
|
unsigned m_index = 0;
|
||||||
|
uint64_t m_version = 0;
|
||||||
|
|
||||||
|
TexWrite(const std::shared_ptr<CTexCore>& core,
|
||||||
|
unsigned index, uint64_t version);
|
||||||
|
void Clear();
|
||||||
|
void Retire();
|
||||||
|
|
||||||
|
public:
|
||||||
|
TexWrite() = default;
|
||||||
|
TexWrite(const TexWrite&) = delete;
|
||||||
|
TexWrite& operator=(const TexWrite&) = delete;
|
||||||
|
TexWrite(TexWrite&& other) noexcept;
|
||||||
|
TexWrite& operator=(TexWrite&& other) noexcept;
|
||||||
|
~TexWrite();
|
||||||
|
|
||||||
|
explicit operator bool() const { return !!m_core; }
|
||||||
|
ID3D12Resource * Get() const;
|
||||||
|
// Seal only after producer commands restore the texture to the pool's
|
||||||
|
// configured immutable state and Execute returns this exact sync point.
|
||||||
|
bool Seal(const FrameDesc& frame, const D12Sync& sync, TexLease& lease);
|
||||||
|
void Cancel();
|
||||||
|
};
|
||||||
|
|
||||||
|
// One pool owns interchangeable textures for a single graph node. Reset stops
|
||||||
|
// acquisition and drains every sealed producer point. Existing writers and
|
||||||
|
// leases keep the retired core alive. Reset invalidates writers; a writer
|
||||||
|
// which already submitted must still Seal so its point is safely drained,
|
||||||
|
// but it will not publish a product from the retired generation.
|
||||||
|
// The configured state is the promised state presented to every consumer;
|
||||||
|
// it is not queried from D3D12. Consumers may read but never transition or
|
||||||
|
// write a shared product.
|
||||||
|
class CTexPool
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
mutable CSRWLock m_lock;
|
||||||
|
std::shared_ptr<CTexCore> m_core;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const unsigned MAX_SLOTS = 4;
|
||||||
|
|
||||||
|
CTexPool() = default;
|
||||||
|
CTexPool(const CTexPool&) = delete;
|
||||||
|
CTexPool& operator=(const CTexPool&) = delete;
|
||||||
|
~CTexPool() { Reset(); }
|
||||||
|
|
||||||
|
TexResult Init(ID3D12Device3 * device, uint64_t graph, unsigned node,
|
||||||
|
const FrameProfile& profile, const D3D12_RESOURCE_DESC& desc,
|
||||||
|
D3D12_RESOURCE_STATES state, unsigned slots);
|
||||||
|
void Reset();
|
||||||
|
TexResult Try(TexWrite& write);
|
||||||
|
};
|
||||||
@@ -236,8 +236,11 @@ void CD3D12CommandSlot::Cancel()
|
|||||||
SetEvent(m_availableEvent.Get());
|
SetEvent(m_availableEvent.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CD3D12CommandSlot::Execute()
|
bool CD3D12CommandSlot::Execute(D12Sync * sync)
|
||||||
{
|
{
|
||||||
|
if (sync)
|
||||||
|
*sync = D12Sync {};
|
||||||
|
|
||||||
State expected = STATE_RECORDING;
|
State expected = STATE_RECORDING;
|
||||||
if (!Atomic::CAS(m_state, expected, STATE_SUBMITTED,
|
if (!Atomic::CAS(m_state, expected, STATE_SUBMITTED,
|
||||||
std::memory_order_acq_rel))
|
std::memory_order_acq_rel))
|
||||||
@@ -256,7 +259,7 @@ bool CD3D12CommandSlot::Execute()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_queue->Submit(*this))
|
if (m_queue->Submit(*this, sync))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!Atomic::Load(m_submitted, std::memory_order_acquire))
|
if (!Atomic::Load(m_submitted, std::memory_order_acquire))
|
||||||
@@ -572,7 +575,7 @@ void CD3D12CommandQueue::WaitForIdle()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CD3D12CommandQueue::Submit(CD3D12CommandSlot& slot)
|
bool CD3D12CommandQueue::Submit(CD3D12CommandSlot& slot, D12Sync * sync)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
CSRWExclusiveLock lock(m_submitLock);
|
CSRWExclusiveLock lock(m_submitLock);
|
||||||
@@ -612,6 +615,14 @@ bool CD3D12CommandQueue::Submit(CD3D12CommandSlot& slot)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture the immutable point while submission is serialized. The slot
|
||||||
|
// may complete and be acquired again as soon as we leave this scope.
|
||||||
|
if (sync)
|
||||||
|
{
|
||||||
|
sync->fence = m_fence;
|
||||||
|
sync->value = fenceTarget;
|
||||||
|
}
|
||||||
|
|
||||||
hr = m_fence->SetEventOnCompletion(fenceTarget, slot.m_event.Get());
|
hr = m_fence->SetEventOnCompletion(fenceTarget, slot.m_event.Get());
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,6 +35,32 @@ using namespace Microsoft::WRL::Wrappers::HandleTraits;
|
|||||||
|
|
||||||
class CD3D12CommandQueue;
|
class CD3D12CommandQueue;
|
||||||
|
|
||||||
|
enum class D12SyncState : uint8_t
|
||||||
|
{
|
||||||
|
PENDING,
|
||||||
|
READY,
|
||||||
|
FAILED,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct D12Sync
|
||||||
|
{
|
||||||
|
ComPtr<ID3D12Fence> fence;
|
||||||
|
UINT64 value = 0;
|
||||||
|
|
||||||
|
bool Valid() const { return fence && value; }
|
||||||
|
D12SyncState State() const
|
||||||
|
{
|
||||||
|
if (!Valid())
|
||||||
|
return D12SyncState::FAILED;
|
||||||
|
const UINT64 completed = fence->GetCompletedValue();
|
||||||
|
if (completed == UINT64_MAX)
|
||||||
|
return D12SyncState::FAILED;
|
||||||
|
return completed >= value ?
|
||||||
|
D12SyncState::READY : D12SyncState::PENDING;
|
||||||
|
}
|
||||||
|
bool Done() const { return State() == D12SyncState::READY; }
|
||||||
|
};
|
||||||
|
|
||||||
class CD3D12CommandSlot
|
class CD3D12CommandSlot
|
||||||
{
|
{
|
||||||
friend class CD3D12CommandQueue;
|
friend class CD3D12CommandQueue;
|
||||||
@@ -104,7 +130,7 @@ class CD3D12CommandSlot
|
|||||||
|
|
||||||
bool Acquire();
|
bool Acquire();
|
||||||
void Cancel();
|
void Cancel();
|
||||||
bool Execute();
|
bool Execute(D12Sync * sync = nullptr);
|
||||||
|
|
||||||
bool BeginTiming();
|
bool BeginTiming();
|
||||||
void EndTiming();
|
void EndTiming();
|
||||||
@@ -162,7 +188,7 @@ class CD3D12CommandQueue
|
|||||||
bool m_timingSupported = false;
|
bool m_timingSupported = false;
|
||||||
|
|
||||||
bool InitTiming(ID3D12Device3 * device, UINT slotCount);
|
bool InitTiming(ID3D12Device3 * device, UINT slotCount);
|
||||||
bool Submit(CD3D12CommandSlot& slot);
|
bool Submit(CD3D12CommandSlot& slot, D12Sync * sync);
|
||||||
bool SnapshotTiming(CD3D12CommandSlot& slot) const;
|
bool SnapshotTiming(CD3D12CommandSlot& slot) const;
|
||||||
bool GetGPUTimes(const CD3D12CommandSlot& slot,
|
bool GetGPUTimes(const CD3D12CommandSlot& slot,
|
||||||
uint64_t& start, uint64_t& end) const;
|
uint64_t& start, uint64_t& end) const;
|
||||||
|
|||||||
Reference in New Issue
Block a user