Fix handling of 3D attachments in pixel history

This commit is contained in:
baldurk
2025-10-27 11:50:35 +00:00
parent f12b2573ad
commit eb3136808d
3 changed files with 130 additions and 11 deletions
+24 -4
View File
@@ -30,6 +30,8 @@ cbuffer cb0 : register(b0)
bool is_float;
bool is_uint;
bool is_int;
bool is_3d;
};
cbuffer cb1 : register(b1)
@@ -47,12 +49,15 @@ Texture2DMSArray<uint2> copyin_stencil_ms : register(t3);
Texture2DArray<float4> copyin_float : register(t4);
Texture2DMSArray<float4> copyin_float_ms : register(t5);
Texture3D<float4> copyin_float_3d : register(t6);
Texture2DArray<uint4> copyin_uint : register(t6);
Texture2DMSArray<uint4> copyin_uint_ms : register(t7);
Texture2DArray<uint4> copyin_uint : register(t8);
Texture2DMSArray<uint4> copyin_uint_ms : register(t9);
Texture3D<uint4> copyin_uint_3d : register(t10);
Texture2DArray<int4> copyin_int : register(t8);
Texture2DMSArray<int4> copyin_int_ms : register(t9);
Texture2DArray<int4> copyin_int : register(t12);
Texture2DMSArray<int4> copyin_int_ms : register(t13);
Texture3D<int4> copyin_int_3d : register(t14);
RWBuffer<float4> copyout_depth : register(u0);
RWBuffer<float4> copyout_float : register(u1);
@@ -93,6 +98,21 @@ RWBuffer<int4> copyout_int : register(u3);
}
}
}
else if(is_3d)
{
if(is_float)
{
copyout_float[dst_slot] = copyin_float_3d.Load(src_coord.xywz);
}
else if(is_uint)
{
copyout_uint[dst_slot] = copyin_uint_3d.Load(src_coord.xywz);
}
else if(is_int)
{
copyout_int[dst_slot] = copyin_int_3d.Load(src_coord.xywz);
}
}
else
{
if(copy_depth || copy_stencil)
+85 -7
View File
@@ -137,6 +137,7 @@ struct D3D11CopyPixelParams
bool floatTex;
bool uintTex;
bool intTex;
bool tex3d;
UINT subres;
@@ -246,12 +247,14 @@ void D3D11DebugManager::PixelHistoryCopyPixel(D3D11CopyPixelParams &p, size_t ev
if(p.floatTex)
offs = 4;
else if(p.uintTex)
offs = 6;
else if(p.intTex)
offs = 8;
else if(p.intTex)
offs = 12;
if(p.multisampled)
offs++;
else if(p.tex3d)
offs += 2;
}
m_pImmediateContext->CSSetShaderResources(offs, 2, p.srv);
@@ -555,7 +558,7 @@ rdcarray<PixelModification> D3D11Replay::PixelHistory(rdcarray<EventUsage> event
ID3D11Texture2D *depthCopyD16 = NULL;
ID3D11ShaderResourceView *depthCopyD16_DepthSRV = NULL;
uint32_t srcxyData[8] = {
uint32_t srcxyData[12] = {
x,
y,
multisampled ? sampleIdx : mip,
@@ -565,14 +568,18 @@ rdcarray<PixelModification> D3D11Replay::PixelHistory(rdcarray<EventUsage> event
uint32_t(floatTex),
uint32_t(uintTex),
uint32_t(intTex),
uint32_t(details.texType == eTexType_3D),
};
uint32_t shadoutsrcxyData[8];
uint32_t shadoutsrcxyData[12];
memcpy(shadoutsrcxyData, srcxyData, sizeof(srcxyData));
// shadout texture doesn't have slices/mips, just one of the right dimension
shadoutsrcxyData[2] = multisampled ? sampleIdx : 0;
shadoutsrcxyData[3] = 0;
// and is not 3D
shadoutsrcxyData[8] = 0;
ID3D11Buffer *srcxyCBuf = GetDebugManager()->MakeCBuffer(sizeof(srcxyData));
ID3D11Buffer *shadoutsrcxyCBuf = GetDebugManager()->MakeCBuffer(sizeof(shadoutsrcxyData));
@@ -622,10 +629,16 @@ rdcarray<PixelModification> D3D11Replay::PixelHistory(rdcarray<EventUsage> event
colourCopyParams.intTex = intTex;
colourCopyParams.srcxyCBuf = srcxyCBuf;
colourCopyParams.storeCBuf = storeCBuf;
colourCopyParams.tex3d = false;
if(details.texType == eTexType_3D)
{
colourCopyParams.subres = mip;
colourCopyParams.tex3d = true;
}
else
{
colourCopyParams.subres = details.texMips * slice + mip;
}
D3D11CopyPixelParams depthCopyParams = colourCopyParams;
@@ -638,6 +651,7 @@ rdcarray<PixelModification> D3D11Replay::PixelHistory(rdcarray<EventUsage> event
depthCopyParams.depthcopy = true;
depthCopyParams.uav = pixstoreDepthUAV;
depthCopyParams.tex3d = false;
// while issuing the above queries we can check to see which tests are enabled so we don't
// bother checking if depth testing failed if the depth test was disabled
@@ -2015,11 +2029,13 @@ rdcarray<PixelModification> D3D11Replay::PixelHistory(rdcarray<EventUsage> event
shadoutCopyParams.subres = 0;
shadoutCopyParams.uav = shadoutStoreUAV;
shadoutCopyParams.srcxyCBuf = shadoutsrcxyCBuf;
shadoutCopyParams.tex3d = false;
depthCopyParams.sourceTex = depthCopyParams.srvTex = shaddepthOutput;
depthCopyParams.srv[0] = shaddepthOutputDepthSRV;
depthCopyParams.srv[1] = shaddepthOutputStencilSRV;
depthCopyParams.srcxyCBuf = shadoutsrcxyCBuf;
depthCopyParams.tex3d = false;
for(size_t h = 0; h < history.size(); h++)
{
@@ -2170,12 +2186,25 @@ rdcarray<PixelModification> D3D11Replay::PixelHistory(rdcarray<EventUsage> event
{
D3D11MarkerRegion middraw("fetching mid-action");
m_pImmediateContext->OMSetRenderTargets(rtIndex + 1, RTVs, shaddepthOutputDSV);
// 3D depth is not supported but we need depth for fragment counting - so as a bit of a compromise
// we fetch postmod via an extra shader output slot. This will not properly emulate precision or blending
if(details.texType == eTexType_3D)
{
ID3D11RenderTargetView *sparseRTVs[8] = {0};
sparseRTVs[rtIndex] = shadOutputRTV;
m_pImmediateContext->OMSetRenderTargets(rtIndex + 1, sparseRTVs, shaddepthOutputDSV);
}
else
{
m_pImmediateContext->OMSetRenderTargets(rtIndex + 1, RTVs, shaddepthOutputDSV);
}
m_pDevice->ReplayLog(0, history[h].eventId, eReplay_OnlyDraw);
GetDebugManager()->PixelHistoryCopyPixel(colourCopyParams, postColSlot, 0);
postColSlot++;
if(details.texType != eTexType_3D)
GetDebugManager()->PixelHistoryCopyPixel(colourCopyParams, postColSlot++, 0);
else
GetDebugManager()->PixelHistoryCopyPixel(shadoutCopyParams, shadColSlot++, 0);
GetDebugManager()->PixelHistoryCopyPixel(depthCopyParams, depthSlot, 1);
}
@@ -2301,11 +2330,60 @@ rdcarray<PixelModification> D3D11Replay::PixelHistory(rdcarray<EventUsage> event
{
lastMod = false;
// colour
if(details.texType != eTexType_3D)
{
uint32_t offsettedSlot = (postColSlot - discardedOffset);
byte *data = pixstoreData + sizeof(Vec4f) * pixstoreStride * offsettedSlot;
memcpy(&history[h].postMod.col.uintValue[0], data, sizeof(Vec4f));
}
else
{
// for 3D textures we copied to an extra shader output slot, because we needed a depth
// texture to count fragments and we can't bind depth with 3D
uint32_t offsettedSlot = (shadColSlot - discardedOffset);
RDCASSERT(discardedOffset <= shadColSlot);
byte *data = shadoutStoreData + sizeof(Vec4f) * pixstoreStride * offsettedSlot;
memcpy(&history[h].postMod.col.uintValue[0], data, sizeof(Vec4f));
shadColSlot++;
// this is a hack - we don't try to emulate precision but at least clamp representable ranges
ResourceFormat fmt = MakeResourceFormat(details.texFmt);
if(fmt.compType == CompType::UNorm)
{
for(uint32_t i = 0; i < 4; i++)
history[h].postMod.col.floatValue[i] =
RDCCLAMP(history[h].postMod.col.floatValue[i], 0.0f, 1.0f);
}
else if(fmt.compType == CompType::Float && fmt.compByteWidth == 2)
{
for(uint32_t i = 0; i < 4; i++)
history[h].postMod.col.floatValue[i] =
RDCCLAMP(history[h].postMod.col.floatValue[i], -65504.0f, 65504.0f);
}
else if(fmt.compType == CompType::UInt && fmt.compByteWidth < 4)
{
uint32_t maxVal = (1U << fmt.compByteWidth) - 1;
for(uint32_t i = 0; i < 4; i++)
history[h].postMod.col.uintValue[i] = RDCMIN(history[h].postMod.col.uintValue[i], maxVal);
}
else if(fmt.compType == CompType::SInt && fmt.compByteWidth == 2)
{
for(uint32_t i = 0; i < 4; i++)
history[h].postMod.col.intValue[i] =
RDCCLAMP(history[h].postMod.col.intValue[i], (int32_t)INT16_MIN, (int32_t)INT16_MAX);
}
else if(fmt.compType == CompType::SInt && fmt.compByteWidth == 1)
{
for(uint32_t i = 0; i < 4; i++)
history[h].postMod.col.intValue[i] =
RDCCLAMP(history[h].postMod.col.intValue[i], (int32_t)INT8_MIN, (int32_t)INT8_MAX);
}
for(uint32_t i = fmt.compCount; i < 4; i++)
history[h].postMod.col.uintValue[i] = 0;
}
{
uint32_t offsettedSlot = (depthSlot - discardedOffset);
@@ -186,6 +186,7 @@ struct PixelHistoryCallbackInfo
Subresource targetSubresource;
uint32_t x;
uint32_t y;
uint32_t z;
uint32_t sampleMask;
// Image used to get per fragment data.
@@ -1535,6 +1536,12 @@ protected:
// For pipeline barriers.
VkImageSubresourceRange subresource = {aspectFlags, baseMip, 1, baseSlice, 1};
if(p.srcImage == m_CallbackInfo.targetImage && (m_CallbackInfo.extent.depth > 1))
{
subresource.baseArrayLayer = baseSlice = 0;
subresource.layerCount = VK_REMAINING_ARRAY_LAYERS;
}
// For multi-sampled images can't call vkCmdCopyImageToBuffer directly,
// copy using a compute shader into a staging image first.
if(p.multisampled)
@@ -1589,6 +1596,11 @@ protected:
region.imageSubresource.mipLevel = baseMip;
region.imageSubresource.layerCount = 1;
if(p.srcImage == m_CallbackInfo.targetImage)
{
region.imageOffset.z = m_CallbackInfo.z;
}
if(!depthCopy)
{
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -4051,6 +4063,8 @@ bool VulkanDebugManager::PixelHistorySetupResources(PixelHistoryResources &resou
imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imgInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if(extent.depth > 1)
imgInfo.arrayLayers = extent.depth;
if(samples != VK_SAMPLE_COUNT_1_BIT)
imgInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
@@ -4543,6 +4557,7 @@ rdcarray<PixelModification> VulkanReplay::PixelHistory(rdcarray<EventUsage> even
callbackInfo.targetSubresource = sub;
callbackInfo.x = x;
callbackInfo.y = y;
callbackInfo.z = 0;
callbackInfo.sampleMask = sampleMask;
callbackInfo.subImage = resources.colorImage;
callbackInfo.subImageView = resources.colorImageView;
@@ -4552,6 +4567,12 @@ rdcarray<PixelModification> VulkanReplay::PixelHistory(rdcarray<EventUsage> even
callbackInfo.dstBuffer = resources.dstBuffer;
callbackInfo.multisampled = imginfo.samples != VK_SAMPLE_COUNT_1_BIT;
if(imginfo.type == VK_IMAGE_TYPE_3D)
{
callbackInfo.z = sub.slice;
callbackInfo.targetSubresource.slice = 0;
}
VulkanOcclusionCallback occlCb(m_pDriver, shaderCache, callbackInfo, occlusionPool, events);
{
VkMarkerRegion occlRegion("VulkanOcclusionCallback");