Implement support for predication on D3D12. Closes #2095

This commit is contained in:
baldurk
2026-02-04 22:33:57 +00:00
parent 1586f3ce57
commit fcf298cff9
16 changed files with 539 additions and 13 deletions
@@ -77,6 +77,12 @@ Output Merger
.. autoclass:: D3D12BlendState
:members:
Predication
-----------
.. autoclass:: D3D12Predication
:members:
Resource States
---------------
@@ -191,6 +191,9 @@ D3D12PipelineStateViewer::D3D12PipelineStateViewer(ICaptureContext &ctx,
for(QToolButton *b : sigButtons)
QObject::connect(b, &QToolButton::clicked, this, &D3D12PipelineStateViewer::rootSigView_clicked);
QObject::connect(ui->predicateView, &QToolButton::clicked, this,
&D3D12PipelineStateViewer::predicateView_clicked);
for(RDLabel *b : shaderLabels)
{
b->setAutoFillBackground(true);
@@ -207,6 +210,13 @@ D3D12PipelineStateViewer::D3D12PipelineStateViewer(ICaptureContext &ctx,
b->setMinimumSizeHint(QSize(100, 0));
}
{
ui->predicate->setAutoFillBackground(true);
ui->predicate->setBackgroundRole(QPalette::ToolTipBase);
ui->predicate->setForegroundRole(QPalette::ToolTipText);
ui->predicate->setMinimumSizeHint(QSize(250, 0));
}
QObject::connect(m_ComputeDebugSelector, &ComputeDebugSelector::beginDebug, this,
&D3D12PipelineStateViewer::computeDebugSelector_beginDebug);
@@ -1165,6 +1175,8 @@ void D3D12PipelineStateViewer::clearState()
ui->stencils->clear();
ui->predicateGroup->setVisible(false);
ui->computeDebugSelector->setEnabled(false);
}
@@ -2075,6 +2087,23 @@ void D3D12PipelineStateViewer::setState()
.arg(ToQStr(state.rasterizer.state.shadingRateCombiners.second, GraphicsAPI::D3D12)));
ui->shadingRateImage->setText(ToQStr(state.rasterizer.state.shadingRateImage));
////////////////////////////////////////////////
// Predication
if(state.predication.resourceId == ResourceId())
{
ui->predicateGroup->setVisible(false);
}
else
{
ui->predicateGroup->setVisible(true);
ui->predicate->setText(
tr("%1 + %2 byte offset")
.arg(ToQStr(state.predication.resourceId))
.arg(Formatter::HumanFormat(state.predication.offset, Formatter::OffsetSize)));
ui->predicateSkipIfZero->setText(state.predication.skipIfZero ? lit("== 0") : lit("!= 0"));
}
////////////////////////////////////////////////
// Output Merger
@@ -2636,6 +2665,15 @@ void D3D12PipelineStateViewer::rootSigView_clicked()
m_Ctx.AddDockWindow(view->Widget(), DockReference::AddTo, this);
}
void D3D12PipelineStateViewer::predicateView_clicked()
{
IBufferViewer *viewer = m_Ctx.ViewBuffer(m_Ctx.CurD3D12PipelineState()->predication.offset, ~0ULL,
m_Ctx.CurD3D12PipelineState()->predication.resourceId,
lit("ulong predicateValue"));
m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this);
}
void D3D12PipelineStateViewer::shaderSave_clicked()
{
const D3D12Pipe::Shader *stage = stageForSender(qobject_cast<QWidget *>(QObject::sender()));
@@ -75,6 +75,7 @@ private slots:
// manual slots
void shaderView_clicked();
void rootSigView_clicked();
void predicateView_clicked();
void shaderSave_clicked();
void resource_itemActivated(RDTreeWidgetItem *item, int column);
void cbuffer_itemActivated(RDTreeWidgetItem *item, int column);
@@ -2780,7 +2780,81 @@
</layout>
</widget>
</item>
<item row="1" column="0">
<item row="1" column="0" colspan="2">
<widget class="QGroupBox" name="predicateGroup">
<property name="title">
<string>Predication</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_41">
<item>
<widget class="RDLabel" name="predicate">
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="margin">
<number>4</number>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="predicateView">
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="toolTip">
<string>Open Predicate Buffer</string>
</property>
<property name="text">
<string>View</string>
</property>
<property name="icon">
<iconset resource="../../Resources/resources.qrc">
<normaloff>:/action.png</normaloff>:/action.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="predicateSkipLabel">
<property name="text">
<string>Actions skipped when value:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="predicateSkipIfZero">
<property name="font">
<font>
<pointsize>12</pointsize>
</font>
</property>
<property name="text">
<string>== 0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="viewportsGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@@ -2835,7 +2909,7 @@
</layout>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QGroupBox" name="scissorGroup">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+39
View File
@@ -925,6 +925,38 @@ struct RootSignature
rdcarray<StaticSampler> staticSamplers;
};
DOCUMENT("Describes the current state of predicated rendering.");
struct Predication
{
DOCUMENT("");
Predication() = default;
Predication(const Predication &) = default;
Predication &operator=(const Predication &) = default;
DOCUMENT(R"(The :class:`ResourceId` of the buffer used for predicate data.
If this is unset, predication is not enabled.
:type: ResourceId
)");
ResourceId resourceId;
DOCUMENT(R"(The offset in the buffer where the value is read.
:type: int
)");
uint64_t offset = 0;
DOCUMENT(R"(``True`` if a value of zero in the buffer would lead to draws/dispatches being skipped when
predication is enabled.
If ``False`` then a zero in the buffer would lead to them being performed as normal.
:type: bool
)");
bool skipIfZero = false;
};
DOCUMENT("The full current D3D12 pipeline state.");
struct State
{
@@ -1017,6 +1049,12 @@ struct State
)");
OM outputMerger;
DOCUMENT(R"(The predicated rendering state.
:type: D3D12Predication
)");
Predication predication;
DOCUMENT(R"(The resource states for the currently live resources.
:type: List[D3D12ResourceData]
@@ -1044,4 +1082,5 @@ DECLARE_REFLECTION_STRUCT(D3D12Pipe::RootTableRange);
DECLARE_REFLECTION_STRUCT(D3D12Pipe::RootParam);
DECLARE_REFLECTION_STRUCT(D3D12Pipe::StaticSampler);
DECLARE_REFLECTION_STRUCT(D3D12Pipe::RootSignature);
DECLARE_REFLECTION_STRUCT(D3D12Pipe::Predication);
DECLARE_REFLECTION_STRUCT(D3D12Pipe::State);
@@ -185,6 +185,20 @@ private:
// the list is reset, and each time the list is submitted the queue takes an additional reference
rdcarray<PatchedRayDispatch::Resources> m_RayDispatches;
struct OutstandingQuery
{
ID3D12QueryHeap *heap;
D3D12_QUERY_TYPE Type;
UINT Index;
bool operator==(const OutstandingQuery &q) const
{
return heap == q.heap && Type == q.Type && Index == q.Index;
}
};
rdcarray<OutstandingQuery> m_OutstandingQueries;
CaptureState &m_State;
WrappedID3D12DebugCommandList m_WrappedDebug;
@@ -72,6 +72,11 @@ bool WrappedID3D12GraphicsCommandList::Serialise_Close(SerialiserType &ser)
for(int i = 0; i < markerCount; i++)
D3D12MarkerRegion::End(list);
for(OutstandingQuery &q : m_OutstandingQueries)
Unwrap(list)->EndQuery(Unwrap(q.heap), q.Type, q.Index);
m_OutstandingQueries.clear();
if(m_Cmd->m_ActionCallback)
m_Cmd->m_ActionCallback->PreCloseCommandList(list);
@@ -2840,10 +2845,19 @@ bool WrappedID3D12GraphicsCommandList::Serialise_BeginQuery(SerialiserType &ser,
{
if(m_Cmd->InRerecordRange(m_Cmd->m_LastCmdListID))
{
// don't replay query calls if we're just doing one event, it doesn't do anything
if(m_Cmd->m_FirstEventID == 1)
{
Unwrap(m_Cmd->RerecordCmdList(m_Cmd->m_LastCmdListID))
->BeginQuery(Unwrap(pQueryHeap), Type, Index);
m_OutstandingQueries.push_back({pQueryHeap, Type, Index});
}
}
}
else
{
Unwrap(pCommandList)->BeginQuery(Unwrap(pQueryHeap), Type, Index);
}
}
@@ -2884,14 +2898,33 @@ bool WrappedID3D12GraphicsCommandList::Serialise_EndQuery(SerialiserType &ser,
{
m_Cmd->m_LastCmdListID = GetResID(pCommandList);
WrappedID3D12QueryHeap *queryHeap = (WrappedID3D12QueryHeap *)pQueryHeap;
// D3D12 requires queries to remain within a command buffer so we don't have to worry about not
// having seen the corresponding begin
if(IsActiveReplaying(m_State))
{
if(m_Cmd->InRerecordRange(m_Cmd->m_LastCmdListID))
{
// don't replay query calls if we're doing partial replays, it doesn't do anything
if(m_Cmd->m_FirstEventID == 1)
{
Unwrap(m_Cmd->RerecordCmdList(m_Cmd->m_LastCmdListID))
->EndQuery(Unwrap(pQueryHeap), Type, Index);
m_OutstandingQueries.removeOne({pQueryHeap, Type, Index});
}
}
}
else
{
Unwrap(pCommandList)->EndQuery(Unwrap(pQueryHeap), Type, Index);
// during replay store which queries are issued in the capture itself, so we know which ones
// we can do a 'real' resolve of and which ones must be faked from initial contents if they
// refer to queries from previous frames.
// see the comment in ResolveQueryData for more information
queryHeap->SetQueryValid(Index, Type);
}
}
@@ -2913,6 +2946,16 @@ void WrappedID3D12GraphicsCommandList::EndQuery(ID3D12QueryHeap *pQueryHeap, D3D
m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc));
m_ListRecord->MarkResourceFrameReferenced(GetResID(pQueryHeap), eFrameRef_Read);
// during capture store which queries have been issued so we know which ones we can resolve for initial contents
WrappedID3D12QueryHeap *queryHeap = (WrappedID3D12QueryHeap *)pQueryHeap;
AddSubmissionASBuildCallback(
false,
[queryHeap, Index, Type]() {
queryHeap->SetQueryValid(Index, Type);
return true;
},
NULL);
}
}
@@ -2936,14 +2979,63 @@ bool WrappedID3D12GraphicsCommandList::Serialise_ResolveQueryData(
{
m_Cmd->m_LastCmdListID = GetResID(pCommandList);
WrappedID3D12QueryHeap *queryHeap = (WrappedID3D12QueryHeap *)pQueryHeap;
if(IsActiveReplaying(m_State))
{
if(m_Cmd->InRerecordRange(m_Cmd->m_LastCmdListID))
{
// let the query heap decide which indices to resolve normally and which to fake from the
// stored buffer
queryHeap->ResolveValidQueryData(m_Cmd->RerecordCmdList(m_Cmd->m_LastCmdListID), Type,
StartIndex, NumQueries, pDestinationBuffer,
AlignedDestinationBufferOffset);
}
}
else
{
// don't resolve queries during load, since we can't know for certain at record time whether
// or not a query will be valid (it could have been queried in a previous frame so not valid
// to resolve right now, and without knowing the submission order ahead of time we can't
// always know if a re-record in this capture will happen before this resolve).
//
// there are cases we can know this is safe, but we can't detect all cases where it's unsafe, e.g:
//
// [previous frame during capture]:
// EndEvent(Index)
//
// [on replay during captured frame]:
// listA->EndEvent(Index)
// listB->ResolveQueryData(Index)
//
// if listA is submitted first we can resolve normally on listB, but if listB were submitted first
// we'd need to fake or skip the resolve.
//
// What we do is skip resolving during load, then all queries that are ever resolved will have
// some kind of data. This case above would still return the 'wrong' data as we'd do a normal
// resolve, when in fact we should fake the resolve to get last frame's data, but at least we
// won't hit a device lost. In future we could detect this after load once we know the submission order.
// queryHeap->ResolveQueryData(pCommandList, Type, StartIndex, NumQueries, pDestinationBuffer,
// AlignedDestinationBufferOffset);
{
m_Cmd->AddEvent();
ActionDescription action;
action.copyDestination = GetResID(pDestinationBuffer);
action.copyDestinationSubresource = 0;
action.flags |= ActionFlags::Resolve;
m_Cmd->AddAction(action);
D3D12ActionTreeNode &actionNode = m_Cmd->GetActionStack().back()->children.back();
actionNode.resourceUsage.push_back(
make_rdcpair(GetResID(pDestinationBuffer),
EventUsage(actionNode.action.eventId, ResourceUsage::ResolveDst)));
}
}
}
@@ -2992,7 +3084,37 @@ bool WrappedID3D12GraphicsCommandList::Serialise_SetPredication(SerialiserType &
{
m_Cmd->m_LastCmdListID = GetResID(pCommandList);
// don't replay predication at all
bool stateUpdate = false;
if(IsActiveReplaying(m_State))
{
if(m_Cmd->InRerecordRange(m_Cmd->m_LastCmdListID))
{
Unwrap(m_Cmd->RerecordCmdList(m_Cmd->m_LastCmdListID))
->SetPredication(Unwrap(pBuffer), AlignedBufferOffset, Operation);
stateUpdate = true;
}
else if(!m_Cmd->IsPartialCmdList(m_Cmd->m_LastCmdListID))
{
stateUpdate = true;
}
}
else
{
Unwrap(pCommandList)->SetPredication(Unwrap(pBuffer), AlignedBufferOffset, Operation);
stateUpdate = true;
}
if(stateUpdate)
{
D3D12RenderState &state = m_Cmd->m_BakedCmdListInfo[m_Cmd->m_LastCmdListID].state;
state.predication.buffer = GetResID(pBuffer);
state.predication.offset = AlignedBufferOffset;
state.predication.op = Operation;
}
}
return true;
+4 -2
View File
@@ -1840,7 +1840,7 @@ bool WrappedID3D12Device::Serialise_CreateQueryHeap(SerialiserType &ser,
}
else
{
ret = new WrappedID3D12QueryHeap(pQueryHeap, ret, this);
ret = new WrappedID3D12QueryHeap(pQueryHeap, ret, Descriptor, this);
}
AddResource(pQueryHeap, ResourceType::Query, "Query Heap");
@@ -1864,7 +1864,7 @@ HRESULT WrappedID3D12Device::CreateQueryHeap(const D3D12_QUERY_HEAP_DESC *pDesc,
if(SUCCEEDED(ret))
{
WrappedID3D12QueryHeap *wrapped = new WrappedID3D12QueryHeap(ResourceId(), real, this);
WrappedID3D12QueryHeap *wrapped = new WrappedID3D12QueryHeap(ResourceId(), real, *pDesc, this);
if(IsCaptureMode(m_State))
{
@@ -1879,6 +1879,8 @@ HRESULT WrappedID3D12Device::CreateQueryHeap(const D3D12_QUERY_HEAP_DESC *pDesc,
wrapped->SetResourceRecord(record);
record->AddChunk(scope.Get());
GetResourceManager()->MarkDirtyResource(wrapped->GetResourceID());
}
*ppvHeap = (ID3D12QueryHeap *)wrapped;
+78 -5
View File
@@ -102,6 +102,55 @@ bool D3D12ResourceManager::Prepare_InitialState(ID3D12DeviceChild *res)
SetInitialContents(heap->GetResourceID(), initContents);
return true;
}
else if(type == Resource_QueryHeap)
{
WrappedID3D12QueryHeap *heap = (WrappedID3D12QueryHeap *)res;
D3D12_RESOURCE_DESC desc;
desc.Alignment = 0;
desc.DepthOrArraySize = 1;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Height = 1;
desc.Width = heap->GetResolveBufferSize();
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
HRESULT hr = S_OK;
ID3D12Resource *copyDst = NULL;
hr = m_Device->CreateInitialStateBuffer(desc, &copyDst);
if(FAILED(hr))
{
RDResult error;
SET_ERROR_RESULT(error, ResultCode::OutOfMemory,
"Couldn't create query heap readback buffer: HRESULT: %s", ToStr(hr).c_str());
m_Device->ReportFatalError(error);
return false;
}
ID3D12GraphicsCommandList *list = Unwrap(m_Device->GetInitialStateList());
heap->SaveValidQueries(list, copyDst);
if(D3D12_Debug_SingleSubmitFlushing())
{
m_Device->CloseInitialStateList();
m_Device->ExecuteLists(NULL, true);
m_Device->FlushLists(true);
}
D3D12InitialContents initContents(D3D12InitialContents::Copy, copyDst);
initContents.resourceType = Resource_QueryHeap;
SetInitialContents(GetResID(res), initContents);
return true;
}
else if(type == Resource_Resource || type == Resource_Heap)
{
WrappedID3D12Resource *wrappedResource = (WrappedID3D12Resource *)res;
@@ -630,6 +679,14 @@ uint64_t D3D12ResourceManager::GetSize_InitialState(ResourceId id, const D3D12In
// add a little extra room for fixed overhead
return 64 + data.numDescriptors * descriptorSerSize;
}
else if(data.resourceType == Resource_QueryHeap)
{
ID3D12Resource *buf = (ID3D12Resource *)data.resource;
uint64_t ret = WriteSerialiser::GetChunkAlignment() + 64;
return ret + uint64_t(buf ? buf->GetDesc().Width : 0);
}
else if(data.resourceType == Resource_Resource)
{
ID3D12Resource *buf = (ID3D12Resource *)data.resource;
@@ -911,7 +968,7 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
SetInitialContents(id, D3D12InitialContents(copyheap));
}
}
else if(type == Resource_Resource || type == Resource_Heap)
else if(type == Resource_Resource || type == Resource_Heap || type == Resource_QueryHeap)
{
byte *ResourceContents = NULL;
uint64_t ContentsLength = 0;
@@ -926,6 +983,8 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
liveRes = (ID3D12Resource *)live;
if(type == Resource_Heap)
liveRes = ((WrappedID3D12Heap *)live)->GetUnwrappedWholeMemBuffer();
if(type == Resource_QueryHeap)
liveRes = NULL;
}
SparseBinds *sparseBinds = NULL;
@@ -998,14 +1057,13 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
// only map on replay if we haven't encountered any errors so far
if(IsReplayingAndReading() && !ser.IsErrored())
{
D3D12_RESOURCE_DESC resDesc = liveRes->GetDesc();
D3D12_HEAP_PROPERTIES heapProps = {};
if(!m_Device->IsSparseResource(id))
if(type != Resource_QueryHeap && !m_Device->IsSparseResource(id))
liveRes->GetHeapProperties(&heapProps, NULL);
const bool isCPUCopyHeap =
heapProps.Type == D3D12_HEAP_TYPE_CUSTOM &&
type != Resource_QueryHeap && heapProps.Type == D3D12_HEAP_TYPE_CUSTOM &&
(heapProps.CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK ||
heapProps.CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE) &&
heapProps.MemoryPoolPreference == D3D12_MEMORY_POOL_L0;
@@ -1113,6 +1171,17 @@ bool D3D12ResourceManager::Serialise_InitialState(SerialiserType &ser, ResourceI
if(IsReplayingAndReading() && mappedBuffer)
{
// for query heaps we can't "apply" the initial contents so we have to pass the buffer over to
// the heap itself to own.
if(type == Resource_QueryHeap)
{
WrappedID3D12QueryHeap *queryHeap = (WrappedID3D12QueryHeap *)GetResource(id);
queryHeap->SetResultBuffer(mappedBuffer);
return true;
}
D3D12InitialContents initContents(D3D12InitialContents::Copy, type);
initContents.resourceType = type;
initContents.resource = mappedBuffer;
@@ -1827,6 +1896,10 @@ void D3D12ResourceManager::Create_InitialState(ResourceId id, ID3D12DeviceChild
// don't create 'default' AS contents as it's not possible. ASs must be written before being
// used by definition
}
else if(type == Resource_QueryHeap)
{
// query heap initial contents are handed over on serialise, and don't need to be created
}
else
{
RDCERR("Unexpected type needing an initial state created: %d", type);
+4
View File
@@ -1116,6 +1116,7 @@ ResourceId D3D12Replay::RenderOverlay(ResourceId texid, FloatVector clearCol, De
D3D12RenderState prev = rs;
rs.predication = {};
rs.pipe = GetResID(pso);
rs.rts.resize(1);
rs.rts[0] = *GetWrapped(rtv);
@@ -1354,6 +1355,8 @@ ResourceId D3D12Replay::RenderOverlay(ResourceId texid, FloatVector clearCol, De
if(!list)
return ResourceId();
Unwrap(list)->SetPredication(NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
for(size_t i = 0; i < rts.size(); i++)
{
const D3D12Descriptor &desc = rts[i];
@@ -1502,6 +1505,7 @@ ResourceId D3D12Replay::RenderOverlay(ResourceId texid, FloatVector clearCol, De
rs.ApplyState(m_pDevice, list);
list->OMSetRenderTargets(1, &rtv, TRUE, NULL);
list->SetPredication(NULL, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
D3D12_VIEWPORT viewport = rs.views[0];
list->RSSetViewports(1, &viewport);
+8
View File
@@ -1697,6 +1697,14 @@ void D3D12Replay::SavePipelineState(uint32_t eventId)
}
}
/////////////////////////////////////////////////
// Predication
/////////////////////////////////////////////////
state.predication.resourceId = rs.predication.buffer;
state.predication.offset = rs.predication.offset;
state.predication.skipIfZero = rs.predication.op == D3D12_PREDICATION_OP_EQUAL_ZERO;
// resource states
{
const std::map<ResourceId, SubresourceStateVector> &states = m_pDevice->GetSubresourceStates();
@@ -1683,3 +1683,91 @@ D3D12_UNORDERED_ACCESS_VIEW_DESC MakeUAVDesc(const D3D12_RESOURCE_DESC &desc)
return ret;
}
UINT64 WrappedID3D12QueryHeap::GetResolveDataSize() const
{
switch(m_Type)
{
case D3D12_QUERY_HEAP_TYPE_OCCLUSION: return sizeof(UINT64); break;
case D3D12_QUERY_HEAP_TYPE_TIMESTAMP: return sizeof(UINT64); break;
case D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS:
return sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS);
break;
case D3D12_QUERY_HEAP_TYPE_SO_STATISTICS: return sizeof(D3D12_QUERY_DATA_SO_STATISTICS); break;
case D3D12_QUERY_HEAP_TYPE_VIDEO_DECODE_STATISTICS:
// return sizeof(D3D12_QUERY_DATA_VIDEO_DECODE_STATISTICS);
return sizeof(UINT64) * 3;
break;
case D3D12_QUERY_HEAP_TYPE_COPY_QUEUE_TIMESTAMP: return sizeof(UINT64); break;
case D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS1:
return sizeof(D3D12_QUERY_DATA_PIPELINE_STATISTICS1);
break;
}
return sizeof(UINT64);
}
UINT64 WrappedID3D12QueryHeap::GetResolveBufferSize() const
{
return m_Valid.size() * GetResolveDataSize();
}
void WrappedID3D12QueryHeap::SaveValidQueries(ID3D12GraphicsCommandList *unwrappedList,
ID3D12Resource *unwrappedDestBuf)
{
UINT64 stride = GetResolveDataSize();
// find contiguous ranges, for sensible applications there should only be a few ranges rather than scattered queries.
for(UINT i = 0; i < m_Valid.size();)
{
// if we found a valid query, get the size of the range
if(m_Valid[i] != InvalidQueryType)
{
UINT start = i;
// find the range of queries of the same type - even in an 'occlusion' query heap there could
// be a mix of binary and non-binary occlusion
for(; i < m_Valid.size(); i++)
if(m_Valid[i] != m_Valid[start])
break;
UINT num = i - start;
unwrappedList->ResolveQueryData(GetReal(), m_Valid[start], start, num, unwrappedDestBuf,
stride * start);
}
else
{
i++;
}
}
}
void WrappedID3D12QueryHeap::ResolveValidQueryData(ID3D12GraphicsCommandList *list,
D3D12_QUERY_TYPE Type, UINT StartIndex,
UINT NumQueries, ID3D12Resource *destBuf,
UINT64 destOffs)
{
UINT64 stride = GetResolveDataSize();
// find contiguous ranges, for sensible applications there should only be a few ranges rather than scattered queries.
for(UINT i = StartIndex, end = StartIndex + NumQueries; i < end;)
{
// doesn't matter if the query is valid or not we will still need to resolve it
UINT start = i;
for(; i < m_Valid.size(); i++)
if(m_Valid[i] != m_Valid[start])
break;
UINT num = i - start;
// if the query range is valid, resolve it normally
if(m_Valid[start] != InvalidQueryType)
Unwrap(list)->ResolveQueryData(GetReal(), m_Valid[start], start, num, Unwrap(destBuf),
destOffs + stride * (start - StartIndex));
else if(m_SavedResults)
Unwrap(list)->CopyBufferRegion(Unwrap(destBuf), destOffs + stride * (start - StartIndex),
Unwrap(m_SavedResults), stride * start, stride * num);
}
}
+36 -2
View File
@@ -1416,6 +1416,23 @@ public:
class WrappedID3D12QueryHeap : public WrappedDeviceChild12<ID3D12QueryHeap>
{
D3D12_QUERY_HEAP_TYPE m_Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
static const D3D12_QUERY_TYPE InvalidQueryType = (D3D12_QUERY_TYPE)~0U;
// during capture, this stores which queries have ever been issued. Any queries that have been
// issued can be resolved at initial contents time so we have their results for data resolves.
//
// during replay this stores which queries are issued in the capture itself, so we know which ones
// can use a 'real' resolve and which ones must be faked with a buffer query
//
// in both cases it's initialised as all ~0U and is set to the query type each time
rdcarray<D3D12_QUERY_TYPE> m_Valid;
// on replay only, this is the 'initial contents' which is owned here instead of in the usual
// place since we can't apply this just once
ID3D12Resource *m_SavedResults = NULL;
public:
ALLOCATE_WITH_WRAPPED_POOL(WrappedID3D12QueryHeap);
@@ -1424,11 +1441,28 @@ public:
TypeEnum = Resource_QueryHeap,
};
WrappedID3D12QueryHeap(ResourceId id, ID3D12QueryHeap *real, WrappedID3D12Device *device)
WrappedID3D12QueryHeap(ResourceId id, ID3D12QueryHeap *real, const D3D12_QUERY_HEAP_DESC &desc,
WrappedID3D12Device *device)
: WrappedDeviceChild12(id, real, device)
{
m_Valid.fill(desc.Count, InvalidQueryType);
m_Type = desc.Type;
}
virtual ~WrappedID3D12QueryHeap() { Shutdown(); }
virtual ~WrappedID3D12QueryHeap()
{
SAFE_RELEASE(m_SavedResults);
Shutdown();
}
void SetQueryValid(UINT idx, D3D12_QUERY_TYPE type) { m_Valid[idx] = type; }
UINT64 GetResolveDataSize() const;
UINT64 GetResolveBufferSize() const;
void SaveValidQueries(ID3D12GraphicsCommandList *unwrappedList, ID3D12Resource *unwrappedDestBuf);
void SetResultBuffer(ID3D12Resource *buf) { m_SavedResults = buf; }
void ResolveValidQueryData(ID3D12GraphicsCommandList *list, D3D12_QUERY_TYPE Type, UINT StartIndex,
UINT NumQueries, ID3D12Resource *destBuf, UINT64 destOffs);
};
class D3D12AccelerationStructure;
+4
View File
@@ -222,6 +222,10 @@ void D3D12RenderState::ApplyState(WrappedID3D12Device *dev, ID3D12GraphicsComman
cmd->SetPipelineState(pipeState);
}
if(predication.buffer != ResourceId())
cmd->SetPredication(GetResourceManager()->GetResAs<ID3D12Resource>(predication.buffer),
predication.offset, predication.op);
if(stateobj != ResourceId())
cmd->SetPipelineState1(GetResourceManager()->GetResAs<ID3D12StateObject>(stateobj));
+7
View File
@@ -183,6 +183,13 @@ struct D3D12RenderState
};
rdcarray<StreamOut> streamouts;
struct Predication
{
ResourceId buffer;
UINT64 offset;
D3D12_PREDICATION_OP op;
} predication;
struct RootSignature
{
ResourceId rootsig;
+13 -1
View File
@@ -1644,6 +1644,16 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::RootSignature &el)
SIZE_CHECK(56);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, D3D12Pipe::Predication &el)
{
SERIALISE_MEMBER(resourceId);
SERIALISE_MEMBER(offset);
SERIALISE_MEMBER(skipIfZero);
SIZE_CHECK(24);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, D3D12Pipe::State &el)
{
@@ -1668,9 +1678,11 @@ void DoSerialise(SerialiserType &ser, D3D12Pipe::State &el)
SERIALISE_MEMBER(outputMerger);
SERIALISE_MEMBER(predication);
SERIALISE_MEMBER(resourceStates);
SIZE_CHECK(776);
SIZE_CHECK(800);
}
#pragma endregion D3D12 pipeline state