From f506dcf3952c76c07e7c1df05a4ffb502607770c Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 23 Apr 2020 14:25:58 +0100 Subject: [PATCH] Add support for storage buffer access --- qrenderdoc/Code/QRDUtils.cpp | 2 +- .../driver/shaders/spirv/spirv_debug.cpp | 69 ++- renderdoc/driver/shaders/spirv/spirv_debug.h | 22 +- .../shaders/spirv/spirv_debug_setup.cpp | 440 ++++++++++++++++-- renderdoc/driver/vulkan/vk_shaderdebug.cpp | 131 ++++-- util/test/demos/vk/vk_shader_debug_zoo.cpp | 179 ++++++- util/test/tests/Vulkan/VK_Shader_Debug_Zoo.py | 104 ++--- 7 files changed, 783 insertions(+), 164 deletions(-) diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index 39ee18584..a2e36254c 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -488,7 +488,7 @@ void RichResourceTextPaint(const QWidget *owner, QPainter *painter, QRect rect, } else { - name = QFormatStr("Unknown 0x%1").arg(ptr->val.pointer, 16, 16); + name = QFormatStr("Unknown 0x%1").arg(ptr->val.pointer, 16, 16, QLatin1Char('0')); valid = false; } } diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.cpp b/renderdoc/driver/shaders/spirv/spirv_debug.cpp index f995b2c76..fdca3ef75 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug.cpp @@ -210,9 +210,20 @@ void ThreadState::WritePointerValue(Id pointer, const ShaderVariable &val) // plus any additional ones for other pointers. Id ptrid = debugger.GetPointerBaseId(var); - rdcarray changes; ShaderVariableChange basechange; - basechange.before = debugger.EvaluatePointerVariable(ids[ptrid]); + + if(debugger.IsOpaquePointer(ids[ptrid])) + { + // if this is a write to a SSBO pointer, don't record any alias changes, just record a no-op + // change to this pointer + basechange.after = basechange.before = debugger.GetPointerValue(ids[pointer]); + m_State->changes.push_back(basechange); + debugger.WriteThroughPointer(var, val); + return; + } + + rdcarray changes; + basechange.before = debugger.GetPointerValue(ids[ptrid]); rdcarray &pointers = pointersForId[ptrid]; @@ -220,13 +231,13 @@ void ThreadState::WritePointerValue(Id pointer, const ShaderVariable &val) // for every other pointer, evaluate its value now before for(size_t i = 0; i < pointers.size(); i++) - changes[i].before = debugger.EvaluatePointerVariable(ids[pointers[i]]); + changes[i].before = debugger.GetPointerValue(ids[pointers[i]]); debugger.WriteThroughPointer(var, val); // now evaluate the value after for(size_t i = 0; i < pointers.size(); i++) - changes[i].after = debugger.EvaluatePointerVariable(ids[pointers[i]]); + changes[i].after = debugger.GetPointerValue(ids[pointers[i]]); // if the pointer we're writing is one of the aliased pointers, be sure we add it even if // it's a no-op change @@ -247,7 +258,7 @@ void ThreadState::WritePointerValue(Id pointer, const ShaderVariable &val) // always add a change for the base storage variable written itself, even if that's a no-op. // This one is not included in any of the pointers lists above - basechange.after = debugger.EvaluatePointerVariable(ids[ptrid]); + basechange.after = debugger.GetPointerValue(ids[ptrid]); m_State->changes.push_back(basechange); } } @@ -266,7 +277,7 @@ void ThreadState::SetDst(Id id, const ShaderVariable &val) if(m_State) { ShaderVariableChange change; - change.after = debugger.EvaluatePointerVariable(ids[id]); + change.after = debugger.GetPointerValue(ids[id]); m_State->changes.push_back(change); debugger.AddSourceVars(sourceVars, id); @@ -289,7 +300,7 @@ void ThreadState::ProcessScopeChange(const rdcarray &oldLive, const rdcarray if(liveGlobals.contains(id)) continue; - m_State->changes.push_back({debugger.EvaluatePointerVariable(ids[id])}); + m_State->changes.push_back({debugger.GetPointerValue(ids[id])}); } for(const Id id : newLive) @@ -297,7 +308,7 @@ void ThreadState::ProcessScopeChange(const rdcarray &oldLive, const rdcarray if(liveGlobals.contains(id)) continue; - m_State->changes.push_back({ShaderVariable(), debugger.EvaluatePointerVariable(ids[id])}); + m_State->changes.push_back({ShaderVariable(), debugger.GetPointerValue(ids[id])}); } } @@ -463,7 +474,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray (void)load.memoryAccess; // get the pointer value, evaluate it (i.e. dereference) and store the result - SetDst(load.result, debugger.EvaluatePointerVariable(GetSrc(load.pointer))); + SetDst(load.result, debugger.ReadFromPointer(GetSrc(load.pointer))); break; } @@ -486,7 +497,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray (void)copy.memoryAccess0; (void)copy.memoryAccess1; - WritePointerValue(copy.target, debugger.EvaluatePointerVariable(GetSrc(copy.source))); + WritePointerValue(copy.target, debugger.ReadFromPointer(GetSrc(copy.source))); break; } @@ -506,6 +517,41 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray break; } + case Op::ArrayLength: + { + OpArrayLength len(it); + + ShaderVariable structPointer = GetSrc(len.structure); + + // get the pointer base offset (should be zero for any binding but could be non-zero for a + // buffer_device_address pointer) + uint64_t offset = structPointer.value.u64v[BufferPointerByteOffsetVariableSlot]; + + // add the offset of the member + const DataType &pointerType = debugger.GetTypeForId(len.structure); + const DataType &structType = debugger.GetType(pointerType.InnerType()); + + offset += structType.children[len.arraymember].decorations.offset; + + ShaderVariable result; + result.rows = result.columns = 1; + result.type = VarType::UInt; + + BindpointIndex bind = debugger.GetPointerValue(structPointer).GetBinding(); + + uint64_t byteLen = debugger.GetAPIWrapper()->GetBufferLength(bind) - offset; + + const Decorations &dec = debugger.GetDecorations(structType.children[len.arraymember].type); + + RDCASSERT(dec.flags & Decorations::HasArrayStride); + byteLen /= dec.arrayStride; + + result.value.uv[0] = uint32_t(byteLen); + + SetDst(len.result, result); + + break; + } ////////////////////////////////////////////////////////////////////////////// // @@ -576,7 +622,7 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray debugger.MakeCompositePointer(ids[extract.composite], extract.composite, extract.indexes); // then evaluate it, to get the extracted value - SetDst(extract.result, debugger.EvaluatePointerVariable(ptr)); + SetDst(extract.result, debugger.ReadFromPointer(ptr)); break; } @@ -2490,4 +2536,5 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray m_State = NULL; } + }; // namespace rdcspv diff --git a/renderdoc/driver/shaders/spirv/spirv_debug.h b/renderdoc/driver/shaders/spirv/spirv_debug.h index e0069b790..02eb0dfbf 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug.h +++ b/renderdoc/driver/shaders/spirv/spirv_debug.h @@ -52,9 +52,11 @@ 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 uint64_t GetBufferLength(BindpointIndex bind) = 0; + + virtual void ReadBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, void *dst) = 0; + virtual void WriteBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, + const void *src) = 0; virtual void FillInputValue(ShaderVariable &var, ShaderBuiltin builtin, uint32_t location, uint32_t component) = 0; @@ -91,7 +93,11 @@ public: uint32_t component) = 0; }; +// this could be cleaner if ShaderVariable wasn't a very public struct, but it's not worth it so +// we just reserve value slots that we know won't be used in opaque variables static const uint32_t TextureTypeVariableSlot = 8; +static const uint32_t BufferPointerByteOffsetVariableSlot = 8; +static const uint32_t BufferPointerTypeIdVariableSlot = 9; typedef ShaderVariable (*ExtInstImpl)(ThreadState &, uint32_t, const rdcarray &); @@ -237,16 +243,20 @@ public: uint32_t GetInstructionForFunction(Id id); uint32_t GetInstructionForLabel(Id id); const DataType &GetType(Id typeId); + const DataType &GetTypeForId(Id ssaId); + const Decorations &GetDecorations(Id typeId); rdcstr GetRawName(Id id) const; rdcstr GetHumanName(Id id); void AddSourceVars(rdcarray &sourceVars, Id id); void AllocateVariable(Id id, Id typeId, DebugVariableType sourceVarType, const rdcstr &sourceName, ShaderVariable &outVar); - ShaderVariable EvaluatePointerVariable(const ShaderVariable &v) const; + ShaderVariable ReadFromPointer(const ShaderVariable &v) const; + ShaderVariable GetPointerValue(const ShaderVariable &v) const; ShaderVariable MakePointerVariable(Id id, const ShaderVariable *v, uint32_t scalar0 = ~0U, uint32_t scalar1 = ~0U) const; Id GetPointerBaseId(const ShaderVariable &v) const; + bool IsOpaquePointer(const ShaderVariable &v) const; void WriteThroughPointer(const ShaderVariable &ptr, const ShaderVariable &val); ShaderVariable MakeCompositePointer(const ShaderVariable &base, Id id, rdcarray &indices); @@ -256,6 +266,7 @@ public: const rdcarray &GetLiveGlobals() { return liveGlobals; } const rdcarray &GetGlobalSourceVars() { return globalSourceVars; } ThreadState &GetActiveLane() { return workgroup[activeLaneIndex]; } + const ThreadState &GetActiveLane() const { return workgroup[activeLaneIndex]; } private: virtual void PreParse(uint32_t maxId); virtual void PostParse(); @@ -267,6 +278,9 @@ private: uint32_t ApplyDerivatives(uint32_t quadIndex, const Decorations &curDecorations, uint32_t location, const DataType &inType, ShaderVariable &outVar); + void WalkVariable(const DataType &type, uint64_t byteOffset, ShaderVariable &var, bool initialise, + std::function callback) const; + void AddSourceVars(rdcarray &sourceVars, const DataType &inType, const rdcstr &sourceName, const rdcstr &varName, uint32_t &offset); void MakeSignatureNames(const rdcarray &sigList, rdcarray &sigNames); diff --git a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp index f5cea1da2..8064ded04 100644 --- a/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_debug_setup.cpp @@ -86,6 +86,16 @@ const rdcspv::DataType &Debugger::GetType(Id typeId) return dataTypes[typeId]; } +const rdcspv::DataType &Debugger::GetTypeForId(Id ssaId) +{ + return dataTypes[idTypes[ssaId]]; +} + +const Decorations &Debugger::GetDecorations(Id typeId) +{ + return decorations[typeId]; +} + void Debugger::MakeSignatureNames(const rdcarray &sigList, rdcarray &sigNames) { @@ -462,8 +472,8 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader } // pick up uniform globals, which could be cbuffers, and push constants - else if((v.storage == StorageClass::Uniform || v.storage == StorageClass::PushConstant) && - (decorations[v.id].flags & Decorations::BufferBlock) == 0) + else if(v.storage == StorageClass::Uniform || v.storage == StorageClass::StorageBuffer || + v.storage == StorageClass::PushConstant) { ShaderVariable var; var.name = GetRawName(v.id); @@ -477,6 +487,9 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader const DataType &innertype = dataTypes[type.InnerType()]; + const bool ssbo = (v.storage == StorageClass::StorageBuffer) || + (decorations[innertype.id].flags & Decorations::BufferBlock); + if(innertype.type == DataType::ArrayType) { RDCERR("uniform Arrays not supported yet"); @@ -489,6 +502,9 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader { if(v.storage == StorageClass::PushConstant) sourceName = "_pushconsts"; + else if(ssbo) + sourceName = StringFormat::Fmt("_buffer_set%u_bind%u", decorations[v.id].set, + decorations[v.id].binding); else sourceName = StringFormat::Fmt("_cbuffer_set%u_bind%u", decorations[v.id].set, decorations[v.id].binding); @@ -502,19 +518,47 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *apiWrapper, const Shader d.flags = Decorations::Flags(d.flags | Decorations::HasDescriptorSet); } - uint32_t offset = 0; - AllocateVariable(d, d, 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)); + + if(ssbo) + { + var.rows = 1; + var.columns = 1; + var.type = VarType::ReadWriteResource; + + uint32_t set = 0, bind = 0; + if(decorations[v.id].flags & Decorations::HasDescriptorSet) + set = decorations[v.id].set; + if(decorations[v.id].flags & Decorations::HasBinding) + bind = decorations[v.id].binding; + + // TODO handle arrays + var.SetBinding((int32_t)set, (int32_t)bind, 0U); + + sourceVar.type = VarType::ReadWriteResource; + sourceVar.rows = 1; + sourceVar.columns = 1; + sourceVar.variables.push_back( + DebugVariableReference(DebugVariableType::ReadWriteResource, var.name)); + + global.readWriteResources.push_back(var); + readWriteIDs.push_back(v.id); + } + else + { + uint32_t offset = 0; + AllocateVariable(d, d, DebugVariableType::Constant, sourceName, 0, innertype, var); + + sourceVar.type = VarType::ConstantBlock; + sourceVar.rows = 0; + sourceVar.columns = 0; + sourceVar.variables.push_back(DebugVariableReference(DebugVariableType::Constant, var.name)); + + global.constantBlocks.push_back(var); + cbufferIDs.push_back(v.id); + } globalSourceVars.push_back(sourceVar); } @@ -790,7 +834,7 @@ rdcarray Debugger::ContinueDebug() initial.nextInstruction = active.nextInstruction; for(const Id &v : active.live) - initial.changes.push_back({ShaderVariable(), EvaluatePointerVariable(active.ids[v])}); + initial.changes.push_back({ShaderVariable(), GetPointerValue(active.ids[v])}); initial.sourceVars = active.sourceVars; @@ -847,7 +891,7 @@ rdcarray Debugger::ContinueDebug() { thread.live.erase(l); ShaderVariableChange change; - change.before = EvaluatePointerVariable(thread.ids[id]); + change.before = GetPointerValue(thread.ids[id]); state.changes.push_back(change); rdcstr name = GetRawName(id); @@ -909,11 +953,115 @@ ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id, if(base.type == VarType::GPUPointer) leaf = (const ShaderVariable *)(uintptr_t)base.value.u64v[0]; + if(leaf->type == VarType::ReadWriteResource) + { + ShaderVariable ret = MakePointerVariable(id, leaf); + + uint64_t byteOffset = base.value.u64v[BufferPointerByteOffsetVariableSlot]; + + const DataType *type = &dataTypes[idTypes[id]]; + + RDCASSERT(type->type == DataType::PointerType); + type = &dataTypes[type->InnerType()]; + + // first walk any aggregate types + size_t i = 0; + while(i < indices.size() && + (type->type == DataType::ArrayType || type->type == DataType::StructType)) + { + if(type->type == DataType::ArrayType) + { + // look up the array stride + const Decorations &dec = decorations[type->id]; + RDCASSERT(dec.flags & Decorations::HasArrayStride); + + // offset increases by index * arrayStride + byteOffset += indices[i] * dec.arrayStride; + + // new type is the inner type + type = &dataTypes[type->InnerType()]; + } + else + { + // otherwise it's a struct member + const DataType::Child &child = type->children[indices[i]]; + + // offset increases by member offset + RDCASSERT(child.decorations.flags & Decorations::HasOffset); + byteOffset += child.decorations.offset; + + // new type is the child type + type = &dataTypes[child.type]; + } + i++; + } + + size_t remaining = indices.size() - i; + if(remaining == 2) + { + // pointer to a scalar in a matrix. indices[i] is column, indices[i + 1] is row + + const Decorations &dec = decorations[type->id]; + RDCASSERT(dec.flags & Decorations::HasMatrixStride); + + // type is the resulting scalar (first inner does matrix->colun type, second does column + // type->scalar type) + type = &dataTypes[dataTypes[type->InnerType()].InnerType()]; + + if(dec.flags & Decorations::RowMajor) + { + byteOffset += dec.matrixStride * indices[i + 1] + indices[i] * (type->scalar().width / 8); + } + else + { + byteOffset += dec.matrixStride * indices[i] + indices[i + 1] * (type->scalar().width / 8); + } + } + else if(remaining == 1) + { + if(type->type == DataType::VectorType) + { + // pointer to a scalar in a vector. + + // type is the resulting scalar (first inner does matrix->colun type, second does column + // type->scalar type) + type = &dataTypes[dataTypes[type->InnerType()].InnerType()]; + + byteOffset += indices[i] * (type->scalar().width / 8); + } + else + { + // pointer to a column in a matrix + + const Decorations &dec = decorations[type->id]; + RDCASSERT(dec.flags & Decorations::HasMatrixStride); + + // type is the resulting vector + type = &dataTypes[type->InnerType()]; + + if(dec.flags & Decorations::RowMajor) + { + // TODO need to store dec.matrixStride as the stride between elements for this pointer + // when reading + byteOffset += indices[i] * (type->scalar().width / 8); + } + else + { + byteOffset += dec.matrixStride * indices[i]; + } + } + } + + ret.value.u64v[BufferPointerTypeIdVariableSlot] = type->id.value(); + ret.value.u64v[BufferPointerByteOffsetVariableSlot] = byteOffset; + + return ret; + } + // first walk any struct member/array indices size_t i = 0; - while(!leaf->members.empty()) + while(i < indices.size() && !leaf->members.empty()) { - RDCASSERT(i < indices.size(), i, indices.size()); leaf = &leaf->members[indices[i++]]; } @@ -934,13 +1082,94 @@ ShaderVariable Debugger::MakeCompositePointer(const ShaderVariable &base, Id id, return MakePointerVariable(id, leaf, scalar0, scalar1); } -ShaderVariable Debugger::EvaluatePointerVariable(const ShaderVariable &ptr) const +ShaderVariable Debugger::GetPointerValue(const ShaderVariable &ptr) const +{ + // opaque pointers display as their inner value + if(IsOpaquePointer(ptr)) + { + const ShaderVariable *inner = (const ShaderVariable *)(uintptr_t)ptr.value.u64v[0]; + ShaderVariable ret = *inner; + ret.name = ptr.name; + return ret; + } + + // every other kind of pointer displays as its contents + return ReadFromPointer(ptr); +} + +ShaderVariable Debugger::ReadFromPointer(const ShaderVariable &ptr) const { if(ptr.type != VarType::GPUPointer) return ptr; + const ShaderVariable *inner = (const ShaderVariable *)(uintptr_t)ptr.value.u64v[0]; + ShaderVariable ret; - ret = *(const ShaderVariable *)(uintptr_t)ptr.value.u64v[0]; + + if(inner->type == VarType::ReadWriteResource) + { + rdcspv::Id typeId = + rdcspv::Id::fromWord(uint32_t(ptr.value.u64v[BufferPointerTypeIdVariableSlot])); + uint64_t byteOffset = ptr.value.u64v[BufferPointerByteOffsetVariableSlot]; + + BindpointIndex bind = inner->GetBinding(); + + WalkVariable(dataTypes[typeId], byteOffset, ret, true, [this, bind](ShaderVariable &var, + const DataType &type, + uint64_t offset) { + if(type.type == DataType::MatrixType) + { + const Decorations &dec = decorations[type.id]; + RDCASSERT(dec.flags & Decorations::HasMatrixStride); + + if(dec.flags & Decorations::RowMajor) + { + for(uint8_t r = 0; r < var.rows; r++) + { + apiWrapper->ReadBufferValue(bind, offset + r * dec.matrixStride, + VarTypeByteSize(var.type) * var.columns, + &var.value.uv[r * var.columns]); + } + } + else + { + ShaderValue tmp = {}; + // read column-wise + for(uint8_t c = 0; c < var.columns; c++) + { + apiWrapper->ReadBufferValue(bind, offset + c * dec.matrixStride, + VarTypeByteSize(var.type) * var.rows, &tmp.uv[c * var.rows]); + } + + // transpose into our row major storage + for(uint8_t r = 0; r < var.rows; r++) + { + for(uint8_t c = 0; c < var.columns; c++) + { + if(VarTypeByteSize(var.type) == 8) + var.value.u64v[r * var.columns + c] = tmp.u64v[c * var.rows + r]; + else + var.value.uv[r * var.columns + c] = tmp.uv[c * var.rows + r]; + } + } + } + } + else if(type.type == DataType::VectorType) + { + apiWrapper->ReadBufferValue(bind, offset, VarTypeByteSize(var.type) * var.columns, + var.value.uv); + } + else if(type.type == DataType::ScalarType) + { + apiWrapper->ReadBufferValue(bind, offset, VarTypeByteSize(var.type), var.value.uv); + } + }); + + ret.name = ptr.name; + return ret; + } + + ret = *inner; ret.name = ptr.name; // we don't support pointers to scalars since our 'unit' of pointer is a ShaderVariable, so check @@ -1010,10 +1239,85 @@ Id Debugger::GetPointerBaseId(const ShaderVariable &ptr) const return Id::fromWord(ptr.value.uv[4]); } +bool Debugger::IsOpaquePointer(const ShaderVariable &ptr) const +{ + if(ptr.type != VarType::GPUPointer) + return false; + + ShaderVariable *inner = (ShaderVariable *)(uintptr_t)ptr.value.u64v[0]; + return inner->type == VarType::ReadOnlyResource || inner->type == VarType::ReadWriteResource; +} + void Debugger::WriteThroughPointer(const ShaderVariable &ptr, const ShaderVariable &val) { ShaderVariable *storage = (ShaderVariable *)(uintptr_t)ptr.value.u64v[0]; + if(storage->type == VarType::ReadWriteResource) + { + rdcspv::Id typeId = + rdcspv::Id::fromWord(uint32_t(ptr.value.u64v[BufferPointerTypeIdVariableSlot])); + uint64_t byteOffset = ptr.value.u64v[BufferPointerByteOffsetVariableSlot]; + + const DataType &type = dataTypes[typeId]; + + BindpointIndex bind = storage->GetBinding(); + + WalkVariable(dataTypes[typeId], byteOffset, (ShaderVariable &)val, false, + [this, bind](ShaderVariable &var, const DataType &type, uint64_t offset) { + if(type.type == DataType::MatrixType) + { + const Decorations &dec = decorations[type.id]; + RDCASSERT(dec.flags & Decorations::HasMatrixStride); + + if(dec.flags & Decorations::RowMajor) + { + for(uint8_t r = 0; r < var.rows; r++) + { + apiWrapper->WriteBufferValue(bind, offset + r * dec.matrixStride, + VarTypeByteSize(var.type) * var.columns, + &var.value.uv[r * var.columns]); + } + } + else + { + ShaderValue tmp = {}; + + // transpose from our row major storage + for(uint8_t r = 0; r < var.rows; r++) + { + for(uint8_t c = 0; c < var.columns; c++) + { + if(VarTypeByteSize(var.type) == 8) + tmp.u64v[c * var.rows + r] = var.value.u64v[r * var.columns + c]; + else + tmp.uv[c * var.rows + r] = var.value.uv[r * var.columns + c]; + } + } + + // read column-wise + for(uint8_t c = 0; c < var.columns; c++) + { + apiWrapper->WriteBufferValue(bind, offset + c * dec.matrixStride, + VarTypeByteSize(var.type) * var.rows, + &tmp.uv[c * var.rows]); + } + } + } + else if(type.type == DataType::VectorType) + { + apiWrapper->WriteBufferValue( + bind, offset, VarTypeByteSize(var.type) * var.columns, var.value.uv); + } + else if(type.type == DataType::ScalarType) + { + apiWrapper->WriteBufferValue(bind, offset, VarTypeByteSize(var.type), + var.value.uv); + } + }); + + return; + } + // we don't support pointers to scalars since our 'unit' of pointer is a ShaderVariable, so check // if we have scalar indices to apply: uint32_t scalar0 = ptr.value.uv[2]; @@ -1370,16 +1674,17 @@ uint32_t Debugger::AllocateVariable(const Decorations &varDecorations, } else if(sourceVarType == DebugVariableType::Constant) { - uint32_t set = 0, bind = 0; + BindpointIndex bindpoint; + // TODO handle arrays if(varDecorations.flags & Decorations::HasDescriptorSet) - set = varDecorations.set; + bindpoint.bindset = (int32_t)varDecorations.set; if(varDecorations.flags & Decorations::HasBinding) - bind = varDecorations.binding; + bindpoint.bind = (int32_t)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); + apiWrapper->ReadBufferValue(bindpoint, offset, VarByteSize(outVar), outVar.value.uv); } else { @@ -1401,8 +1706,7 @@ uint32_t Debugger::AllocateVariable(const Decorations &varDecorations, for(uint32_t c = 0; c < sourceVar.columns; c++) { // read the column - apiWrapper->ReadConstantBufferValue(set, bind, offset + c * matrixStride, colSize, - &tmp.uv[0]); + apiWrapper->ReadBufferValue(bindpoint, 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++) @@ -1416,8 +1720,8 @@ uint32_t Debugger::AllocateVariable(const Decorations &varDecorations, 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]); + apiWrapper->ReadBufferValue(bindpoint, offset + r * matrixStride, rowSize, + &outVar.value.uv[r * sourceVar.columns]); } } } @@ -1427,6 +1731,90 @@ uint32_t Debugger::AllocateVariable(const Decorations &varDecorations, return outVar.rows; } +void Debugger::WalkVariable( + const DataType &type, uint64_t byteOffset, ShaderVariable &var, bool initialise, + std::function callback) const +{ + switch(type.type) + { + case DataType::ScalarType: + { + if(initialise) + { + var.type = type.scalar().Type(); + var.rows = 1; + var.columns = 1; + } + break; + } + case DataType::VectorType: + { + if(initialise) + { + var.type = type.scalar().Type(); + var.rows = 1; + var.columns = RDCMAX(1U, type.vector().count); + } + break; + } + case DataType::MatrixType: + { + if(initialise) + { + var.type = type.scalar().Type(); + var.columns = RDCMAX(1U, type.matrix().count); + var.rows = RDCMAX(1U, type.vector().count); + } + break; + } + case DataType::StructType: + { + for(int32_t i = 0; i < type.children.count(); i++) + { + if(initialise) + { + var.members.push_back(ShaderVariable()); + var.members.back().name = StringFormat::Fmt("_child%d", i); + } + + WalkVariable(dataTypes[type.children[i].type], + byteOffset + type.children[i].decorations.offset, var.members.back(), + initialise, callback); + } + return; + } + case DataType::ArrayType: + { + ShaderVariable len = GetActiveLane().ids[type.length]; + for(uint32_t i = 0; i < len.value.u.x; i++) + { + if(initialise) + { + var.members.push_back(ShaderVariable()); + var.members.back().name = StringFormat::Fmt("[%u]", i); + } + + WalkVariable(dataTypes[type.InnerType()], byteOffset, var.members.back(), initialise, + callback); + + byteOffset += decorations[type.id].arrayStride; + } + return; + } + case DataType::PointerType: + case DataType::ImageType: + case DataType::SamplerType: + case DataType::SampledImageType: + case DataType::UnknownType: + { + RDCERR("Unexpected variable type %d", type.type); + return; + } + } + + callback(var, type, byteOffset); +} + uint32_t Debugger::ApplyDerivatives(uint32_t quadIndex, const Decorations &curDecorations, uint32_t location, const DataType &inType, ShaderVariable &outVar) { diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index 704b15ded..cee02876e 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -143,11 +143,15 @@ class VulkanAPIWrapper : public rdcspv::DebugAPIWrapper rdcarray m_DescSets; public: - VulkanAPIWrapper(WrappedVulkan *vk, VulkanCreationInfo &creation, VkShaderStageFlagBits stage) - : m_DebugData(vk->GetReplay()->GetShaderDebugData()), m_Creation(creation) + VulkanAPIWrapper(WrappedVulkan *vk, VulkanCreationInfo &creation, VkShaderStageFlagBits stage, + uint32_t eid) + : m_DebugData(vk->GetReplay()->GetShaderDebugData()), m_Creation(creation), m_EventID(eid) { m_pDriver = vk; + // when we're first setting up, the state is pristine and no replay is needed + m_ResourcesDirty = false; + const VulkanRenderState &state = m_pDriver->GetRenderState(); const bool compute = (stage == VK_SHADER_STAGE_COMPUTE_BIT); @@ -277,40 +281,52 @@ public: m_pDriver->vkDestroySampler(dev, it->second, NULL); } + void ResetReplay() + { + // replay the draw to get back to 'normal' state for this event, and mark that we need to replay + // back to pristine state next time we need to fetch data. + m_pDriver->ReplayLog(0, m_EventID, eReplay_OnlyDraw); + m_ResourcesDirty = true; + } + virtual void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, rdcstr d) override { 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 + virtual uint64_t GetBufferLength(BindpointIndex bind) override { - rdcpair key = make_rdcpair(set, bind); - auto insertIt = cbufferCache.insert(std::make_pair(key, bytebuf())); - bytebuf &data = insertIt.first->second; - if(insertIt.second) + bool valid = true; + const VkDescriptorBufferInfo &bufData = + GetDescriptor("reading buffer length", bind, valid); + if(valid) { - if(set == PushConstantBindSet) - { - data = pushData; - } - else - { - // TODO handle arrays here - BindpointIndex index(set, bind, 0); + if(bufData.range != VK_WHOLE_SIZE) + return bufData.range; - bool valid = true; - const VkDescriptorBufferInfo &bufData = - GetDescriptor("reading constant buffer value", index, valid); - if(valid) - m_pDriver->GetDebugManager()->GetBufferData(GetResID(bufData.buffer), bufData.offset, - bufData.range, data); - } + return m_Creation.m_Buffer[GetResID(bufData.buffer)].size - bufData.offset; } + return 0; + } + + virtual void ReadBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, + void *dst) override + { + const bytebuf &data = PopulateBuffer(bind); + if(offset + byteSize <= data.size()) - memcpy(dst, data.data() + offset, byteSize); + memcpy(dst, data.data() + (size_t)offset, (size_t)byteSize); + } + + virtual void WriteBufferValue(BindpointIndex bind, uint64_t offset, uint64_t byteSize, + const void *src) override + { + bytebuf &data = PopulateBuffer(bind); + + if(offset + byteSize <= data.size()) + memcpy(data.data() + (size_t)offset, src, (size_t)byteSize); } virtual void FillInputValue(ShaderVariable &var, ShaderBuiltin builtin, uint32_t location, @@ -1014,6 +1030,9 @@ private: ShaderDebugData &m_DebugData; VulkanCreationInfo &m_Creation; + bool m_ResourcesDirty = false; + uint32_t m_EventID; + std::map m_SampleViews; typedef rdcpair SamplerBiasKey; @@ -1021,7 +1040,7 @@ private: bytebuf pushData; - std::map, bytebuf> cbufferCache; + std::map bufferCache; template const T &GetDescriptor(const rdcstr &access, BindpointIndex index, bool &valid) { @@ -1086,6 +1105,40 @@ private: return elemData[index.arrayIndex]; } + bytebuf &PopulateBuffer(BindpointIndex bind) + { + auto insertIt = bufferCache.insert(std::make_pair(bind, bytebuf())); + bytebuf &data = insertIt.first->second; + if(insertIt.second) + { + if(bind.bindset == PushConstantBindSet) + { + data = pushData; + } + else + { + bool valid = true; + const VkDescriptorBufferInfo &bufData = + GetDescriptor("accessing buffer value", bind, valid); + if(valid) + { + // if the resources might be dirty from side-effects from the draw, replay back to right + // before it. + if(m_ResourcesDirty) + { + m_pDriver->ReplayLog(0, m_EventID, eReplay_WithoutDraw); + m_ResourcesDirty = false; + } + + m_pDriver->GetDebugManager()->GetBufferData(GetResID(bufData.buffer), bufData.offset, + bufData.range, data); + } + } + } + + return data; + } + VkPipeline MakePipe(const ShaderConstParameters ¶ms, bool uintTex, bool sintTex) { VkSpecializationMapEntry specMaps[sizeof(params) / sizeof(uint32_t)]; @@ -2884,6 +2937,9 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u if(!(draw->flags & DrawFlags::Drawcall)) return new ShaderDebugTrace(); + // get ourselves in pristine state before this draw (without any side effects it may have had) + m_pDriver->ReplayLog(0, eventId, eReplay_WithoutDraw); + const VulkanCreationInfo::Pipeline &pipe = c.m_Pipeline[state.graphics.pipeline]; VulkanCreationInfo::ShaderModule &shader = c.m_ShaderModule[pipe.shaders[0].module]; rdcstr entryPoint = pipe.shaders[0].entryPoint; @@ -2894,7 +2950,8 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u shadRefl.PopulateDisassembly(shader.spirv); - VulkanAPIWrapper *apiWrapper = new VulkanAPIWrapper(m_pDriver, c, VK_SHADER_STAGE_VERTEX_BIT); + VulkanAPIWrapper *apiWrapper = + new VulkanAPIWrapper(m_pDriver, c, VK_SHADER_STAGE_VERTEX_BIT, eventId); std::map &builtins = apiWrapper->builtin_inputs; builtins[ShaderBuiltin::BaseInstance] = ShaderVariable(rdcstr(), draw->instanceOffset, 0U, 0U, 0U); @@ -2994,6 +3051,7 @@ ShaderDebugTrace *VulkanReplay::DebugVertex(uint32_t eventId, uint32_t vertid, u debugger->Parse(shader.spirv.GetSPIRV()); ShaderDebugTrace *ret = debugger->BeginDebug(apiWrapper, ShaderStage::Vertex, entryPoint, spec, shadRefl.instructionLines, shadRefl.patchData, 0); + apiWrapper->ResetReplay(); return ret; } @@ -3032,6 +3090,9 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ if(!(draw->flags & DrawFlags::Drawcall)) return new ShaderDebugTrace(); + // get ourselves in pristine state before this draw (without any side effects it may have had) + m_pDriver->ReplayLog(0, eventId, eReplay_WithoutDraw); + const VulkanCreationInfo::Pipeline &pipe = c.m_Pipeline[state.graphics.pipeline]; VulkanCreationInfo::ShaderModule &shader = c.m_ShaderModule[pipe.shaders[4].module]; rdcstr entryPoint = pipe.shaders[4].entryPoint; @@ -3042,7 +3103,8 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ shadRefl.PopulateDisassembly(shader.spirv); - VulkanAPIWrapper *apiWrapper = new VulkanAPIWrapper(m_pDriver, c, VK_SHADER_STAGE_FRAGMENT_BIT); + VulkanAPIWrapper *apiWrapper = + new VulkanAPIWrapper(m_pDriver, c, VK_SHADER_STAGE_FRAGMENT_BIT, eventId); std::map &builtins = apiWrapper->builtin_inputs; builtins[ShaderBuiltin::DeviceIndex] = ShaderVariable(rdcstr(), 0U, 0U, 0U, 0U); @@ -3594,6 +3656,7 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ ret = debugger->BeginDebug(apiWrapper, ShaderStage::Pixel, entryPoint, spec, shadRefl.instructionLines, shadRefl.patchData, destIdx); + apiWrapper->ResetReplay(); } else { @@ -3654,6 +3717,9 @@ ShaderDebugTrace *VulkanReplay::DebugThread(uint32_t eventId, const uint32_t gro if(!(draw->flags & DrawFlags::Dispatch)) return new ShaderDebugTrace(); + // get ourselves in pristine state before this dispatch (without any side effects it may have had) + m_pDriver->ReplayLog(0, eventId, eReplay_WithoutDraw); + const VulkanCreationInfo::Pipeline &pipe = c.m_Pipeline[state.compute.pipeline]; VulkanCreationInfo::ShaderModule &shader = c.m_ShaderModule[pipe.shaders[5].module]; rdcstr entryPoint = pipe.shaders[5].entryPoint; @@ -3664,7 +3730,8 @@ ShaderDebugTrace *VulkanReplay::DebugThread(uint32_t eventId, const uint32_t gro shadRefl.PopulateDisassembly(shader.spirv); - VulkanAPIWrapper *apiWrapper = new VulkanAPIWrapper(m_pDriver, c, VK_SHADER_STAGE_COMPUTE_BIT); + VulkanAPIWrapper *apiWrapper = + new VulkanAPIWrapper(m_pDriver, c, VK_SHADER_STAGE_COMPUTE_BIT, eventId); uint32_t threadDim[3]; threadDim[0] = shadRefl.refl.dispatchThreadsDimension[0]; @@ -3693,6 +3760,7 @@ ShaderDebugTrace *VulkanReplay::DebugThread(uint32_t eventId, const uint32_t gro debugger->Parse(shader.spirv.GetSPIRV()); ShaderDebugTrace *ret = debugger->BeginDebug(apiWrapper, ShaderStage::Compute, entryPoint, spec, shadRefl.instructionLines, shadRefl.patchData, 0); + apiWrapper->ResetReplay(); return ret; } @@ -3706,5 +3774,10 @@ rdcarray VulkanReplay::ContinueDebug(ShaderDebugger *debugger) VkMarkerRegion region("ContinueDebug Simulation Loop"); - return spvDebugger->ContinueDebug(); + rdcarray ret = spvDebugger->ContinueDebug(); + + VulkanAPIWrapper *api = (VulkanAPIWrapper *)spvDebugger->GetAPIWrapper(); + api->ResetReplay(); + + return ret; } diff --git a/util/test/demos/vk/vk_shader_debug_zoo.cpp b/util/test/demos/vk/vk_shader_debug_zoo.cpp index 21b947004..06322553b 100644 --- a/util/test/demos/vk/vk_shader_debug_zoo.cpp +++ b/util/test/demos/vk/vk_shader_debug_zoo.cpp @@ -96,7 +96,7 @@ void main() )EOSHADER"; - std::string pixel_glsl = R"EOSHADER( + std::string pixel_glsl_header = R"EOSHADER( #version 460 core #extension GL_EXT_samplerless_texture_functions : require @@ -128,14 +128,18 @@ layout(set = 0, binding = 3) uniform texture2D sampledImage; layout(set = 0, binding = 4) uniform sampler2D linearSampledImage; -/* +struct dummy +{ + uvec4 val; + uvec4 val2; +}; + layout(set = 0, binding = 5, std430) buffer storebuftype { vec4 x; - uvec4 y; + dummy y; vec4 arr[]; } storebuf; -*/ //layout(set = 0, binding = 6, rgba32f) uniform coherent image2D storeImage; @@ -151,12 +155,15 @@ layout(push_constant) uniform PushData { layout(offset = 16) ivec4 data; } push; +layout(location = 0, index = 0) out vec4 Color; + #define inout_type in )EOSHADER" + v2f + R"EOSHADER( -layout(location = 0, index = 0) out vec4 Color; +)EOSHADER"; + std::string pixel_glsl1 = pixel_glsl_header + R"EOSHADER( void main() { float posinf = linearData.oneVal/linearData.zeroVal.x; @@ -492,7 +499,7 @@ void main() break; } )EOSHADER" - R"EOSHADER( + R"EOSHADER( case 51: { Color = fwidthFine(vec4(inpos, inposIncreased)); @@ -859,7 +866,7 @@ void main() break; } )EOSHADER" - R"EOSHADER( + R"EOSHADER( case 102: { uint a = zerou + 0x0dadbeef; @@ -1046,6 +1053,54 @@ void main() Color = unpacked; break; } + case 127: + { + uint len = storebuf.arr.length(); + Color = vec4(float(len), float(len), float(len), float(len)); + break; + } + case 128: + { + // test storage buffer write here, we'll read from it in GLSL test 2 + storebuf.x = vec4(3.1f, 4.1f, 5.9f, 2.6f); + storebuf.y.val = uvec4(31, 41, 59, 26); + storebuf.arr[flatData.intval - flatData.test] = vec4(inpos, inposIncreased); + + Color = storebuf.x; + break; + } + default: break; + } +} + +)EOSHADER"; + + std::string pixel_glsl2 = pixel_glsl_header + R"EOSHADER( +void main() +{ + uint test = flatData.test; + + Color = vec4(0,0,0,0); + switch(test) + { + case 0: + { + // test loading from the storage buffer (after a nice big barrier) + Color = storebuf.x; + break; + } + case 1: + { + // test loading from the storage buffer (after a nice big barrier) + Color = vec4(storebuf.y.val); + break; + } + case 2: + { + // test loading from the storage buffer (after a nice big barrier) + Color = storebuf.arr[flatData.intval - flatData.test]; + break; + } default: break; } } @@ -1689,10 +1744,26 @@ void main() "OpCopyMemory %Color %gl_FragCoord\n" "; no_out\n", + "%_src = OpAccessChain %ptr_Uniform_float4 %buffer %uint_0\n" + "%_dst = OpAccessChain %ptr_Uniform_float4 %buffer %uint_2 %uint_3\n" + "OpCopyMemory %_dst %_src\n" + "OpCopyMemory %Color %_src\n" + "; no_out\n", + "%frag = OpLoad %float4 %gl_FragCoord\n" "%_out_float4 = OpCopyObject %float4 %frag\n", }); + // test SSBO pointers + append_tests({ + "%_y = OpAccessChain %ptr_Uniform_dummy %buffer %uint_1\n" + "%_src = OpAccessChain %ptr_Uniform_uint4 %_y %uint_0\n" + "%_dst = OpAccessChain %ptr_Uniform_uint4 %_y %uint_1\n" + "%_tmp = OpLoad %uint4 %_src\n" + "OpStore %_dst %_tmp\n" + "%_out_uint4 = OpLoad %uint4 %_dst\n", + }); + // disabled while shaderc has a bug that doesn't respect the target environment /* if(vk_version >= 0x12) @@ -1959,6 +2030,16 @@ void main() OpDecorate %Color Index 0 OpDecorate %Color Location 0 OpDecorate %gl_FragCoord BuiltIn FragCoord + + OpDecorate %rtarray_float4 ArrayStride 16 + OpMemberDecorate %dummy 0 Offset 0 + OpMemberDecorate %dummy 1 Offset 16 + OpMemberDecorate %buftype 0 Offset 0 + OpMemberDecorate %buftype 1 Offset 16 + OpMemberDecorate %buftype 2 Offset 48 + OpDecorate %buftype BufferBlock + OpDecorate %buffer DescriptorSet 0 + OpDecorate %buffer Binding 5 )EOSHADER"; std::string typesConstants = R"EOSHADER( @@ -1988,6 +2069,8 @@ void main() %mainfunc = OpTypeFunction %void +%rtarray_float4 = OpTypeRuntimeArray %float4 + %v2f = OpTypeStruct %float2 %float2 %float2 %float %float %float %flatv2f = OpTypeStruct %uint %uint @@ -1997,6 +2080,9 @@ void main() %f32f32 = OpTypeStruct %float %float %f32i32 = OpTypeStruct %float %int + %dummy = OpTypeStruct %uint4 %uint4 + %buftype = OpTypeStruct %float4 %dummy %rtarray_float4 + %ptr_Input_v2f = OpTypePointer Input %v2f %ptr_Input_flatv2f = OpTypePointer Input %flatv2f %ptr_Input_uint = OpTypePointer Input %uint @@ -2023,6 +2109,9 @@ void main() %ptr_Uniform_int3 = OpTypePointer Uniform %int3 %ptr_Uniform_int4 = OpTypePointer Uniform %int4 +%ptr_Uniform_dummy = OpTypePointer Uniform %dummy +%ptr_Uniform_buftype = OpTypePointer Uniform %buftype + %linearData = OpVariable %ptr_Input_v2f Input %flatData = OpVariable %ptr_Input_flatv2f Input %gl_FragCoord = OpVariable %ptr_Input_float4 Input @@ -2031,6 +2120,8 @@ void main() %priv_int = OpVariable %ptr_Private_int Private %priv_float = OpVariable %ptr_Private_float Private + %buffer = OpVariable %ptr_Uniform_buftype Uniform + %flatv2f_test_idx = OpConstant %int 0 %flatv2f_intval_idx = OpConstant %int 1 @@ -2264,6 +2355,9 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource { optDevExts.push_back(VK_KHR_SPIRV_1_4_EXTENSION_NAME); + // we require this to pixel shader debug anyway, so we might as well require it for all tests. + features.fragmentStoresAndAtomics = VK_TRUE; + VulkanGraphicsTest::Prepare(argc, argv); vk_version = 0x10; @@ -2291,10 +2385,15 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource make_asm_tests(); - size_t lastTest = pixel_glsl.rfind("case "); + size_t lastTest = pixel_glsl1.rfind("case "); lastTest += sizeof("case ") - 1; - const uint32_t numGLSLTests = atoi(pixel_glsl.c_str() + lastTest) + 1; + const uint32_t numGLSL1Tests = atoi(pixel_glsl1.c_str() + lastTest) + 1; + + lastTest = pixel_glsl2.rfind("case "); + lastTest += sizeof("case ") - 1; + + const uint32_t numGLSL2Tests = atoi(pixel_glsl2.c_str() + lastTest) + 1; const uint32_t numASMTests = (uint32_t)asm_tests.size(); @@ -2317,7 +2416,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource {setlayout}, {vkh::PushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 16, sizeof(Vec4i))})); // calculate number of tests, wrapping each row at 256 - uint32_t texWidth = AlignUp(std::max(numGLSLTests, numASMTests), 256U); + uint32_t texWidth = AlignUp(std::max(std::max(numGLSL1Tests, numGLSL2Tests), numASMTests), 256U); uint32_t texHeight = std::max(1U, texWidth / 256U); texWidth /= texHeight; @@ -2360,10 +2459,15 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource pipeCreateInfo.stages = { CompileShaderModule(vertex, ShaderLang::glsl, ShaderStage::vert, "main"), - CompileShaderModule(pixel_glsl, ShaderLang::glsl, ShaderStage::frag, "main"), + CompileShaderModule(pixel_glsl1, ShaderLang::glsl, ShaderStage::frag, "main"), }; - VkPipeline glslpipe = createGraphicsPipeline(pipeCreateInfo); + VkPipeline glslpipe1 = createGraphicsPipeline(pipeCreateInfo); + + pipeCreateInfo.stages[1] = + CompileShaderModule(pixel_glsl2, ShaderLang::glsl, ShaderStage::frag, "main"); + + VkPipeline glslpipe2 = createGraphicsPipeline(pipeCreateInfo); SPIRVTarget target = SPIRVTarget::vulkan; @@ -2639,7 +2743,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource vkCmdClearColorImage(cmd, store_image.image, VK_IMAGE_LAYOUT_GENERAL, vkh::ClearColorValue(6.66f, 6.66f, 6.66f, 6.66f), 1, vkh::ImageSubresourceRange()); - vkCmdFillBuffer(cmd, store_buffer.buffer, 0, VK_WHOLE_SIZE, 0xcccccccc); + vkCmdFillBuffer(cmd, store_buffer.buffer, 0, VK_WHOLE_SIZE, 0x42424242); vkCmdFillBuffer(cmd, store_texbuffer.buffer, 0, VK_WHOLE_SIZE, 0); vkh::cmdPipelineBarrier( @@ -2666,7 +2770,7 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource s.extent.width = texWidth; s.extent.height = texHeight; - vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, glslpipe); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, glslpipe1); vkCmdSetViewport(cmd, 0, 1, &v); vkCmdSetScissor(cmd, 0, 1, &s); vkh::cmdBindVertexBuffers(cmd, 0, {vb.buffer}, {0}); @@ -2680,8 +2784,8 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource {vkh::ClearValue(0.0f, 0.0f, 0.0f, 0.0f)}), VK_SUBPASS_CONTENTS_INLINE); - pushMarker(cmd, "GLSL tests"); - uint32_t numTests = numGLSLTests; + pushMarker(cmd, "GLSL1 tests"); + uint32_t numTests = numGLSL1Tests; uint32_t offset = 0; // loop drawing 256 tests at a time while(numTests > 0) @@ -2696,10 +2800,6 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource vkCmdEndRenderPass(cmd); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, asmpipe); - vkCmdSetViewport(cmd, 0, 1, &v); - - vkh::cmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, {descset}, {}); - vkCmdPushConstants(cmd, layout, VK_SHADER_STAGE_FRAGMENT_BIT, 16, sizeof(Vec4i), &push); vkCmdBeginRenderPass(cmd, vkh::RenderPassBeginInfo(renderPass, framebuffer, s, {vkh::ClearValue(0.0f, 0.0f, 0.0f, 0.0f)}), @@ -2720,6 +2820,45 @@ OpMemberDecorate %cbuffer_struct 17 Offset 216 ; double doublePackSource vkCmdEndRenderPass(cmd); + // sync all the storage work + vkh::cmdPipelineBarrier( + cmd, + { + vkh::ImageMemoryBarrier(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL, + store_image.image), + }, + { + vkh::BufferMemoryBarrier(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + store_buffer.buffer), + vkh::BufferMemoryBarrier(VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + store_texbuffer.buffer), + }); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, glslpipe2); + + vkCmdBeginRenderPass(cmd, vkh::RenderPassBeginInfo(renderPass, framebuffer, s, + {vkh::ClearValue(0.0f, 0.0f, 0.0f, 0.0f)}), + VK_SUBPASS_CONTENTS_INLINE); + + pushMarker(cmd, "GLSL2 tests"); + numTests = numGLSL2Tests; + offset = 0; + // loop drawing 256 tests at a time + while(numTests > 0) + { + uint32_t num = std::min(numTests, 256U); + vkCmdDraw(cmd, 3, num, 0, offset); + offset += num; + numTests -= num; + } + popMarker(cmd); + + vkCmdEndRenderPass(cmd); + FinishUsingBackbuffer(cmd, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_GENERAL); vkEndCommandBuffer(cmd); diff --git a/util/test/tests/Vulkan/VK_Shader_Debug_Zoo.py b/util/test/tests/Vulkan/VK_Shader_Debug_Zoo.py index 5a7f87d80..17f30c1f7 100644 --- a/util/test/tests/Vulkan/VK_Shader_Debug_Zoo.py +++ b/util/test/tests/Vulkan/VK_Shader_Debug_Zoo.py @@ -13,87 +13,45 @@ class VK_Shader_Debug_Zoo(rdtest.TestCase): failed = False - rdtest.log.begin_section("GLSL tests") - draw = self.find_draw("GLSL tests") - for child in range(len(draw.children)): - section = draw.children[child] - self.controller.SetFrameEvent(section.eventId, False) - pipe: rd.PipeState = self.controller.GetPipelineState() + for test_name in ["GLSL1 tests", "GLSL2 tests", "ASM tests"]: + rdtest.log.begin_section(test_name) + draw = self.find_draw(test_name) + for child in range(len(draw.children)): + section = draw.children[child] + self.controller.SetFrameEvent(section.eventId, False) + pipe: rd.PipeState = self.controller.GetPipelineState() - for test in range(section.numInstances): - x = 4 * test + 1 - y = 4 * child + 1 + for test in range(section.numInstances): + x = 4 * test + 1 + y = 4 * child + 1 - # Debug the shader - trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, rd.ReplayController.NoPreference, - rd.ReplayController.NoPreference) + # Debug the shader + trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, rd.ReplayController.NoPreference, + rd.ReplayController.NoPreference) - if trace.debugger is None: - failed = True - rdtest.log.error("Test {} in sub-section {} did not debug at all".format(test, child)) - self.controller.FreeTrace(trace) - continue + if trace.debugger is None: + failed = True + rdtest.log.error("Test {} in sub-section {} did not debug at all".format(test, child)) + self.controller.FreeTrace(trace) + continue - cycles, variables = self.process_trace(trace) + cycles, variables = self.process_trace(trace) - output: rd.SourceVariableMapping = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) + output: rd.SourceVariableMapping = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) - debugged = self.evaluate_source_var(output, variables) + debugged = self.evaluate_source_var(output, variables) - try: - self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, x, y, debugged.value.fv[0:4]) - except rdtest.TestFailureException as ex: - failed = True - rdtest.log.error("Test {} in sub-section {} did not match. {}".format(test, child, str(ex))) - continue - finally: - self.controller.FreeTrace(trace) + try: + self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, x, y, debugged.value.fv[0:4]) + except rdtest.TestFailureException as ex: + failed = True + rdtest.log.error("Test {} in sub-section {} did not match. {}".format(test, child, str(ex))) + continue + finally: + self.controller.FreeTrace(trace) - rdtest.log.success("Test {} in sub-section {} matched as expected".format(test, child)) - rdtest.log.end_section("GLSL tests") - - self.controller.SetFrameEvent(draw.eventId, False) - - pipe: rd.PipeState = self.controller.GetPipelineState() - - rdtest.log.begin_section("ASM tests") - draw = self.find_draw("ASM tests") - for child in range(len(draw.children)): - section = draw.children[child] - self.controller.SetFrameEvent(section.eventId, False) - pipe: rd.PipeState = self.controller.GetPipelineState() - - for test in range(section.numInstances): - x = 4 * test + 1 - y = 4 * child + 1 - - # Debug the shader - trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, rd.ReplayController.NoPreference, - rd.ReplayController.NoPreference) - - if trace.debugger is None: - failed = True - rdtest.log.error("Test {} in sub-section {} did not debug at all".format(test, child)) - self.controller.FreeTrace(trace) - continue - - cycles, variables = self.process_trace(trace) - - output: rd.SourceVariableMapping = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) - - debugged = self.evaluate_source_var(output, variables) - - try: - self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, x, y, debugged.value.fv[0:4]) - except rdtest.TestFailureException as ex: - failed = True - rdtest.log.error("Test {} in sub-section {} did not match. {}".format(test, child, str(ex))) - continue - finally: - self.controller.FreeTrace(trace) - - rdtest.log.success("Test {} in sub-section {} matched as expected".format(test, child)) - rdtest.log.end_section("ASM tests") + rdtest.log.success("Test {} in sub-section {} matched as expected".format(test, child)) + rdtest.log.end_section(test_name) if failed: raise rdtest.TestFailureException("Some tests were not as expected")