diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 761de7ed8..c4d21b229 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -1661,7 +1661,8 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) if(type == VarType::Unknown) type = ui->intView->isChecked() ? VarType::SInt : VarType::Float; - if(type == VarType::ReadOnlyResource || type == VarType::ReadWriteResource) + if(type == VarType::ReadOnlyResource || type == VarType::ReadWriteResource || + type == VarType::Sampler) { BindpointIndex varBind = var.GetBinding(); @@ -1671,6 +1672,8 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) resList = m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage); else if(type == VarType::ReadWriteResource) resList = m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage); + else if(type == VarType::Sampler) + resList = m_Ctx.CurPipelineState().GetSamplers(m_Stage); int32_t bindIdx = resList.indexOf(Bindpoint(varBind)); @@ -1682,12 +1685,40 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, uint32_t row) if(varBind.arrayIndex >= res.resources.size()) return QString(); + if(type == VarType::Sampler) + return samplerRep(varBind, varBind.arrayIndex, res.resources[varBind.arrayIndex].resourceId); return ToQStr(res.resources[varBind.arrayIndex].resourceId); } return RowString(var, row, type); } +QString ShaderViewer::samplerRep(Bindpoint bind, uint32_t arrayIndex, ResourceId id) +{ + if(id == ResourceId()) + { + QString contents; + if(bind.bindset > 0) + { + // a bit ugly to do an API-specific switch here but we don't have a better way to refer + // by binding + contents = IsD3D(m_Ctx.APIProps().pipelineType) ? tr("space%1, ") : tr("Set %1, "); + contents = contents.arg(bind.bindset); + } + + if(arrayIndex == ~0U) + contents += QString::number(bind.bind); + else + contents += QFormatStr("%1[%2]").arg(bind.bind).arg(arrayIndex); + + return contents; + } + else + { + return ToQStr(id); + } +} + QString ShaderViewer::targetName(const ShaderProcessingTool &disasm) { return lit("%1 (%2)").arg(ToQStr(disasm.output)).arg(disasm.name); @@ -1918,7 +1949,8 @@ bool ShaderViewer::getVar(RDTreeWidgetItem *item, ShaderVariable *var, QString * return false; // don't find resource variables - if(tag.debugVar.type == DebugVariableType::ReadOnlyResource || + if(tag.debugVar.type == DebugVariableType::Sampler || + tag.debugVar.type == DebugVariableType::ReadOnlyResource || tag.debugVar.type == DebugVariableType::ReadWriteResource) return false; @@ -1965,7 +1997,8 @@ bool ShaderViewer::getVar(RDTreeWidgetItem *item, ShaderVariable *var, QString * { // don't find resource variables - if(mapping.variables[0].type == DebugVariableType::ReadOnlyResource || + if(mapping.variables[0].type == DebugVariableType::Sampler || + mapping.variables[0].type == DebugVariableType::ReadOnlyResource || mapping.variables[0].type == DebugVariableType::ReadWriteResource) return false; @@ -2278,9 +2311,9 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(ro.name)) continue; - uint32_t idx = ro.value.u.x; + int32_t idx = m_Mapping->readOnlyResources.indexOf(Bindpoint(ro.GetBinding())); - if(idx >= m_Mapping->readOnlyResources.size()) + if(idx < 0) continue; Bindpoint bind = m_Mapping->readOnlyResources[idx]; @@ -2345,9 +2378,9 @@ void ShaderViewer::updateDebugState() if(varsMapped.contains(rw.name)) continue; - uint32_t idx = rw.value.u.x; + int32_t idx = m_Mapping->readWriteResources.indexOf(Bindpoint(rw.GetBinding())); - if(idx >= m_Mapping->readWriteResources.size()) + if(idx < 0) continue; Bindpoint bind = m_Mapping->readWriteResources[idx]; @@ -2402,6 +2435,73 @@ void ShaderViewer::updateDebugState() ui->constants->addTopLevelItem(node); } } + + rdcarray samplers = m_Ctx.CurPipelineState().GetSamplers(m_Stage); + + for(int i = 0; i < m_Trace->samplers.count(); i++) + { + const ShaderVariable &s = m_Trace->samplers[i]; + + if(varsMapped.contains(s.name)) + continue; + + int32_t idx = m_Mapping->samplers.indexOf(Bindpoint(s.GetBinding())); + + if(idx < 0) + continue; + + Bindpoint bind = m_Mapping->samplers[idx]; + + if(!bind.used) + continue; + + int32_t bindIdx = samplers.indexOf(bind); + + if(bindIdx < 0) + continue; + + BoundResourceArray sampBind = samplers[bindIdx]; + + if(bind.arraySize == 1) + { + RDTreeWidgetItem *node = + new RDTreeWidgetItem({m_ShaderDetails->samplers[i].name, s.name, lit("Sampler"), + samplerRep(bind, ~0U, sampBind.resources[0].resourceId)}); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::Sampler, s.name)))); + ui->constants->addTopLevelItem(node); + } + else if(bind.arraySize == ~0U) + { + RDTreeWidgetItem *node = new RDTreeWidgetItem( + {m_ShaderDetails->samplers[i].name, s.name, lit("[unbounded]"), QString()}); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::Sampler, s.name)))); + ui->constants->addTopLevelItem(node); + } + else + { + RDTreeWidgetItem *node = + new RDTreeWidgetItem({m_ShaderDetails->samplers[i].name, s.name, + QFormatStr("[%1]").arg(bind.arraySize), QString()}); + node->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::Sampler, s.name)))); + + for(uint32_t a = 0; a < bind.arraySize; a++) + { + QString childName = QFormatStr("%1[%2]").arg(s.name).arg(a); + RDTreeWidgetItem *child = new RDTreeWidgetItem({ + QFormatStr("%1[%2]").arg(m_ShaderDetails->samplers[i].name).arg(a), childName, + lit("Sampler"), samplerRep(bind, a, sampBind.resources[a].resourceId), + }); + child->setTag(QVariant::fromValue( + VariableTag(DebugVariableReference(DebugVariableType::Sampler, childName)))); + node->addChild(child); + } + + ui->constants->addTopLevelItem(node); + } + } } { @@ -2780,6 +2880,55 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi regNames += lit("-"); value += lit("?"); } + else if(r.type == DebugVariableType::Sampler) + { + const ShaderVariable *reg = GetDebugVariable(r); + + if(reg == NULL) + continue; + + regNames = r.name; + typeName = lit("Sampler"); + + rdcarray samplers = m_Ctx.CurPipelineState().GetSamplers(m_Stage); + + int32_t idx = m_Mapping->samplers.indexOf(Bindpoint(reg->GetBinding())); + + if(idx < 0) + continue; + + Bindpoint bind = m_Mapping->samplers[idx]; + + int32_t bindIdx = samplers.indexOf(bind); + + if(bindIdx < 0) + continue; + + BoundResourceArray res = samplers[bindIdx]; + + if(bind.arraySize == 1) + { + value = samplerRep(bind, ~0U, res.resources[0].resourceId); + } + else if(bind.arraySize == ~0U) + { + regNames = QString(); + typeName = lit("[unbounded]"); + value = QString(); + } + else + { + for(uint32_t a = 0; a < bind.arraySize; a++) + children.push_back(new RDTreeWidgetItem({ + QFormatStr("%1[%2]").arg(localName).arg(a), QFormatStr("%1[%2]").arg(regNames).arg(a), + typeName, samplerRep(bind, a, res.resources[a].resourceId), + })); + + regNames = QString(); + typeName = QFormatStr("[%1]").arg(bind.arraySize); + value = QString(); + } + } else if(r.type == DebugVariableType::ReadOnlyResource || r.type == DebugVariableType::ReadWriteResource) { @@ -2787,6 +2936,9 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi const ShaderVariable *reg = GetDebugVariable(r); + if(reg == NULL) + continue; + regNames = r.name; typeName = isReadOnlyResource ? lit("Resource") : lit("RW Resource"); @@ -2794,11 +2946,11 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi isReadOnlyResource ? m_Ctx.CurPipelineState().GetReadOnlyResources(m_Stage) : m_Ctx.CurPipelineState().GetReadWriteResources(m_Stage); - uint32_t idx = reg->value.u.x; + int32_t idx = + (isReadOnlyResource ? m_Mapping->readOnlyResources : m_Mapping->readWriteResources) + .indexOf(Bindpoint(reg->GetBinding())); - if(isReadOnlyResource && idx >= m_Mapping->readOnlyResources.size()) - continue; - if(!isReadOnlyResource && idx >= m_Mapping->readWriteResources.size()) + if(idx < 0) continue; Bindpoint bind = isReadOnlyResource ? m_Mapping->readOnlyResources[idx] @@ -2958,6 +3110,14 @@ const ShaderVariable *ShaderViewer::GetDebugVariable(const DebugVariableReferenc return NULL; } + else if(r.type == DebugVariableType::Sampler) + { + for(int i = 0; i < m_Trace->samplers.count(); i++) + if(m_Trace->samplers[i].name == r.name) + return &m_Trace->samplers[i]; + + return NULL; + } else if(r.type == DebugVariableType::Input || r.type == DebugVariableType::Constant || r.type == DebugVariableType::Variable) { diff --git a/qrenderdoc/Windows/ShaderViewer.h b/qrenderdoc/Windows/ShaderViewer.h index 5f7680ce8..476ef5082 100644 --- a/qrenderdoc/Windows/ShaderViewer.h +++ b/qrenderdoc/Windows/ShaderViewer.h @@ -290,6 +290,7 @@ private: void applyForwardsChange(); QString stringRep(const ShaderVariable &var, uint32_t row = 0); + QString samplerRep(Bindpoint bind, uint32_t arrayIndex, ResourceId id); void combineStructures(RDTreeWidgetItem *root, int skipPrefixLength = 0); RDTreeWidgetItem *findVarInTree(RDTreeWidgetItem *root, QString name, bool fullmatch, int maxDepth); void highlightMatchingVars(RDTreeWidgetItem *root, const QString varName, diff --git a/renderdoc/api/replay/pipestate.h b/renderdoc/api/replay/pipestate.h index 28b47e517..742924780 100644 --- a/renderdoc/api/replay/pipestate.h +++ b/renderdoc/api/replay/pipestate.h @@ -305,15 +305,23 @@ For some APIs that don't distinguish by entry point, this may be empty. DOCUMENT(R"(Retrieves the read-only resources bound to a particular shader stage. :param ShaderStage stage: The shader stage to fetch from. -:return: The currently bound read-only resoruces. +:return: The currently bound read-only resources. :rtype: ``list`` of :class:`BoundResourceArray` entries )"); rdcarray GetReadOnlyResources(ShaderStage stage) const; + DOCUMENT(R"(Retrieves the samplers bound to a particular shader stage. + +:param ShaderStage stage: The shader stage to fetch from. +:return: The currently bound sampler resources. +:rtype: ``list`` of :class:`BoundResourceArray` entries +)"); + rdcarray GetSamplers(ShaderStage stage) const; + DOCUMENT(R"(Retrieves the read/write resources bound to a particular shader stage. :param ShaderStage stage: The shader stage to fetch from. -:return: The currently bound read/write resoruces. +:return: The currently bound read/write resources. :rtype: ``list`` of :class:`BoundResourceArray` entries )"); rdcarray GetReadWriteResources(ShaderStage stage) const; diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index 14fab0d22..160a0de5c 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -1015,6 +1015,133 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui return ret; } +rdcarray PipeState::GetSamplers(ShaderStage stage) const +{ + rdcarray ret; + + if(IsCaptureLoaded()) + { + if(IsCaptureD3D11()) + { + const D3D11Pipe::Shader &s = GetD3D11Stage(stage); + + ret.reserve(s.samplers.size()); + + for(int i = 0; i < s.samplers.count(); i++) + { + Bindpoint key(0, i); + BoundResource val; + + val.resourceId = s.samplers[i].resourceId; + + ret.push_back(BoundResourceArray(key, {val})); + } + + return ret; + } + else if(IsCaptureD3D12()) + { + const D3D12Pipe::Shader &s = GetD3D12Stage(stage); + + size_t size = s.bindpointMapping.samplers.size(); + ret.reserve(size); + + for(size_t bp = 0; bp < size; bp++) + { + const Bindpoint &bind = s.bindpointMapping.samplers[bp]; + ret.push_back(BoundResourceArray()); + ret.back().bindPoint = bind; + + uint32_t start = bind.bind; + uint32_t end = (bind.arraySize == ~0U) ? bind.arraySize : bind.bind + bind.arraySize; + + rdcarray &val = ret.back().resources; + + for(size_t i = 0; i < m_D3D12->rootElements.size(); ++i) + { + const D3D12Pipe::RootSignatureRange &element = m_D3D12->rootElements[i]; + if((element.visibility & MaskForStage(stage)) == ShaderStageMask::Unknown) + continue; + + if(element.type == BindType::Sampler && element.registerSpace == (uint32_t)bind.bindset) + { + for(size_t j = 0; j < element.samplers.size(); ++j) + { + const D3D12Pipe::Sampler &samp = element.samplers[j]; + if(samp.bind >= start && samp.bind <= end) + { + val.push_back(BoundResource()); + // no resource ID to add here + } + } + } + } + } + + return ret; + } + else if(IsCaptureGL()) + { + ret.reserve(m_GL->samplers.size()); + + for(int i = 0; i < m_GL->samplers.count(); i++) + { + Bindpoint key(0, i); + BoundResource val; + + val.resourceId = m_GL->samplers[i].resourceId; + + ret.push_back(BoundResourceArray(key, {val})); + } + + return ret; + } + else if(IsCaptureVK()) + { + const rdcarray &descsets = stage == ShaderStage::Compute + ? m_Vulkan->compute.descriptorSets + : m_Vulkan->graphics.descriptorSets; + + ShaderStageMask mask = MaskForStage(stage); + + size_t size = 0; + for(int set = 0; set < descsets.count(); set++) + size += descsets[set].bindings.size(); + + ret.reserve(size); + + for(int set = 0; set < descsets.count(); set++) + { + const VKPipe::DescriptorSet &descset = descsets[set]; + for(int slot = 0; slot < descset.bindings.count(); slot++) + { + const VKPipe::DescriptorBinding &bind = descset.bindings[slot]; + if((bind.type == BindType::Sampler || bind.type == BindType::ImageSampler) && + (bind.stageFlags & mask) == mask) + { + ret.push_back(BoundResourceArray()); + ret.back().bindPoint = Bindpoint(set, slot); + + rdcarray &val = ret.back().resources; + val.resize(bind.descriptorCount); + + ret.back().dynamicallyUsedCount = bind.dynamicallyUsedCount; + + for(uint32_t i = 0; i < bind.descriptorCount; i++) + { + val[i].resourceId = bind.binds[i].samplerResourceId; + } + } + } + } + + return ret; + } + } + + return ret; +} + rdcarray PipeState::GetReadOnlyResources(ShaderStage stage) const { rdcarray ret; diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 7c9eef874..4acd30e8e 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -120,6 +120,10 @@ DOCUMENT(R"(Represents the category of debugging variable that a source variable A constant buffer value, stored globally. +.. data:: Sampler + + A sampler, stored globally. + .. data:: ReadOnlyResource A read-only resource, stored globally. @@ -137,6 +141,7 @@ enum class DebugVariableType : uint8_t Undefined, Input, Constant, + Sampler, ReadOnlyResource, ReadWriteResource, Variable, diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index cba8388df..762377ec9 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -750,6 +750,14 @@ other metadata as well as find the binding from the pipeline state. )"); rdcarray readWriteResources; + DOCUMENT(R"(The sampler variables for this shader as a list of :class:`ShaderVariable`. + +The 'value' of the variable is always a single unsigned integer, which is the bindpoint - an index +into the :data:`ShaderBindpointMapping.samplers` list, which can be used to look up the +other metadata as well as find the binding from the pipeline state. +)"); + rdcarray samplers; + DOCUMENT(R"(An optional list of :class:`SourceVariableMapping` indicating which high-level source variables map to which debug variables and includes extra type information. @@ -1357,6 +1365,15 @@ struct Bindpoint used = false; arraySize = 1; } + // construct out of an index, useful for searching bindpoint lists (since we only compare by + // bindset and bind) + Bindpoint(const BindpointIndex &idx) + { + bindset = idx.bindset; + bind = idx.bind; + used = false; + arraySize = 1; + } bool operator<(const Bindpoint &o) const { diff --git a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp index 50754fb82..213dfad8e 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_debug.cpp @@ -5160,7 +5160,8 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC struct ResList { - DebugVariableType varType; + VarType varType; + DebugVariableType debugVarType; const rdcarray &binds; const rdcarray &resources; const char *regChars; @@ -5169,12 +5170,12 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC ResList lists[2] = { { - DebugVariableType::ReadOnlyResource, mapping.readOnlyResources, refl.readOnlyResources, - "tT", ret->readOnlyResources, + VarType::ReadOnlyResource, DebugVariableType::ReadOnlyResource, mapping.readOnlyResources, + refl.readOnlyResources, "tT", ret->readOnlyResources, }, { - DebugVariableType::ReadWriteResource, mapping.readWriteResources, refl.readWriteResources, - "uU", ret->readWriteResources, + VarType::ReadWriteResource, DebugVariableType::ReadWriteResource, + mapping.readWriteResources, refl.readWriteResources, "uU", ret->readWriteResources, }, }; @@ -5197,21 +5198,20 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC else identifier = StringFormat::Fmt("%c%u", list.regChars[0], b.bind); - ShaderVariable reg(identifier, (uint32_t)i, 0U, 0U, 0U); + ShaderVariable reg(identifier, 0U, 0U, 0U, 0U); + reg.rows = 1; reg.columns = 1; + reg.SetBinding(b.bindset, b.bind, 0U); + SourceVariableMapping sourcemap; sourcemap.name = r.name; - sourcemap.type = r.variableType.descriptor.type; - if(sourcemap.type == VarType::Unknown) - sourcemap.type = VarType::UInt; - sourcemap.rows = r.variableType.descriptor.rows; - sourcemap.columns = r.variableType.descriptor.columns; - if(sourcemap.rows == 0 && sourcemap.columns == 0) - sourcemap.rows = sourcemap.columns = 1; + sourcemap.type = VarType::Sampler; + sourcemap.rows = 1; + sourcemap.columns = 1; sourcemap.offset = 0; DebugVariableReference ref; - ref.type = list.varType; + ref.type = list.debugVarType; ref.name = reg.name; sourcemap.variables.push_back(ref); @@ -5220,6 +5220,43 @@ ShaderDebugTrace *InterpretDebugger::BeginDebug(const DXBC::DXBCContainer *dxbcC } } + ret->samplers.reserve(mapping.samplers.size()); + for(size_t i = 0; i < mapping.samplers.size(); i++) + { + const Bindpoint &b = mapping.samplers[i]; + const ShaderSampler &s = refl.samplers[i]; + + if(!b.used) + continue; + + rdcstr identifier; + + if(dxbc->GetDXBCByteCode()->IsShaderModel51()) + identifier = StringFormat::Fmt("S%zu", i); + else + identifier = StringFormat::Fmt("s%u", b.bind); + + ShaderVariable reg(identifier, 0U, 0U, 0U, 0U); + reg.rows = 1; + reg.columns = 1; + + reg.SetBinding(b.bindset, b.bind, 0U); + + SourceVariableMapping sourcemap; + sourcemap.name = s.name; + sourcemap.type = VarType::Sampler; + sourcemap.rows = 1; + sourcemap.columns = 1; + sourcemap.offset = 0; + DebugVariableReference ref; + ref.type = DebugVariableType::Sampler; + ref.name = reg.name; + sourcemap.variables.push_back(ref); + + ret->sourceVars.push_back(sourcemap); + ret->samplers.push_back(reg); + } + return ret; } diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 983510c2d..724185781 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -401,6 +401,7 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el) SERIALISE_MEMBER(stage); SERIALISE_MEMBER(inputs); SERIALISE_MEMBER(constantBlocks); + SERIALISE_MEMBER(samplers); SERIALISE_MEMBER(readOnlyResources); SERIALISE_MEMBER(readWriteResources); SERIALISE_MEMBER(sourceVars); @@ -414,7 +415,7 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el) if(ser.IsReading()) el.debugger = (ShaderDebugger *)debugger; - SIZE_CHECK(160); + SIZE_CHECK(184); } template