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
This commit is contained in:
Jake Turner
2024-01-17 10:46:32 +00:00
parent b36b7cf6b8
commit 3cb8998841
8 changed files with 145 additions and 19 deletions
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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;
+2 -1
View File
@@ -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.
+3 -1
View File
@@ -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)
+2 -1
View File
@@ -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);
+2 -2
View File
@@ -851,10 +851,10 @@ struct CaptureContextInvoker : ObjectForwarder<ICaptureContext>
}
virtual IPixelHistoryView *ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y,
const TextureDisplay &display) override
uint32_t view, const TextureDisplay &display) override
{
return InvokeRetFunction<IPixelHistoryView *>(&ICaptureContext::ViewPixelHistory, texID, x, y,
display);
view, display);
}
virtual QWidget *CreateBuiltinWindow(const rdcstr &objectName) override
+5 -2
View File
@@ -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);
+128 -9
View File
@@ -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<uint32_t> &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<uint32_t> &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<uint32_t> &fragspv, uint32_t &structSt
? editor.DeclareType(rdcspv::scalar<int32_t>())
: editor.DeclareType(rdcspv::scalar<uint32_t>());
}
else if(param.systemValue == ShaderBuiltin::MultiViewIndex)
{
access = &viewIndex;
access->type = VarTypeCompType(param.varType) == CompType::SInt
? editor.DeclareType(rdcspv::scalar<int32_t>())
: editor.DeclareType(rdcspv::scalar<uint32_t>());
}
if(access)
{
@@ -3276,6 +3286,25 @@ static void CreatePSInputFetcher(rdcarray<uint32_t> &fragspv, uint32_t &structSt
editor.AddCapability(rdcspv::Capability::SampleRateShading);
}
if(viewIndex.base == rdcspv::Id() && useViewIndex)
{
rdcspv::Id type = editor.DeclareType(rdcspv::scalar<uint32_t>());
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::Decoration::BuiltIn>(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<uint32_t> &fragspv, uint32_t &structSt
uint32Type,
// uint sample;
uint32Type,
// uint view;
uint32Type,
// uint valid;
uint32Type,
// float ddxDerivCheck;
floatType,
// <uint3 padding>
// IN
PSInput,
@@ -3421,6 +3453,12 @@ static void CreatePSInputFetcher(rdcarray<uint32_t> &fragspv, uint32_t &structSt
offs += sizeof(uint32_t);
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "view");
offs += sizeof(uint32_t);
member++;
editor.AddDecoration(rdcspv::OpMemberDecorate(
PSHit, member, rdcspv::DecorationParam<rdcspv::Decoration::Offset>(offs)));
editor.SetMemberName(PSHit, member, "valid");
@@ -3433,6 +3471,9 @@ static void CreatePSInputFetcher(rdcarray<uint32_t> &fragspv, uint32_t &structSt
offs += sizeof(uint32_t);
member++;
// <uint3 padding>
offs += sizeof(uint32_t) * 3;
RDCASSERT((offs % sizeof(Vec4f)) == 0);
RDCASSERT((structStride % sizeof(Vec4f)) == 0);
@@ -3471,14 +3512,14 @@ static void CreatePSInputFetcher(rdcarray<uint32_t> &fragspv, uint32_t &structSt
editor.AddDecoration(rdcspv::OpDecorate(
PSHitRTArray, rdcspv::DecorationParam<rdcspv::Decoration::ArrayStride>(structStride * 5 +
sizeof(Vec4f) * 2)));
sizeof(Vec4f) * 3)));
rdcspv::Id bufBase = editor.DeclareStructType({
// uint hit_count;
uint32Type,
// uint test;
// uint total_count;
uint32Type,
// <uint3 padding>
// <uint2 padding>
// PSHit hits[];
PSHitRTArray,
@@ -3553,6 +3594,8 @@ static void CreatePSInputFetcher(rdcarray<uint32_t> &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<uint32_t> &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<uint32_t> &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