diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.h b/renderdoc/driver/shaders/spirv/spirv_debug.h index c3da05f4d..c9844da1b 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.h +++ b/renderdoc/driver/shaders/spirv/spirv_debug.h @@ -36,6 +36,9 @@ public: virtual ~DebugAPIWrapper() {} virtual void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d) = 0; + // TODO handle arrays of cbuffers + virtual void ReadConstantBufferValue(uint32_t set, uint32_t bind, uint32_t offset, + uint32_t byteSize, void *dst) = 0; virtual void FillInputValue(ShaderVariable &var, ShaderBuiltin builtin, uint32_t location, uint32_t offset) = 0; }; diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp index 510918bee..bc0d28c4b 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp @@ -26,6 +26,12 @@ #include "common/formatting.h" #include "spirv_op_helpers.h" +static uint32_t VarByteSize(const ShaderVariable &var) +{ + return VarTypeByteSize(var.type) * RDCMAX(1U, (uint32_t)var.rows) * + RDCMAX(1U, (uint32_t)var.columns); +} + namespace rdcspv { void AssignValue(ShaderVariable &dst, const ShaderVariable &src) @@ -83,7 +89,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader for(auto it = constants.begin(); it != constants.end(); it++) active.ids[it->first] = EvaluateConstant(it->first, specInfo); - rdcarray inputIDs, outputIDs; + rdcarray inputIDs, outputIDs, cbufferIDs; // allocate storage for globals with opaque storage classes, and prepare to set up pointers to // them for the global variables themselves @@ -129,6 +135,48 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader outputIDs.push_back(v.id); } } + + // pick up uniform globals, which could be cbuffers + if(v.storage == StorageClass::Uniform && (decorations[v.id].flags & Decorations::BufferBlock) == 0) + { + ShaderVariable var; + var.name = GetRawName(v.id); + + rdcstr sourceName = strings[v.id]; + if(sourceName.empty()) + sourceName = var.name; + + const DataType &type = dataTypes[v.type]; + + // global variables should all be pointers into opaque storage + RDCASSERT(type.type == DataType::PointerType); + + const DataType &innertype = dataTypes[type.InnerType()]; + + if(innertype.type == DataType::ArrayType) + { + RDCERR("uniform Arrays not supported yet"); + } + else if(innertype.type == DataType::StructType) + { + uint32_t offset = 0; + AllocateVariable(decorations[v.id], decorations[v.id], DebugVariableType::Constant, + sourceName, 0, innertype, var); + + global.constantBlocks.push_back(var); + cbufferIDs.push_back(v.id); + + SourceVariableMapping sourceVar; + sourceVar.name = sourceName; + sourceVar.type = VarType::Unknown; + sourceVar.rows = 0; + sourceVar.columns = 0; + sourceVar.offset = 0; + sourceVar.variables.push_back(DebugVariableReference(DebugVariableType::Constant, var.name)); + + sourceVars.push_back(sourceVar); + } + } } // now that the globals are allocated and their storage won't move, we can take pointers to them @@ -136,6 +184,8 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader active.ids[inputIDs[i]] = MakePointerVariable(inputIDs[i], &active.inputs[i]); for(size_t i = 0; i < active.outputs.size(); i++) active.ids[outputIDs[i]] = MakePointerVariable(outputIDs[i], &active.outputs[i]); + for(size_t i = 0; i < global.constantBlocks.size(); i++) + active.ids[cbufferIDs[i]] = MakePointerVariable(cbufferIDs[i], &global.constantBlocks[i]); // only outputs are considered mutable active.live.append(outputIDs); @@ -531,6 +581,60 @@ void Debugger::AllocateVariable(const Decorations &varDecorations, const Decorat (curDecorations.flags & Decorations::HasLocation) ? curDecorations.location : 0, (curDecorations.flags & Decorations::HasOffset) ? curDecorations.offset : 0); } + else if(sourceVarType == DebugVariableType::Constant) + { + uint32_t set = 0, bind = 0; + if(varDecorations.flags & Decorations::HasDescriptorSet) + set = varDecorations.set; + if(varDecorations.flags & Decorations::HasBinding) + bind = varDecorations.binding; + + // non-matrix case is simple, just read the size of the variable + if(sourceVar.rows == 1) + { + apiWrapper->ReadConstantBufferValue(set, bind, offset, VarByteSize(outVar), outVar.value.uv); + } + else + { + // matrix case is more complicated. Either read column by column or row by row depending on + // majorness + uint32_t matrixStride = curDecorations.matrixStride; + + if(!(curDecorations.flags & Decorations::HasMatrixStride)) + { + RDCWARN("Matrix without matrix stride - assuming legacy vec4 packed"); + matrixStride = 16; + } + + if(curDecorations.flags & Decorations::ColMajor) + { + ShaderValue tmp; + + uint32_t colSize = VarTypeByteSize(sourceVar.type) * sourceVar.rows; + for(uint32_t c = 0; c < sourceVar.columns; c++) + { + // read the column + apiWrapper->ReadConstantBufferValue(set, bind, offset + c * matrixStride, colSize, + &tmp.uv[0]); + + // now write it into the appropiate elements in the destination ShaderValue + for(uint32_t r = 0; r < sourceVar.rows; r++) + outVar.value.uv[r * sourceVar.columns + c] = tmp.uv[r]; + } + } + else + { + // row major is easier, read row-by-row directly into the output variable + uint32_t rowSize = VarTypeByteSize(sourceVar.type) * sourceVar.columns; + for(uint32_t r = 0; r < sourceVar.rows; r++) + { + // read the column into the destination ShaderValue, which is tightly packed with rows + apiWrapper->ReadConstantBufferValue(set, bind, offset + r * matrixStride, rowSize, + &outVar.value.uv[r * sourceVar.columns]); + } + } + } + } } void Debugger::PreParse(uint32_t maxId) diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index 1c84e44ef..7d5f97dbc 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -38,6 +38,19 @@ public: m_pDriver->AddDebugMessage(c, sv, src, d); } + virtual void ReadConstantBufferValue(uint32_t set, uint32_t bind, uint32_t offset, + uint32_t byteSize, void *dst) override + { + auto it = cbuffers.find(make_rdcpair(set, bind)); + if(it == cbuffers.end()) + return; + + bytebuf &data = it->second; + + if(offset + byteSize <= data.size()) + memcpy(dst, data.data() + offset, byteSize); + } + virtual void FillInputValue(ShaderVariable &var, ShaderBuiltin builtin, uint32_t location, uint32_t offset) override { @@ -65,6 +78,7 @@ public: RDCERR("Couldn't get input for location=%u, offset=%u", location, offset); } + std::map, bytebuf> cbuffers; std::map builtin_inputs; rdcarray location_inputs; @@ -97,6 +111,42 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u shadRefl.PopulateDisassembly(shader.spirv); VulkanAPIWrapper *apiWrapper = new VulkanAPIWrapper(m_pDriver); + for(uint32_t set = 0; set < state.graphics.descSets.size(); set++) + { + const VulkanStatePipeline::DescriptorAndOffsets &src = state.graphics.descSets[set]; + + const WrappedVulkan::DescriptorSetInfo &setInfo = m_pDriver->m_DescriptorSetState[src.descSet]; + ResourceId layoutId = setInfo.layout; + + uint32_t dynOffsetIdx = 0; + + for(uint32_t bind = 0; bind < setInfo.currentBindings.size(); bind++) + { + DescriptorSetSlot *info = setInfo.currentBindings[bind]; + const DescSetLayout::Binding &layoutBind = c.m_DescSetLayout[layoutId].bindings[bind]; + + if(layoutBind.stageFlags == 0) + continue; + + uint32_t dynOffset = 0; + + if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC || + layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + dynOffset = src.offsets[dynOffsetIdx++]; + + // TODO handle arrays of bindings + const uint32_t arrayIdx = 0; + + if(layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || + layoutBind.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) + { + const DescriptorSetSlotBufferInfo &bufInfo = info[arrayIdx].bufferInfo; + GetDebugManager()->GetBufferData(bufInfo.buffer, bufInfo.offset + dynOffset, bufInfo.range, + apiWrapper->cbuffers[make_rdcpair(set, bind)]); + } + } + } + std::map &builtins = apiWrapper->builtin_inputs; builtins[ShaderBuiltin::BaseInstance] = ShaderVariable(rdcstr(), draw->instanceOffset, 0U, 0U, 0U); builtins[ShaderBuiltin::BaseVertex] = ShaderVariable(