diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index d487a36f1..4dd7fc4ab 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -91,15 +91,19 @@ struct D3D12ViewTag OMDepth, }; - D3D12ViewTag() : type(SRV), space(0), rootSignatureIndex(0), immediate(false) {} + D3D12ViewTag() + : type(SRV), space(0), rootSignatureIndex(0), immediate(false), directHeapAccess(false) + { + } D3D12ViewTag(ResType t, int s, int el, bool imm, const D3D12Pipe::View &rs) - : type(t), space(s), rootSignatureIndex(el), immediate(imm), res(rs) + : type(t), space(s), rootSignatureIndex(el), immediate(imm), directHeapAccess(false), res(rs) { } ResType type; int space, rootSignatureIndex; bool immediate; + bool directHeapAccess; D3D12Pipe::View res; }; @@ -731,9 +735,10 @@ void D3D12PipelineStateViewer::addResourceRow(const D3D12ViewTag &view, const Bi { const D3D12Pipe::View &r = view.res; bool uav = view.type == D3D12ViewTag::UAV; + bool directHeapAccess = view.directHeapAccess; bool filledSlot = (r.resourceId != ResourceId()); - bool usedSlot = (bind && bind->used && r.dynamicallyUsed); + bool usedSlot = (bind && bind->used && r.dynamicallyUsed) || directHeapAccess; // if a target is set to RTVs or DSV, it is implicitly used if(filledSlot) @@ -752,6 +757,9 @@ void D3D12PipelineStateViewer::addResourceRow(const D3D12ViewTag &view, const Bi if(view.type == D3D12ViewTag::OMDepth) regname = tr("Depth"); + if(directHeapAccess) + regname = tr(""); + uint32_t w = 1, h = 1, d = 1; uint32_t a = 1; QString format = tr("Unknown"); @@ -847,12 +855,20 @@ void D3D12PipelineStateViewer::addResourceRow(const D3D12ViewTag &view, const Bi } else { - QString rootel = view.immediate - ? tr("#%1 Direct").arg(view.rootSignatureIndex) - : tr("#%1 Table[%2]").arg(view.rootSignatureIndex).arg(r.tableIndex); + QString spaceStr; + if(!directHeapAccess) + spaceStr = QString::number(view.space); + + QString rootel; + if(directHeapAccess) + rootel = tr("ResourceDescriptorHeap[%1]").arg(r.tableIndex); + else if(view.immediate) + rootel = tr("#%1 Direct").arg(view.rootSignatureIndex); + else + rootel = tr("#%1 Table[%2]").arg(view.rootSignatureIndex).arg(r.tableIndex); node = new RDTreeWidgetItem( - {rootel, view.space, regname, r.resourceId, typeName, w, h, d, a, format, QString()}); + {rootel, spaceStr, regname, r.resourceId, typeName, w, h, d, a, format, QString()}); } node->setTag(QVariant::fromValue(view)); @@ -1076,6 +1092,8 @@ void D3D12PipelineStateViewer::setShaderState( tag.rootSignatureIndex = rootElements[i].rootSignatureIndex; tag.immediate = rootElements[i].immediate; + bool directHeapAccess = rootElements[i].registerSpace == ~0U; + switch(rootElements[i].type) { case BindType::ReadOnlyResource: @@ -1093,6 +1111,19 @@ void D3D12PipelineStateViewer::setShaderState( const rdcarray &views = rootElements[i].views; + // add direct heap access resources + if(directHeapAccess) + { + for(size_t view = 0; view < views.size(); ++view) + { + tag.space = -1; + tag.res = views[view]; + tag.directHeapAccess = directHeapAccess; + addResourceRow(tag, NULL, NULL, tree); + } + continue; + } + size_t firstView = rootElements[i].firstUsedIndex; size_t lastView = qMin(views.size() - 1, size_t(rootElements[i].lastUsedIndex)); @@ -1219,7 +1250,7 @@ void D3D12PipelineStateViewer::setShaderState( const Bindpoint *bind = NULL; const ShaderSampler *shaderInput = NULL; - if(stage.reflection) + if(stage.reflection && !directHeapAccess) { for(int k = 0; k < stage.bindpointMapping.samplers.count(); ++k) { @@ -1241,19 +1272,30 @@ void D3D12PipelineStateViewer::setShaderState( } } } - - QString rootel = - rootElements[i].immediate - ? tr("#%1 Static").arg(rootElements[i].rootSignatureIndex) - : tr("#%1 Table[%2]").arg(rootElements[i].rootSignatureIndex).arg(s.tableIndex); + QString spaceStr = QString::number(rootElements[i].registerSpace); + QString rootel; + if(directHeapAccess) + { + rootel = tr("SamplerDescriptorHeap[%1]").arg(s.tableIndex); + spaceStr = tr(""); + } + else if(rootElements[i].immediate) + { + rootel = tr("#%1 Static").arg(rootElements[i].rootSignatureIndex); + } + else + { + rootel = tr("#%1 Table[%2]").arg(rootElements[i].rootSignatureIndex).arg(s.tableIndex); + } bool filledSlot = s.filter.minify != FilterMode::NoFilter; - bool usedSlot = (bind && bind->used); + bool usedSlot = (bind && bind->used) || directHeapAccess; if(showNode(usedSlot, filledSlot)) { - QString regname = QString::number(s.bind); - + QString regname; + if(!directHeapAccess) + regname = QString::number(s.bind); if(shaderInput && !shaderInput->name.empty()) regname += lit(": ") + shaderInput->name; @@ -1307,7 +1349,7 @@ void D3D12PipelineStateViewer::setShaderState( filter += QFormatStr(" (%1)").arg(ToQStr(s.filter.filter)); RDTreeWidgetItem *node = new RDTreeWidgetItem( - {rootel, rootElements[i].registerSpace, regname, addressing, filter, + {rootel, spaceStr, regname, addressing, filter, QFormatStr("%1 - %2") .arg(s.minLOD == -FLT_MAX ? lit("0") : QString::number(s.minLOD)) .arg(s.maxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(s.maxLOD)), @@ -1335,7 +1377,7 @@ void D3D12PipelineStateViewer::setShaderState( const Bindpoint *bind = NULL; const ConstantBlock *shaderCBuf = NULL; - if(stage.reflection) + if(stage.reflection && !directHeapAccess) { for(int k = 0; k < stage.bindpointMapping.constantBlocks.count(); ++k) { @@ -1360,14 +1402,19 @@ void D3D12PipelineStateViewer::setShaderState( } } } - + if(directHeapAccess) + cbuftag = QVariant::fromValue(D3D12CBufTag(0, (uint32_t)j, (uint32_t)i)); if(!cbuftag.isValid()) cbuftag = QVariant::fromValue(D3D12CBufTag(rootElements[i].registerSpace, b.bind, (uint32_t)i)); QString rootel; - if(rootElements[i].immediate) + if(directHeapAccess) + { + rootel = tr("ResourceDescriptorHeap[%1]").arg(b.tableIndex); + } + else if(rootElements[i].immediate) { if(!b.rootValues.empty()) rootel = tr("#%1 Consts").arg(rootElements[i].rootSignatureIndex); @@ -1383,7 +1430,7 @@ void D3D12PipelineStateViewer::setShaderState( if(rootElements[i].immediate && !b.rootValues.empty()) filledSlot = true; - bool usedSlot = (bind && bind->used); + bool usedSlot = (bind && bind->used) || directHeapAccess; if(showNode(usedSlot, filledSlot)) { @@ -1397,12 +1444,18 @@ void D3D12PipelineStateViewer::setShaderState( QString regname = QString::number(b.bind); - if(shaderCBuf && !shaderCBuf->name.empty()) - regname += lit(": ") + shaderCBuf->name; - - if(bind != NULL && bind->arraySize > 1) - regname += tr("[%1]").arg(b.bind - bind->bind); + if(directHeapAccess) + { + regname = tr(""); + } + else + { + if(shaderCBuf && !shaderCBuf->name.empty()) + regname += lit(": ") + shaderCBuf->name; + if(bind != NULL && bind->arraySize > 1) + regname += tr("[%1]").arg(b.bind - bind->bind); + } QString sizestr; if(bytesize == (uint32_t)length) sizestr = tr("%1 Variables, %2 bytes").arg(numvars).arg(length); @@ -1413,8 +1466,11 @@ void D3D12PipelineStateViewer::setShaderState( if(length < bytesize) filledSlot = false; + QString spaceStr = QString::number(rootElements[i].registerSpace); + if(directHeapAccess) + spaceStr = tr(""); RDTreeWidgetItem *node = new RDTreeWidgetItem( - {rootel, (qulonglong)rootElements[i].registerSpace, regname, b.resourceId, + {rootel, spaceStr, regname, b.resourceId, QFormatStr("%1 - %2").arg(offset).arg(offset + bytesize), sizestr, QString()}); node->setTag(cbuftag); @@ -2703,6 +2759,10 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe { if((els[i].visibility & MaskForStage(sh.stage)) == ShaderStageMask::Unknown) continue; + bool directHeapAccess = els[i].registerSpace == ~0U; + QString spaceStr; + if(!directHeapAccess) + spaceStr = QString::number(els[i].registerSpace); switch(els[i].type) { @@ -2713,7 +2773,7 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe const D3D12Pipe::View &v = els[i].views[j]; const ShaderResource *shaderInput = NULL; - if(sh.reflection) + if(sh.reflection && !directHeapAccess) { for(int32_t k = 0; k < sh.bindpointMapping.readOnlyResources.count(); ++k) { @@ -2737,13 +2797,17 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe } } - QString rootel = els[i].immediate - ? tr("#%1 Direct").arg(els[i].rootSignatureIndex) - : tr("#%1 Table[%2]").arg(els[i].rootSignatureIndex).arg(v.tableIndex); + QString rootel; + if(directHeapAccess) + rootel = tr("ResourceDescriptorHeap[%1]").arg(v.tableIndex); + else if(els[i].immediate) + rootel = tr("#%1 Direct").arg(els[i].rootSignatureIndex); + else + rootel = tr("#%1 Table[%2]").arg(els[i].rootSignatureIndex).arg(v.tableIndex); QVariantList row = exportViewHTML(v, false, shaderInput, QString()); - row.push_front(els[i].registerSpace); + row.push_front(spaceStr); row.push_front(rootel); rowsRO.push_back(row); @@ -2757,7 +2821,7 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe const D3D12Pipe::View &v = els[i].views[j]; const ShaderResource *shaderInput = NULL; - if(sh.reflection) + if(sh.reflection && !directHeapAccess) { for(int32_t k = 0; k < sh.bindpointMapping.readWriteResources.count(); ++k) { @@ -2780,14 +2844,16 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe } } } - - QString rootel = els[i].immediate - ? tr("#%1 Direct").arg(els[i].rootSignatureIndex) - : tr("#%1 Table[%2]").arg(els[i].rootSignatureIndex).arg(v.tableIndex); - + QString rootel; + if(directHeapAccess) + rootel = tr("ResourceDescriptorHeap[%1]").arg(v.tableIndex); + else if(els[i].immediate) + rootel = tr("#%1 Direct").arg(els[i].rootSignatureIndex); + else + rootel = tr("#%1 Table[%2]").arg(els[i].rootSignatureIndex).arg(v.tableIndex); QVariantList row = exportViewHTML(v, true, shaderInput, QString()); - row.push_front(els[i].registerSpace); + row.push_front(spaceStr); row.push_front(rootel); rowsRW.push_back(row); @@ -2801,7 +2867,7 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe const D3D12Pipe::Sampler &s = els[i].samplers[j]; const ShaderSampler *shaderInput = NULL; - if(sh.reflection) + if(sh.reflection && !directHeapAccess) { for(int32_t k = 0; k < sh.bindpointMapping.samplers.count(); ++k) { @@ -2824,14 +2890,18 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe } } } - - QString rootel = els[i].immediate - ? tr("#%1 Static").arg(els[i].rootSignatureIndex) - : tr("#%1 Table[%2]").arg(els[i].rootSignatureIndex).arg(s.tableIndex); + QString rootel; + if(directHeapAccess) + rootel = tr("SamplerDescriptorHeap[%1]").arg(s.tableIndex); + else if(els[i].immediate) + rootel = tr("#%1 Static").arg(els[i].rootSignatureIndex); + else + rootel = tr("#%1 Table[%2]").arg(els[i].rootSignatureIndex).arg(s.tableIndex); { - QString regname = QString::number(s.bind); - + QString regname; + if(!directHeapAccess) + regname = QString::number(s.bind); if(shaderInput && !shaderInput->name.empty()) regname += lit(": ") + shaderInput->name; @@ -2885,7 +2955,7 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe filter += QFormatStr(" (%1)").arg(ToQStr(s.filter.filter)); rowsSampler.push_back( - {rootel, els[i].registerSpace, regname, addressing, filter, + {rootel, spaceStr, regname, addressing, filter, QFormatStr("%1 - %2") .arg(s.minLOD == -FLT_MAX ? lit("0") : QString::number(s.minLOD)) .arg(s.maxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(s.maxLOD)), @@ -2901,7 +2971,7 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe const D3D12Pipe::ConstantBuffer &b = els[i].constantBuffers[j]; const ConstantBlock *shaderCBuf = NULL; - if(sh.reflection) + if(sh.reflection && !directHeapAccess) { for(int32_t k = 0; k < sh.bindpointMapping.constantBlocks.count(); ++k) { @@ -2927,8 +2997,11 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe } QString rootel; - - if(els[i].immediate) + if(directHeapAccess) + { + rootel = tr("ResourceDescriptorHeap[%1]").arg(b.tableIndex); + } + else if(els[i].immediate) { if(!b.rootValues.empty()) rootel = tr("#%1 Consts").arg(els[i].rootSignatureIndex); @@ -2955,15 +3028,16 @@ void D3D12PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D12Pipe else name = tr("Empty"); - QString regname = QString::number(b.bind); - + QString regname; + if(!directHeapAccess) + regname = QString::number(b.bind); if(shaderCBuf && !shaderCBuf->name.empty()) regname += lit(": ") + shaderCBuf->name; length = qMin(length, (uint64_t)bytesize); - rowsCB.push_back({rootel, els[i].registerSpace, regname, name, (qulonglong)offset, - (qulonglong)length, numvars}); + rowsCB.push_back( + {rootel, spaceStr, regname, name, (qulonglong)offset, (qulonglong)length, numvars}); } } break; diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index e0fb4442e..6890d00ed 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -932,6 +932,24 @@ void D3D12Replay::FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor } } +void D3D12Replay::FillSampler(D3D12Pipe::Sampler &samp, const D3D12_SAMPLER_DESC &sampDesc) +{ + samp.addressU = MakeAddressMode(sampDesc.AddressU); + samp.addressV = MakeAddressMode(sampDesc.AddressV); + samp.addressW = MakeAddressMode(sampDesc.AddressW); + + samp.borderColor = sampDesc.BorderColor; + + samp.compareFunction = MakeCompareFunc(sampDesc.ComparisonFunc); + samp.filter = MakeFilter(sampDesc.Filter); + samp.maxAnisotropy = 0; + if(samp.filter.minify == FilterMode::Anisotropic) + samp.maxAnisotropy = sampDesc.MaxAnisotropy; + samp.maxLOD = sampDesc.MaxLOD; + samp.minLOD = sampDesc.MinLOD; + samp.mipLODBias = sampDesc.MipLODBias; +} + ShaderStageMask ToShaderStageMask(D3D12_SHADER_VISIBILITY vis) { switch(vis) @@ -959,7 +977,7 @@ void D3D12Replay::FillRootElements(uint32_t eventId, const D3D12RenderState::Roo const D3D12FeedbackBindIdentifier *curUsage = usage.used.begin(); const D3D12FeedbackBindIdentifier *lastUsage = usage.used.end(); - D3D12FeedbackBindIdentifier curIdentifier; + D3D12FeedbackBindIdentifier curIdentifier = {}; WrappedID3D12RootSignature *sig = m_pDevice->GetResourceManager()->GetCurrentAs(rootSig.rootsig); @@ -1232,22 +1250,7 @@ void D3D12Replay::FillRootElements(uint32_t eventId, const D3D12RenderState::Roo if(desc) { const D3D12_SAMPLER_DESC &sampDesc = desc->GetSampler(); - - samp.addressU = MakeAddressMode(sampDesc.AddressU); - samp.addressV = MakeAddressMode(sampDesc.AddressV); - samp.addressW = MakeAddressMode(sampDesc.AddressW); - - samp.borderColor = sampDesc.BorderColor; - - samp.compareFunction = MakeCompareFunc(sampDesc.ComparisonFunc); - samp.filter = MakeFilter(sampDesc.Filter); - samp.maxAnisotropy = 0; - if(samp.filter.minify == FilterMode::Anisotropic) - samp.maxAnisotropy = sampDesc.MaxAnisotropy; - samp.maxLOD = sampDesc.MaxLOD; - samp.minLOD = sampDesc.MinLOD; - samp.mipLODBias = sampDesc.MipLODBias; - + FillSampler(samp, sampDesc); desc++; } } @@ -1355,6 +1358,82 @@ void D3D12Replay::FillRootElements(uint32_t eventId, const D3D12RenderState::Roo } } } + // direct heap access resources + { + D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState; + WrappedID3D12DescriptorHeap *resourceHeap = NULL; + WrappedID3D12DescriptorHeap *samplerHeap = NULL; + for(ResourceId id : rs.heaps) + { + WrappedID3D12DescriptorHeap *heap = + (WrappedID3D12DescriptorHeap *)rm->GetCurrentAs(id); + D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc(); + if(desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) + resourceHeap = heap; + else + samplerHeap = heap; + } + + D3D12Pipe::RootSignatureRange *element = NULL; + while(curUsage < lastUsage && curUsage->directAccess) + { + ShaderStageMask visibility = (ShaderStageMask)(1 << (int)curUsage->shaderStage); + if(element == NULL || (element->visibility != visibility) || + (element->type != curUsage->bindType)) + { + rootElements.resize_for_index(ridx); + element = &rootElements[ridx++]; + element->immediate = false; + element->rootSignatureIndex = ~0U; + element->type = curUsage->bindType; + element->visibility = visibility; + element->registerSpace = ~0U; + element->dynamicallyUsedCount = 0; + element->samplers.clear(); + element->constantBuffers.clear(); + element->views.clear(); + } + if(curUsage->bindType == BindType::Sampler) + { + D3D12Descriptor *desc = + (D3D12Descriptor *)samplerHeap->GetCPUDescriptorHandleForHeapStart().ptr; + desc += curUsage->descIndex; + element->samplers.push_back(D3D12Pipe::Sampler()); + D3D12Pipe::Sampler &samp = element->samplers.back(); + const D3D12_SAMPLER_DESC &sampDesc = desc->GetSampler(); + FillSampler(samp, sampDesc); + samp.tableIndex = curUsage->descIndex; + element->dynamicallyUsedCount++; + } + else if(curUsage->bindType == BindType::ConstantBuffer) + { + D3D12Descriptor *desc = + (D3D12Descriptor *)resourceHeap->GetCPUDescriptorHandleForHeapStart().ptr; + desc += curUsage->descIndex; + element->constantBuffers.push_back(D3D12Pipe::ConstantBuffer()); + D3D12Pipe::ConstantBuffer &cb = element->constantBuffers.back(); + + const D3D12_CONSTANT_BUFFER_VIEW_DESC &cbv = desc->GetCBV(); + WrappedID3D12Resource::GetResIDFromAddr(cbv.BufferLocation, cb.resourceId, cb.byteOffset); + cb.resourceId = rm->GetOriginalID(cb.resourceId); + cb.byteSize = cbv.SizeInBytes; + cb.tableIndex = curUsage->descIndex; + element->dynamicallyUsedCount++; + } + else + { + D3D12Descriptor *desc = + (D3D12Descriptor *)resourceHeap->GetCPUDescriptorHandleForHeapStart().ptr; + desc += curUsage->descIndex; + D3D12Pipe::View view; + FillResourceView(view, desc); + view.tableIndex = curUsage->descIndex; + element->views.push_back(view); + element->dynamicallyUsedCount++; + } + curUsage++; + } + } // Each static sampler gets its own RootElement for(size_t i = 0; i < sig->sig.StaticSamplers.size(); i++) diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index ad5aa3b04..8a405a83e 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -55,19 +55,35 @@ struct D3D12FeedbackBindIdentifier size_t rootEl; size_t rangeIndex; UINT descIndex; + ShaderStage shaderStage; // Only used for direct access views + BindType bindType; // Only used for direct access views + bool directAccess; bool operator<(const D3D12FeedbackBindIdentifier &o) const { - if(rootEl != o.rootEl) - return rootEl < o.rootEl; - if(rangeIndex != o.rangeIndex) - return rangeIndex < o.rangeIndex; + if(directAccess != o.directAccess) + return directAccess < o.directAccess; + if(!directAccess) + { + if(rootEl != o.rootEl) + return rootEl < o.rootEl; + if(rangeIndex != o.rangeIndex) + return rangeIndex < o.rangeIndex; + } + else + { + if(shaderStage != o.shaderStage) + return shaderStage < o.shaderStage; + if(bindType != o.bindType) + return bindType < o.bindType; + } return descIndex < o.descIndex; } bool operator==(const D3D12FeedbackBindIdentifier &o) const { - return rootEl == o.rootEl && rangeIndex == o.rangeIndex && descIndex == o.descIndex; + return rootEl == o.rootEl && rangeIndex == o.rangeIndex && descIndex == o.descIndex && + directAccess == o.directAccess && shaderStage == o.shaderStage && bindType == o.bindType; } }; @@ -245,6 +261,7 @@ private: const ShaderBindpointMapping *mappings[(uint32_t)ShaderStage::Count], rdcarray &rootElements); void FillResourceView(D3D12Pipe::View &view, const D3D12Descriptor *desc); + void FillSampler(D3D12Pipe::Sampler &view, const D3D12_SAMPLER_DESC &desc); bool CreateSOBuffers(); void ClearPostVSCache(); diff --git a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp index 4d7a5f67a..e8ad6a694 100644 --- a/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp +++ b/renderdoc/driver/d3d12/d3d12_shader_feedback.cpp @@ -70,7 +70,20 @@ private: }; static const uint32_t numReservedSlots = 4; -static const uint32_t magicFeedbackValue = 0xbeebf33d; +static const uint32_t magicFeedbackValue = 0xbeebf330; +// keep the 4 lower bits to store the type of resource for direct heap access bindless +static const uint32_t magicFeedbackTypeMask = 0x0000000f; + +static D3D12FeedbackKey GetDirectHeapAccessKey() +{ + Bindpoint bind; + bind.bind = -1; + bind.bindset = -1; + D3D12FeedbackKey key; + key.type = DXBCBytecode::OperandType::TYPE_RESOURCE; + key.bind = bind; + return key; +} static bool AnnotateDXBCShader(const DXBC::DXBCContainer *dxbc, uint32_t space, const std::map &slots, @@ -195,16 +208,81 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, const DXIL::Type *handleType = editor.GetTypeByName("dx.types.Handle"); const DXIL::Function *createHandle = editor.GetFunctionByName("dx.op.createHandle"); + const DXIL::Function *createHandleFromBinding = + editor.GetFunctionByName("dx.op.createHandleFromBinding"); + const DXIL::Function *createHandleFromHeap = + editor.GetFunctionByName("dx.op.createHandleFromHeap"); + const DXIL::Function *annotateHandle = editor.GetFunctionByName("dx.op.annotateHandle"); + bool isShaderModel6_6OrAbove = + dxbc->m_Version.Major > 6 || (dxbc->m_Version.Major == 6 && dxbc->m_Version.Minor >= 6); // if we don't have the handle type then this shader can't use any dynamically! we have no // feedback to get - if(!handleType || !createHandle) + if(!handleType || (!createHandle && !isShaderModel6_6OrAbove) || + (isShaderModel6_6OrAbove && !createHandleFromHeap && !createHandleFromBinding)) return false; const DXIL::Type *i32 = editor.GetInt32Type(); const DXIL::Type *i8 = editor.GetInt8Type(); const DXIL::Type *i1 = editor.GetBoolType(); + // Create createHandleFromBinding we'll need to create the feedback UAV + if(!createHandleFromBinding && isShaderModel6_6OrAbove) + { + const DXIL::Type *resBindType = editor.GetTypeByName("dx.types.ResBind"); + if(!resBindType) + { + DXIL::Type resBindTypeTmp; + resBindTypeTmp.type = DXIL::Type::Struct; + resBindTypeTmp.scalarType = DXIL::Type::Void; + resBindTypeTmp.name = "dx.types.ResBind"; + resBindTypeTmp.members = {i32, i32, i32, i8}; + resBindType = editor.AddType(resBindTypeTmp); + } + + const DXIL::Type *funcType = NULL; + for(const DXIL::Type &type : editor.GetTypes()) + { + if(type.type == DXIL::Type::Function && type.inner == handleType && + type.members.size() == 4 && type.members[0] == i32 && type.members[1] == resBindType && + type.members[2] == i32 && type.members[3] == i1) + { + funcType = &type; + break; + } + } + + if(!funcType) + { + DXIL::Type funcTypeTmp; + funcTypeTmp.type = DXIL::Type::Function; + funcTypeTmp.inner = handleType; + funcTypeTmp.members = {i32, resBindType, i32, i1}; + funcType = editor.AddType(funcTypeTmp); + } + DXIL::Type funcPtrType; + funcPtrType.type = DXIL::Type::Pointer; + funcPtrType.inner = funcType; + + DXIL::Function createHandleBaseFunction; + createHandleBaseFunction.name = "dx.op.createHandleFromBinding"; + createHandleBaseFunction.funcType = editor.AddType(funcPtrType); + createHandleBaseFunction.external = true; + + for(const DXIL::AttributeSet &attrs : editor.GetAttributeSets()) + { + if(attrs.functionSlot && attrs.functionSlot->params == DXIL::Attribute::NoUnwind) + { + createHandleBaseFunction.attrs = &attrs; + break; + } + } + + if(!createHandleBaseFunction.attrs) + RDCWARN("Couldn't find existing nounwind attr set"); + createHandleFromBinding = editor.DeclareFunction(createHandleBaseFunction); + } + // get the atomic function we'll need const DXIL::Function *atomicBinOp = editor.GetFunctionByName("dx.op.atomicBinOp.i32"); if(!atomicBinOp) @@ -280,7 +358,15 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, } DXIL::Metadata *resources = editor.GetMetadataByName("dx.resources"); - + if(!resources) + { + DXIL::Metadata tmpResList; + tmpResList.children.resize(4); + DXIL::NamedMetadata tmpResources; + tmpResources.name = "dx.resources"; + tmpResources.children.push_back(editor.AddMetadata(tmpResList)); + resources = editor.AddNamedMetadata(tmpResources); + } // if there are no resources declared we can't have any dynamic indexing if(!resources) return false; @@ -575,9 +661,14 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, return false; } + // One ( and only one ) of createHandle or createHandleFromBinding should be defined + RDCASSERTNOTEQUAL(createHandle == NULL, createHandleFromBinding == NULL); + int startInst = 0; // create our handle first thing - DXIL::Instruction *handle; + DXIL::Instruction *handle = NULL; + if(createHandle) { + RDCASSERT(!isShaderModel6_6OrAbove); DXIL::Instruction inst; inst.op = DXIL::Operation::Call; inst.type = handleType; @@ -595,7 +686,64 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i1, 0U))), }; - handle = editor.AddInstruction(f, 0, inst); + handle = editor.AddInstruction(f, startInst++, inst); + } + else if(createHandleFromBinding) + { + RDCASSERT(isShaderModel6_6OrAbove); + const DXIL::Type *resBindType = editor.GetTypeByName("dx.types.ResBind"); + DXIL::Constant resBindConstant(resBindType, 0U); + resBindConstant.members = { + // Lower id bound + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0))), + // Upper id bound + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0))), + // Space ID + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, space))), + // kind = UAV + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i8, (uint32_t)DXIL::HandleKind::UAV))), + }; + + DXIL::Instruction inst; + inst.op = DXIL::Operation::Call; + inst.type = handleType; + inst.funcCall = createHandleFromBinding; + inst.args = { + // dx.op.createHandleFromBinding opcode + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 217U))), + // resBind + DXIL::Value(editor.GetOrAddConstant(f, resBindConstant)), + // ID/slot + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0U))), + // non-uniform + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i1, 0U))), + }; + + handle = editor.AddInstruction(f, startInst++, inst); + + const DXIL::Type *resPropsType = editor.GetTypeByName("dx.types.ResourceProperties"); + DXIL::Constant resPropConstant(resPropsType, 0U); + resPropConstant.members = { + // IsUav : (1 << 12) + DXIL::Value(editor.GetOrAddConstant( + f, DXIL::Constant(i32, (1 << 12) | (uint32_t)DXIL::ResourceKind::RawBuffer))), + // + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 0))), + }; + + // Annotate handle + DXIL::Instruction inst2; + inst2.op = DXIL::Operation::Call; + inst2.type = handleType; + inst2.funcCall = editor.GetFunctionByName("dx.op.annotateHandle"); + inst2.args = {// dx.op.annotateHandle opcode + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 216U))), + // Resource handle + DXIL::Value(handle), + // Resource properties + DXIL::Value(editor.GetOrAddConstant(f, resPropConstant))}; + + handle = editor.AddInstruction(f, startInst++, inst2); } const DXIL::Constant *undefi32; @@ -629,41 +777,102 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, magicFeedbackValue))), }; - editor.AddInstruction(f, 1, inst); + editor.AddInstruction(f, startInst++, inst); } - for(size_t i = 2; i < f->instructions.size(); i++) + for(size_t i = startInst; i < f->instructions.size(); i++) { const DXIL::Instruction &inst = f->instructions[i]; // we want to annotate any calls to createHandle - if(inst.op == DXIL::Operation::Call && inst.funcCall->name == createHandle->name) + if(inst.op == DXIL::Operation::Call && + ((createHandle && inst.funcCall->name == createHandle->name) || + (createHandleFromBinding && inst.funcCall->name == createHandleFromBinding->name))) { - if(inst.args.size() != 5) - { - RDCERR("Unexpected number of arguments to createHandle"); - continue; - } - - DXIL::Value kindArg = inst.args[1]; - DXIL::Value idArg = inst.args[2]; - DXIL::Value idxArg = inst.args[3]; - - if(kindArg.type != DXIL::ValueType::Constant || idArg.type != DXIL::ValueType::Constant) - { - RDCERR("Unexpected non-constant argument to createHandle"); - continue; - } - - DXIL::HandleKind kind = (DXIL::HandleKind)kindArg.constant->val.u32v[0]; - uint32_t id = idArg.constant->val.u32v[0]; - + DXIL::Value idxArg; rdcpair slotInfo = {0, 0}; + if((createHandle && inst.funcCall->name == createHandle->name)) + { + RDCASSERT(!isShaderModel6_6OrAbove); + if(inst.args.size() != 5) + { + RDCERR("Unexpected number of arguments to createHandle"); + continue; + } - if(kind == DXIL::HandleKind::SRV && id < srvBaseSlots.size()) - slotInfo = srvBaseSlots[id]; - else if(kind == DXIL::HandleKind::UAV && id < uavBaseSlots.size()) - slotInfo = uavBaseSlots[id]; + DXIL::Value kindArg = inst.args[1]; + DXIL::Value idArg = inst.args[2]; + idxArg = inst.args[3]; + if(kindArg.type != DXIL::ValueType::Constant || idArg.type != DXIL::ValueType::Constant) + { + RDCERR("Unexpected non-constant argument to createHandle"); + continue; + } + DXIL::HandleKind kind = (DXIL::HandleKind)kindArg.constant->val.u32v[0]; + uint32_t id = idArg.constant->val.u32v[0]; + if(kind == DXIL::HandleKind::SRV && id < srvBaseSlots.size()) + slotInfo = srvBaseSlots[id]; + else if(kind == DXIL::HandleKind::UAV && id < uavBaseSlots.size()) + slotInfo = uavBaseSlots[id]; + } + else + { + RDCASSERT(isShaderModel6_6OrAbove); + if(inst.args.size() != 4) + { + RDCERR("Unexpected number of arguments to createHandleFromBinding"); + continue; + } + DXIL::Value resBindArg = inst.args[1]; + idxArg = inst.args[2]; + if(resBindArg.type != DXIL::ValueType::Constant) + { + RDCERR("Unexpected non-constant argument to createHandleFromBinding"); + continue; + } + if(resBindArg.constant->members.size() != 4 && !resBindArg.constant->nullconst) + { + RDCERR("Unexpected number of members to resBind"); + continue; + } + + D3D12FeedbackKey key; + if(resBindArg.constant->nullconst) + { + key.type = DXBCBytecode::TYPE_RESOURCE; + key.bind.bindset = 0; + key.bind.bind = 0; + } + else + { + DXIL::Value regArg = resBindArg.constant->members[0]; + DXIL::Value spaceArg = resBindArg.constant->members[2]; + DXIL::Value kindArg = resBindArg.constant->members[3]; + if(regArg.type != DXIL::ValueType::Constant || + spaceArg.type != DXIL::ValueType::Constant || kindArg.type != DXIL::ValueType::Constant) + { + RDCERR("Unexpected non-constant argument to createHandleFromBinding"); + continue; + } + + DXIL::HandleKind kind = (DXIL::HandleKind)kindArg.constant->val.u32v[0]; + if(kind != DXIL::HandleKind::SRV && kind != DXIL::HandleKind::UAV) + continue; + key.type = kind == DXIL::HandleKind::UAV ? DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW + : DXBCBytecode::TYPE_RESOURCE; + key.bind.bindset = spaceArg.constant->val.u32v[0]; + key.bind.bind = regArg.constant->val.u32v[0]; + } + + auto it = slots.find(key); + // not annotated + if(it == slots.end()) + continue; + // static used i.e. not arrayed? ignore + if(it->second.StaticUsed()) + continue; + slotInfo = {it->second.Slot(), key.bind.bind}; + } if(slotInfo.first == 0) continue; @@ -724,6 +933,148 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, magicFeedbackValue))), }; + // don't care about the return value from this + editor.AddInstruction(f, i, op); + i++; + } + else if(inst.op == DXIL::Operation::Call && createHandleFromHeap && + inst.funcCall->name == createHandleFromHeap->name) + { + RDCASSERT(isShaderModel6_6OrAbove); + if(inst.args.size() != 4) + { + RDCERR("Unexpected number of arguments to createHandleFromHeap"); + continue; + } + DXIL::Value isSamplerArg = inst.args[2]; + if(isSamplerArg.type != DXIL::ValueType::Constant) + { + RDCERR("Unexpected non-constant argument to createHandleFromHeap"); + continue; + } + bool isSampler = isSamplerArg.constant->val.u32v[0] != 0; + + D3D12FeedbackKey key = GetDirectHeapAccessKey(); + auto it = slots.find(key); + if(it == slots.end()) + continue; + + // Look for annotation for the type of this view ( SRV/UAV/CBV/Sampler ) + const DXIL::Instruction *annotateInst = NULL; + for(size_t nextInstIndex = i + 1; nextInstIndex < f->instructions.size(); nextInstIndex++) + { + const DXIL::Instruction &nextInst = f->instructions[nextInstIndex]; + if(nextInst.op == DXIL::Operation::Call && annotateHandle && + nextInst.funcCall->name == annotateHandle->name) + { + if(nextInst.args.size() != 3) + { + RDCERR("Unexpected number of arguments to annotateHandle"); + continue; + } + DXIL::Value idxArg = nextInst.args[1]; + DXIL::Value instValue(&inst); + if(idxArg.instruction->resultID == instValue.instruction->resultID) + { + annotateInst = &nextInst; + break; + } + } + } + if(annotateInst == NULL) + { + RDCERR("Unexpected, could not find annotateHandle for createHandleFromHeap"); + continue; + } + + DXIL::Value resPropArg = annotateInst->args[2]; + if(resPropArg.type != DXIL::ValueType::Constant) + { + RDCERR("Unexpected non-constant argument for dx.types.ResourceProperties"); + continue; + } + if(resPropArg.constant->members.size() != 2) + { + RDCERR("Unexpected number of arguments to dx.types.ResourceProperties"); + continue; + } + DXIL::Value resKindArg = resPropArg.constant->members[0]; + if(resKindArg.type != DXIL::ValueType::Constant) + { + RDCERR("Unexpected non-constant argument for dx.types.ResourceProperties's resource kind"); + continue; + } + DXIL::HandleKind handleKind = DXIL::HandleKind::SRV; + DXIL::ResourceKind resKind = (DXIL::ResourceKind)(resKindArg.constant->val.u32v[0] & 0xFF); + bool isUav = (resKindArg.constant->val.u32v[0] & (1 << 12)) != 0; + if(resKind == DXIL::ResourceKind::Sampler || resKind == DXIL::ResourceKind::SamplerComparison) + { + handleKind = DXIL::HandleKind::Sampler; + RDCASSERT(isSampler); + RDCASSERT(!isUav); + } + else if(resKind == DXIL::ResourceKind::CBuffer) + { + handleKind = DXIL::HandleKind::CBuffer; + RDCASSERT(!isSampler); + RDCASSERT(!isUav); + } + else if(isUav) + { + handleKind = DXIL::HandleKind::UAV; + RDCASSERT(!isSampler); + } + else + { + handleKind = DXIL::HandleKind::SRV; + RDCASSERT(!isSampler); + } + + DXIL::Value idxArg = inst.args[1]; + DXIL::Instruction op; + + op.op = DXIL::Operation::Add; + op.type = i32; + op.args = { + // idx to the createHandleFromHeap op + DXIL::Value(idxArg), + // base slot + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, it->second.Slot()))), + }; + // slotPlusBase = idx0Based + slot + DXIL::Instruction *slotPlusBase = editor.AddInstruction(f, i, op); + i++; + + op.op = DXIL::Operation::ShiftLeft; + op.type = i32; + op.args = { + DXIL::Value(slotPlusBase), DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 2U))), + }; + // byteOffset = slotPlusBase << 2 + DXIL::Instruction *byteOffset = editor.AddInstruction(f, i, op); + i++; + + uint32_t feedbackValue = magicFeedbackValue | (1 << (uint32_t)handleKind); + op.op = DXIL::Operation::Call; + op.type = i32; + op.funcCall = atomicBinOp; + op.args = { + // dx.op.atomicBinOp.i32 opcode + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 78U))), + // feedback UAV handle + DXIL::Value(handle), + // operation OR + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, 2U))), + // offset + DXIL::Value(byteOffset), + // offset 2 + DXIL::Value(undefi32), + // offset 3 + DXIL::Value(undefi32), + // value + DXIL::Value(editor.GetOrAddConstant(f, DXIL::Constant(i32, feedbackValue))), + }; + // don't care about the return value from this editor.AddInstruction(f, i, op); i++; @@ -736,7 +1087,7 @@ static bool AnnotateDXILShader(const DXBC::DXBCContainer *dxbc, uint32_t space, static void AddArraySlots(WrappedID3D12PipelineState::ShaderEntry *shad, uint32_t space, uint32_t maxDescriptors, std::map &slots, uint32_t &numSlots, - bytebuf &editedBlob, D3D12_SHADER_BYTECODE &desc) + bytebuf &editedBlob, D3D12_SHADER_BYTECODE &desc, bool directHeapAccess) { if(!shad) return; @@ -798,6 +1149,21 @@ static void AddArraySlots(WrappedID3D12PipelineState::ShaderEntry *shad, uint32_ } } + if(shad->GetDXBC()->m_Version.Major < 6 || + (shad->GetDXBC()->m_Version.Major == 6 && shad->GetDXBC()->m_Version.Minor < 6)) + { + directHeapAccess = false; + } + if(directHeapAccess) + directHeapAccess = shad->GetDXBC()->GetDXILByteCode()->GetDirectHeapAcessCount() > 0; + + if(directHeapAccess) + { + D3D12FeedbackKey key = GetDirectHeapAccessKey(); + slots[key].SetSlot(numSlots); + numSlots += maxDescriptors; + } + // if we haven't encountered any array slots, no need to do any patching if(numSlots == numReservedSlots) return; @@ -888,7 +1254,7 @@ void D3D12Replay::FetchShaderFeedback(uint32_t eventId) return; } - bytebuf editedBlob[5]; + bytebuf editedBlob[(uint32_t)ShaderStage::Count]; D3D12_EXPANDED_PIPELINE_STATE_STREAM_DESC pipeDesc; pipe->Fill(pipeDesc); @@ -898,17 +1264,14 @@ void D3D12Replay::FetchShaderFeedback(uint32_t eventId) uint32_t maxDescriptors = 0; for(ResourceId id : rs.heaps) { - D3D12_DESCRIPTOR_HEAP_DESC desc = rm->GetCurrentAs(id)->GetDesc(); - - if(desc.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) - { - maxDescriptors = desc.NumDescriptors; - RDCDEBUG("Clamping any unbounded ranges to %u descriptors", maxDescriptors); - break; - } + WrappedID3D12DescriptorHeap *heap = + (WrappedID3D12DescriptorHeap *)rm->GetCurrentAs(id); + D3D12_DESCRIPTOR_HEAP_DESC desc = heap->GetDesc(); + maxDescriptors = RDCMAX(maxDescriptors, desc.NumDescriptors); } + RDCDEBUG("Clamping any unbounded ranges to %u descriptors", maxDescriptors); - std::map slots[6]; + std::map slots[(uint32_t)ShaderStage::Count]; // reserve the first 4 dwords for debug info and a validity flag uint32_t numSlots = numReservedSlots; @@ -925,10 +1288,13 @@ void D3D12Replay::FetchShaderFeedback(uint32_t eventId) return; modsig = ((WrappedID3D12RootSignature *)sig)->sig; - + bool directHeapAccess = + (modsig.Flags & (D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | + D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED)) != 0; space = modsig.maxSpaceIndex; - AddArraySlots(pipe->CS(), space, maxDescriptors, slots[0], numSlots, editedBlob[0], pipeDesc.CS); + AddArraySlots(pipe->CS(), space, maxDescriptors, slots[(uint32_t)ShaderStage::Compute], numSlots, + editedBlob[(uint32_t)ShaderStage::Compute], pipeDesc.CS, directHeapAccess); } else { @@ -938,14 +1304,22 @@ void D3D12Replay::FetchShaderFeedback(uint32_t eventId) return; modsig = ((WrappedID3D12RootSignature *)sig)->sig; + bool directHeapAccess = + (modsig.Flags & (D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | + D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED)) != 0; space = modsig.maxSpaceIndex; - AddArraySlots(pipe->VS(), space, maxDescriptors, slots[0], numSlots, editedBlob[0], pipeDesc.VS); - AddArraySlots(pipe->HS(), space, maxDescriptors, slots[1], numSlots, editedBlob[1], pipeDesc.HS); - AddArraySlots(pipe->DS(), space, maxDescriptors, slots[2], numSlots, editedBlob[2], pipeDesc.DS); - AddArraySlots(pipe->GS(), space, maxDescriptors, slots[3], numSlots, editedBlob[3], pipeDesc.GS); - AddArraySlots(pipe->PS(), space, maxDescriptors, slots[4], numSlots, editedBlob[4], pipeDesc.PS); + AddArraySlots(pipe->VS(), space, maxDescriptors, slots[(uint32_t)ShaderStage::Vertex], numSlots, + editedBlob[uint32_t(ShaderStage::Vertex)], pipeDesc.VS, directHeapAccess); + AddArraySlots(pipe->HS(), space, maxDescriptors, slots[(uint32_t)ShaderStage::Hull], numSlots, + editedBlob[uint32_t(ShaderStage::Hull)], pipeDesc.HS, directHeapAccess); + AddArraySlots(pipe->DS(), space, maxDescriptors, slots[(uint32_t)ShaderStage::Domain], numSlots, + editedBlob[uint32_t(ShaderStage::Domain)], pipeDesc.DS, directHeapAccess); + AddArraySlots(pipe->GS(), space, maxDescriptors, slots[(uint32_t)ShaderStage::Geometry], numSlots, + editedBlob[uint32_t(ShaderStage::Geometry)], pipeDesc.GS, directHeapAccess); + AddArraySlots(pipe->PS(), space, maxDescriptors, slots[(uint32_t)ShaderStage::Pixel], numSlots, + editedBlob[uint32_t(ShaderStage::Pixel)], pipeDesc.PS, directHeapAccess); } // if numSlots wasn't increased, none of the resources were arrayed so we have nothing to do. @@ -1139,12 +1513,20 @@ void D3D12Replay::FetchShaderFeedback(uint32_t eventId) // see which shader's binds we should look up for this range switch(p.ShaderVisibility) { - case D3D12_SHADER_VISIBILITY_ALL: visMask = result.compute ? 0x1 : 0xff; break; - case D3D12_SHADER_VISIBILITY_VERTEX: visMask = 1 << 0; break; - case D3D12_SHADER_VISIBILITY_HULL: visMask = 1 << 1; break; - case D3D12_SHADER_VISIBILITY_DOMAIN: visMask = 1 << 2; break; - case D3D12_SHADER_VISIBILITY_GEOMETRY: visMask = 1 << 3; break; - case D3D12_SHADER_VISIBILITY_PIXEL: visMask = 1 << 4; break; + case D3D12_SHADER_VISIBILITY_ALL: + visMask = result.compute ? (uint32_t)ShaderStageMask::Compute : 0xff; + break; + case D3D12_SHADER_VISIBILITY_VERTEX: + visMask = (uint32_t)ShaderStageMask::Vertex; + break; + case D3D12_SHADER_VISIBILITY_HULL: visMask = (uint32_t)ShaderStageMask::Hull; break; + case D3D12_SHADER_VISIBILITY_DOMAIN: + visMask = (uint32_t)ShaderStageMask::Domain; + break; + case D3D12_SHADER_VISIBILITY_GEOMETRY: + visMask = (uint32_t)ShaderStageMask::Geometry; + break; + case D3D12_SHADER_VISIBILITY_PIXEL: visMask = uint32_t(ShaderStageMask::Pixel); break; default: RDCERR("Unexpected shader visibility %d", p.ShaderVisibility); return; } @@ -1154,7 +1536,7 @@ void D3D12Replay::FetchShaderFeedback(uint32_t eventId) else if(range.RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_UAV) curKey.type = DXBCBytecode::TYPE_UNORDERED_ACCESS_VIEW; - for(uint32_t st = 0; st < 5; st++) + for(uint32_t st = 0; st < (uint32_t)ShaderStage::Count; st++) { if(visMask & (1 << st)) { @@ -1214,6 +1596,51 @@ void D3D12Replay::FetchShaderFeedback(uint32_t eventId) } } + D3D12FeedbackKey directAccessKey = GetDirectHeapAccessKey(); + for(uint32_t shaderStage = 0; shaderStage < (uint32_t)ShaderStage::Count; shaderStage++) + { + if(slots[shaderStage].find(directAccessKey) == slots[shaderStage].end()) + continue; + D3D12FeedbackSlot &feedbackSlots = slots[shaderStage].at(directAccessKey); + for(uint32_t i = feedbackSlots.Slot(); i < feedbackSlots.Slot() + maxDescriptors; ++i) + { + if((slotsData[i] & magicFeedbackValue) == magicFeedbackValue) + { + uint32_t usedSlot = i - feedbackSlots.Slot(); + D3D12FeedbackBindIdentifier directAccessIdentifier = {}; + directAccessIdentifier.descIndex = usedSlot; + directAccessIdentifier.rootEl = ~0U; + directAccessIdentifier.rangeIndex = ~0U; + directAccessIdentifier.directAccess = true; + directAccessIdentifier.shaderStage = (ShaderStage)shaderStage; + uint32_t handleKind = slotsData[i] & magicFeedbackTypeMask; + if((handleKind & (1 << (uint32_t)DXIL::HandleKind::Sampler)) != 0) + { + directAccessIdentifier.bindType = BindType::Sampler; + result.used.push_back(directAccessIdentifier); + } + bool isCBV = (handleKind & (1 << (uint32_t)DXIL::HandleKind::CBuffer)) != 0; + bool isSRV = (handleKind & (1 << (uint32_t)DXIL::HandleKind::SRV)) != 0; + bool isUAV = (handleKind & (1 << (uint32_t)DXIL::HandleKind::UAV)) != 0; + if(isCBV || isSRV || isUAV) + { + if((isCBV && isSRV) || (isCBV && isUAV) || (isSRV && isUAV)) + { + RDCERR("Unexpected, resource used with multiple incompatible types"); + continue; + } + if(isCBV) + directAccessIdentifier.bindType = BindType::ConstantBuffer; + else if(isSRV) + directAccessIdentifier.bindType = BindType::ReadOnlyResource; + else if(isUAV) + directAccessIdentifier.bindType = BindType::ReadWriteResource; + result.used.push_back(directAccessIdentifier); + } + } + } + } + std::sort(result.used.begin(), result.used.end()); } else diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp index f11bf98ed..0b150807e 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.cpp @@ -1300,6 +1300,8 @@ Program::Program(const byte *bytes, size_t length) if(!inst.type->isVoid()) m_Values.push_back(Value(&f.instructions.back())); + if(inst.funcCall->name == "dx.op.createHandleFromHeap") + m_directHeapAccessCount++; } else if(op.type == FunctionRecord::INST_CAST) { diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode.h b/renderdoc/driver/shaders/dxil/dxil_bytecode.h index 7f298c113..f5f559ef6 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode.h @@ -673,6 +673,7 @@ public: const Metadata *GetMetadataByName(const rdcstr &name) const; size_t GetMetadataCount() const { return m_Metadata.size() + m_NamedMeta.size(); } + uint32_t GetDirectHeapAcessCount() const { return m_directHeapAccessCount; } protected: void MakeDisassemblyString(); @@ -703,6 +704,7 @@ protected: rdcarray m_Aliases; rdcarray m_Values; rdcarray m_Sections; + uint32_t m_directHeapAccessCount = 0; rdcarray m_Kinds; diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp index 2ca517a21..3115d27c6 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.cpp @@ -722,6 +722,34 @@ Metadata *ProgramEditor::AddMetadata(const Metadata &m) return &m_Metadata.back(); } +NamedMetadata *ProgramEditor::AddNamedMetadata(const NamedMetadata &m) +{ + if(m.dwarf || m.debugLoc) + { + RDCERR("Metadata with debug information is not supported"); + return NULL; + } + + // check that inner pointers are valid + if(m.type && !IN_ARRAY(m_Types, m.type) && !IN_ARRAY(m_OldTypes, m.type)) + { + RDCERR("Metadata references invalid type"); + return NULL; + } + + for(const Metadata *c : m.children) + { + if(c && !IN_ARRAY(m_Metadata, c) && !IN_ARRAY(m_OldMetadata, c)) + { + RDCERR("New metadata references invalid member metadata"); + return NULL; + } + } + + m_NamedMeta.push_back(m); + return &m_NamedMeta.back(); +} + const Constant *ProgramEditor::GetOrAddConstant(const Constant &c) { // check that inner pointers are valid @@ -1974,28 +2002,40 @@ void ProgramEditor::RegisterUAV(DXILResourceType type, uint32_t space, uint32_t if(cur >= end) return; - uint32_t *resourceBindSize = (uint32_t *)cur; - cur += sizeof(uint32_t); - if(cur >= end) - return; - - // fortunately UAVs are the last entry so we don't need to walk the list to insert in the right - // place, we can just add it at the end - cur += (*resourceBindSize) * (*numResources); - if(cur >= end) - return; - - // add an extra resource - (*numResources)++; - - if(*resourceBindSize == sizeof(ResourceBind1) || *resourceBindSize == sizeof(ResourceBind0)) + if(*numResources > 0) { - psv0blob.insert(cur - begin, (byte *)&bind, *resourceBindSize); + uint32_t *resourceBindSize = (uint32_t *)cur; + cur += sizeof(uint32_t); + if(cur >= end) + return; + + // fortunately UAVs are the last entry so we don't need to walk the list to insert in the + // right place, we can just add it at the end + cur += (*resourceBindSize) * (*numResources); + if(cur >= end) + return; + + // add an extra resource + (*numResources)++; + + if(*resourceBindSize == sizeof(ResourceBind1) || *resourceBindSize == sizeof(ResourceBind0)) + { + psv0blob.insert(cur - begin, (byte *)&bind, *resourceBindSize); + } + else + { + RDCERR("Unexpected resource bind size %u", *resourceBindSize); + return; + } } else { - RDCERR("Unexpected resource bind size %u", *resourceBindSize); - return; + // If there is no resource in the chunk we also need to insert the size of a resource bind + *numResources = 1; + size_t insertOffset = cur - begin; + uint32_t resourceBindSize = sizeof(ResourceBind1); + psv0blob.insert(insertOffset, (byte *)&resourceBindSize, sizeof(resourceBindSize)); + psv0blob.insert(insertOffset + sizeof(resourceBindSize), (byte *)&bind, resourceBindSize); } DXBC::DXBCContainer::ReplaceChunk(m_OutBlob, DXBC::FOURCC_PSV0, psv0blob); diff --git a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h index da5ccf34d..e466f5714 100644 --- a/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h +++ b/renderdoc/driver/shaders/dxil/dxil_bytecode_editor.h @@ -76,6 +76,7 @@ public: const rdcarray &GetAttributeSets() { return m_AttributeSets; } // find existing type or metadata by name, returns NULL if not found + const rdcarray &GetTypes() const { return m_Types; } const Type *GetTypeByName(const rdcstr &name); Function *GetFunctionByName(const rdcstr &name); Metadata *GetMetadataByName(const rdcstr &name); @@ -85,6 +86,7 @@ public: const Type *AddType(const Type &t); const Function *DeclareFunction(const Function &f); Metadata *AddMetadata(const Metadata &m); + NamedMetadata *AddNamedMetadata(const NamedMetadata &m); // I think constants have to be unique, so this will return an existing constant (for simple cases // like integers or NULL) if it exists