From 3cb89988414e7c6b8f0b47a45bb9dd2be7f5c2b2 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Wed, 17 Jan 2024 10:46:32 +0000 Subject: [PATCH] Vulkan DebugPixel support for multiview debugging Output "ViewIndex" into the pixel shader hits array Select the pixel shader input which matches the viewIndex to the passed in view Ignore the view parameter if set to ~0U (rd.ReplayController.NoPreference) Ignore the view parameter if any subpass has empty multiviews --- qrenderdoc/Code/CaptureContext.cpp | 4 +- qrenderdoc/Code/CaptureContext.h | 2 +- qrenderdoc/Code/Interface/QRDInterface.h | 3 +- qrenderdoc/Windows/PixelHistoryView.cpp | 4 +- qrenderdoc/Windows/PixelHistoryView.h | 3 +- qrenderdoc/Windows/PythonShell.cpp | 4 +- qrenderdoc/Windows/TextureViewer.cpp | 7 +- renderdoc/driver/vulkan/vk_shaderdebug.cpp | 137 +++++++++++++++++++-- 8 files changed, 145 insertions(+), 19 deletions(-) diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 3a2d41d01..95538d4ea 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -2620,9 +2620,9 @@ IBufferViewer *CaptureContext::ViewConstantBuffer(ShaderStage stage, uint32_t sl } IPixelHistoryView *CaptureContext::ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y, - const TextureDisplay &display) + uint32_t view, const TextureDisplay &display) { - return new PixelHistoryView(*this, texID, QPoint(x, y), display, m_MainWindow); + return new PixelHistoryView(*this, texID, QPoint(x, y), view, display, m_MainWindow); } QWidget *CaptureContext::CreateBuiltinWindow(const rdcstr &objectName) diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 861e07702..0409771dc 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -269,7 +269,7 @@ public: const rdcstr &format = "") override; IBufferViewer *ViewConstantBuffer(ShaderStage stage, uint32_t slot, uint32_t idx) override; - IPixelHistoryView *ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y, + IPixelHistoryView *ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y, uint32_t view, const TextureDisplay &display) override; QWidget *CreateBuiltinWindow(const rdcstr &objectName) override; diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 8261890db..3a42a3028 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -2624,12 +2624,13 @@ operation. :param renderdoc.ResourceId id: The ID of the texture to show the history of. :param int x: The x co-ordinate of the pixel to search for. :param int y: The y co-ordinate of the pixel to search for. +:param int view: The layered or multiview rendering view index of the pixel to search for. :param renderdoc.TextureDisplay display: The texture display configuration to use when looking up the history. :return: The new :class:`PixelHistoryView` window opened, but not shown. :rtype: PixelHistoryView )"); - virtual IPixelHistoryView *ViewPixelHistory(ResourceId id, uint32_t x, uint32_t y, + virtual IPixelHistoryView *ViewPixelHistory(ResourceId id, uint32_t x, uint32_t y, uint32_t view, const TextureDisplay &display) = 0; DOCUMENT(R"(Creates and returns a built-in window. diff --git a/qrenderdoc/Windows/PixelHistoryView.cpp b/qrenderdoc/Windows/PixelHistoryView.cpp index 2185fb2ce..8ac14b8e7 100644 --- a/qrenderdoc/Windows/PixelHistoryView.cpp +++ b/qrenderdoc/Windows/PixelHistoryView.cpp @@ -611,7 +611,7 @@ private: } }; -PixelHistoryView::PixelHistoryView(ICaptureContext &ctx, ResourceId id, QPoint point, +PixelHistoryView::PixelHistoryView(ICaptureContext &ctx, ResourceId id, QPoint point, uint32_t view, const TextureDisplay &display, QWidget *parent) : QFrame(parent), ui(new Ui::PixelHistoryView), m_Ctx(ctx) { @@ -622,6 +622,7 @@ PixelHistoryView::PixelHistoryView(ICaptureContext &ctx, ResourceId id, QPoint p m_Pixel = point; m_Display = display; m_ID = id; + m_View = view; updateWindowTitle(); @@ -770,6 +771,7 @@ void PixelHistoryView::startDebug(EventTag tag) DebugPixelInputs inputs; inputs.sample = m_Display.subresource.sample; inputs.primitive = tag.primitive; + inputs.view = m_View; trace = r->DebugPixel((uint32_t)m_Pixel.x(), (uint32_t)m_Pixel.y(), inputs); if(trace->debugger == NULL) diff --git a/qrenderdoc/Windows/PixelHistoryView.h b/qrenderdoc/Windows/PixelHistoryView.h index adaed550d..14242d7e7 100644 --- a/qrenderdoc/Windows/PixelHistoryView.h +++ b/qrenderdoc/Windows/PixelHistoryView.h @@ -40,7 +40,7 @@ class PixelHistoryView : public QFrame, public IPixelHistoryView, public ICaptur Q_OBJECT public: - explicit PixelHistoryView(ICaptureContext &ctx, ResourceId id, QPoint point, + explicit PixelHistoryView(ICaptureContext &ctx, ResourceId id, QPoint point, uint32_t view, const TextureDisplay &display, QWidget *parent = 0); ~PixelHistoryView(); @@ -74,6 +74,7 @@ private: ResourceId m_ID; TextureDisplay m_Display; QPoint m_Pixel; + uint32_t m_View; PixelHistoryItemModel *m_Model; bool m_ShowFailures = true; void startDebug(EventTag tag); diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 40d179fae..63ce4f8bc 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -851,10 +851,10 @@ struct CaptureContextInvoker : ObjectForwarder } virtual IPixelHistoryView *ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y, - const TextureDisplay &display) override + uint32_t view, const TextureDisplay &display) override { return InvokeRetFunction(&ICaptureContext::ViewPixelHistory, texID, x, y, - display); + view, display); } virtual QWidget *CreateBuiltinWindow(const rdcstr &objectName) override diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index c138a94a3..a08ab62dc 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -4098,9 +4098,11 @@ void TextureViewer::on_debugPixelContext_clicked() bool done = false; ShaderDebugTrace *trace = NULL; - m_Ctx.Replay().AsyncInvoke([this, &trace, &done, x, y](IReplayController *r) { + uint32_t view = m_TexDisplay.subresource.slice - m_Following.GetFirstArraySlice(m_Ctx); + m_Ctx.Replay().AsyncInvoke([this, &trace, &done, x, y, view](IReplayController *r) { DebugPixelInputs inputs; inputs.sample = m_TexDisplay.subresource.sample; + inputs.view = view; trace = r->DebugPixel((uint32_t)x, (uint32_t)y, inputs); if(trace->debugger == NULL) @@ -4160,7 +4162,8 @@ void TextureViewer::on_pixelHistory_clicked() if(m_TexDisplay.flipY) y = (int)(mipHeight - 1) - y; - IPixelHistoryView *hist = m_Ctx.ViewPixelHistory(texptr->resourceId, x, y, m_TexDisplay); + uint32_t view = m_TexDisplay.subresource.slice - m_Following.GetFirstArraySlice(m_Ctx); + IPixelHistoryView *hist = m_Ctx.ViewPixelHistory(texptr->resourceId, x, y, view, m_TexDisplay); m_Ctx.AddDockWindow(hist->Widget(), DockReference::TransientPopupArea, this, 0.3f); diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp index b76e008eb..ca604eb35 100644 --- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp +++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp @@ -3052,15 +3052,17 @@ struct PSHit Vec4f pos; uint32_t prim; uint32_t sample; + uint32_t view; uint32_t valid; float ddxDerivCheck; + uint32_t padding[3]; // PSInput base, ddx, .... }; static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structStride, VulkanCreationInfo::ShaderModuleReflection &shadRefl, const uint32_t paramAlign, StorageMode storageMode, - bool usePrimitiveID, bool useSampleID) + bool usePrimitiveID, bool useSampleID, bool useViewIndex) { rdcspv::Editor editor(fragspv); @@ -3182,7 +3184,7 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt rdcspv::Id base; rdcspv::Id type; uint32_t member = ~0U; - } fragCoord, primitiveID, sampleIndex; + } fragCoord, primitiveID, sampleIndex, viewIndex; // look to see which ones are already provided for(size_t i = 0; i < shadRefl.refl->inputSignature.size(); i++) @@ -3211,6 +3213,14 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt ? editor.DeclareType(rdcspv::scalar()) : editor.DeclareType(rdcspv::scalar()); } + else if(param.systemValue == ShaderBuiltin::MultiViewIndex) + { + access = &viewIndex; + + access->type = VarTypeCompType(param.varType) == CompType::SInt + ? editor.DeclareType(rdcspv::scalar()) + : editor.DeclareType(rdcspv::scalar()); + } if(access) { @@ -3276,6 +3286,25 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt editor.AddCapability(rdcspv::Capability::SampleRateShading); } + if(viewIndex.base == rdcspv::Id() && useViewIndex) + { + rdcspv::Id type = editor.DeclareType(rdcspv::scalar()); + rdcspv::Id ptrType = editor.DeclareType(rdcspv::Pointer(type, rdcspv::StorageClass::Input)); + + viewIndex.base = + editor.AddVariable(rdcspv::OpVariable(ptrType, editor.MakeId(), rdcspv::StorageClass::Input)); + viewIndex.type = type; + + editor.AddDecoration(rdcspv::OpDecorate( + viewIndex.base, + rdcspv::DecorationParam(rdcspv::BuiltIn::ViewIndex))); + editor.AddDecoration(rdcspv::OpDecorate(viewIndex.base, rdcspv::Decoration::Flat)); + + addedInputs.push_back(viewIndex.base); + + editor.AddCapability(rdcspv::Capability::MultiView); + } + rdcspv::Id PSInput; enum Variant @@ -3381,10 +3410,13 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt uint32Type, // uint sample; uint32Type, + // uint view; + uint32Type, // uint valid; uint32Type, // float ddxDerivCheck; floatType, + // // IN PSInput, @@ -3421,6 +3453,12 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt offs += sizeof(uint32_t); member++; + editor.AddDecoration(rdcspv::OpMemberDecorate( + PSHit, member, rdcspv::DecorationParam(offs))); + editor.SetMemberName(PSHit, member, "view"); + offs += sizeof(uint32_t); + member++; + editor.AddDecoration(rdcspv::OpMemberDecorate( PSHit, member, rdcspv::DecorationParam(offs))); editor.SetMemberName(PSHit, member, "valid"); @@ -3433,6 +3471,9 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt offs += sizeof(uint32_t); member++; + // + offs += sizeof(uint32_t) * 3; + RDCASSERT((offs % sizeof(Vec4f)) == 0); RDCASSERT((structStride % sizeof(Vec4f)) == 0); @@ -3471,14 +3512,14 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt editor.AddDecoration(rdcspv::OpDecorate( PSHitRTArray, rdcspv::DecorationParam(structStride * 5 + - sizeof(Vec4f) * 2))); + sizeof(Vec4f) * 3))); rdcspv::Id bufBase = editor.DeclareStructType({ // uint hit_count; uint32Type, - // uint test; + // uint total_count; uint32Type, - // + // // PSHit hits[]; PSHitRTArray, @@ -3553,6 +3594,8 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt // add the extension editor.AddExtension(storageMode == KHR_bda ? "SPV_KHR_physical_storage_buffer" : "SPV_EXT_physical_storage_buffer"); + if(useViewIndex) + editor.AddExtension("SPV_KHR_multiview"); // change the memory model to physical storage buffer 64 rdcspv::Iter it = editor.Begin(rdcspv::Section::MemoryModel); @@ -3878,12 +3921,44 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt ops.add(rdcspv::OpAccessChain(uint32BufPtr, editor.MakeId(), hit, {getUIntConst(2)})); ops.add(rdcspv::OpStore(storePtr, loaded, alignedAccess)); + if(viewIndex.base != rdcspv::Id()) + { + if(viewIndex.member == ~0U) + { + loaded = ops.add(rdcspv::OpLoad(viewIndex.type, editor.MakeId(), viewIndex.base)); + } + else + { + rdcspv::Id inPtrType = + editor.DeclareType(rdcspv::Pointer(viewIndex.type, rdcspv::StorageClass::Input)); + + rdcspv::Id viewidxptr = + ops.add(rdcspv::OpAccessChain(inPtrType, editor.MakeId(), viewIndex.base, + {editor.AddConstantImmediate(viewIndex.member)})); + loaded = ops.add(rdcspv::OpLoad(viewIndex.type, editor.MakeId(), viewidxptr)); + } + + // if it was loaded as signed int by the shader and not as unsigned by us, bitcast to + // unsigned. + if(viewIndex.type != uint32Type) + loaded = ops.add(rdcspv::OpBitcast(uint32Type, editor.MakeId(), loaded)); + } + else + { + // explicitly store 0 + loaded = getUIntConst(0); + } + storePtr = ops.add(rdcspv::OpAccessChain(uint32BufPtr, editor.MakeId(), hit, {getUIntConst(3)})); + ops.add(rdcspv::OpStore(storePtr, loaded, alignedAccess)); + + storePtr = + ops.add(rdcspv::OpAccessChain(uint32BufPtr, editor.MakeId(), hit, {getUIntConst(4)})); ops.add(rdcspv::OpStore(storePtr, editor.AddConstantImmediate(validMagicNumber), alignedAccess)); // store ddx(gl_FragCoord.x) to check that derivatives are working - storePtr = ops.add(rdcspv::OpAccessChain(floatBufPtr, editor.MakeId(), hit, {getUIntConst(4)})); + storePtr = ops.add(rdcspv::OpAccessChain(floatBufPtr, editor.MakeId(), hit, {getUIntConst(5)})); rdcspv::Id fragCoord_ddx_x = ops.add(rdcspv::OpCompositeExtract(floatType, editor.MakeId(), fragCoord_ddx, {0})); ops.add(rdcspv::OpStore(storePtr, fragCoord_ddx_x, alignedAccess)); @@ -3892,11 +3967,11 @@ static void CreatePSInputFetcher(rdcarray &fragspv, uint32_t &structSt rdcspv::Id inputPtrType = editor.DeclareType(rdcspv::Pointer(PSInput, bufferClass)); rdcspv::Id outputPtrs[Variant_Count] = { - ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(5)})), ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(6)})), ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(7)})), ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(8)})), ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(9)})), + ops.add(rdcspv::OpAccessChain(inputPtrType, editor.MakeId(), hit, {getUIntConst(10)})), }; for(size_t i = 0; i < values.size(); i++) @@ -4300,6 +4375,43 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ RDCLOG("useSampleID is %u because of bare capability", useSampleID); } + bool useViewIndex = (view == ~0U) ? false : true; + if(useViewIndex) + { + ResourceId rp = state.GetRenderPass(); + if(rp != ResourceId()) + { + const VulkanCreationInfo::RenderPass &rpInfo = + m_pDriver->GetDebugManager()->GetRenderPassInfo(rp); + for(auto it = rpInfo.subpasses.begin(); it != rpInfo.subpasses.end(); ++it) + { + if(it->multiviews.isEmpty()) + { + if(Vulkan_Debug_ShaderDebugLogging()) + RDCLOG( + "Disabling useViewIndex because at least one subpass does not have multiple views"); + useViewIndex = false; + break; + } + } + } + else + { + useViewIndex = pipe.viewMask != 0; + if(!useViewIndex && Vulkan_Debug_ShaderDebugLogging()) + RDCLOG("Disabling useViewIndex because viewMask is zero"); + } + } + else + { + if(Vulkan_Debug_ShaderDebugLogging()) + RDCLOG("Disabling useViewIndex from input view %u", view); + } + if(useViewIndex) + { + builtins[ShaderBuiltin::MultiViewIndex] = ShaderVariable(rdcstr(), view, 0U, 0U, 0U); + } + StorageMode storageMode = Binding; if(m_pDriver->GetExtensions(NULL).ext_KHR_buffer_device_address) @@ -4349,7 +4461,7 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ uint32_t structStride = 0; CreatePSInputFetcher(fragspv, structStride, shadRefl, paramAlign, storageMode, usePrimitiveID, - useSampleID); + useSampleID, useViewIndex); if(!Vulkan_Debug_PSDebugDumpDirPath().empty()) FileIO::WriteAll(Vulkan_Debug_PSDebugDumpDirPath() + "/debug_psinput_after.spv", fragspv); @@ -4489,8 +4601,8 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ } // create fragment shader with modified code - VkShaderModuleCreateInfo moduleCreateInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; + VkShaderModuleCreateInfo moduleCreateInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; VkSpecializationMapEntry specMaps[] = { { (uint32_t)InputSpecConstant::Address, @@ -4724,6 +4836,13 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_ continue; } + // if we're looking for a specific view, ignore hits from the wrong view + if(useViewIndex) + { + if(hit->view != view) + continue; + } + // see if this hit is a closer match than the previous winner. // if there's no previous winner it's clearly better