mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-26 00:16:45 +00:00
Implement support for predication on D3D12. Closes #2095
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, ©Dst);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -183,6 +183,13 @@ struct D3D12RenderState
|
||||
};
|
||||
rdcarray<StreamOut> streamouts;
|
||||
|
||||
struct Predication
|
||||
{
|
||||
ResourceId buffer;
|
||||
UINT64 offset;
|
||||
D3D12_PREDICATION_OP op;
|
||||
} predication;
|
||||
|
||||
struct RootSignature
|
||||
{
|
||||
ResourceId rootsig;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user