Fix pixel history getting confused if some (not all) fragments discard

This commit is contained in:
baldurk
2015-07-16 22:17:06 +02:00
parent 8a402f1a28
commit 5678ea7b29
4 changed files with 297 additions and 14 deletions
+4 -1
View File
@@ -381,12 +381,15 @@ extern "C" RENDERDOC_API void RENDERDOC_CC Camera_RotateArcball(Camera *c, float
extern "C" RENDERDOC_API void RENDERDOC_CC Camera_GetBasis(Camera *c, FloatVector *pos, FloatVector *fwd, FloatVector *right, FloatVector *up);
//////////////////////////////////////////////////////////////////////////
// Maths/format related exports
// Maths/format/misc related exports
//////////////////////////////////////////////////////////////////////////
extern "C" RENDERDOC_API float RENDERDOC_CC Maths_HalfToFloat(uint16_t half);
extern "C" RENDERDOC_API uint16_t RENDERDOC_CC Maths_FloatToHalf(float f);
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC Topology_NumVerticesPerPrimitive(PrimitiveTopology topology);
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC Topology_VertexOffset(PrimitiveTopology topology, uint32_t primitive);
//////////////////////////////////////////////////////////////////////////
// Create a replay renderer, for playback and analysis.
//
+155 -11
View File
@@ -3881,10 +3881,10 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
ID3D11Query *testQueries[6] = {0}; // one query for each test we do per-drawcall
uint32_t pixstoreStride = 3;
uint32_t pixstoreStride = 4;
// reserve 3 pixels per draw (worst case all events). This is used for Pre value, Post value and
// # frag overdraw. It's reused later to retrieve per-fragment post values.
// # frag overdraw (with & without original shader). It's reused later to retrieve per-fragment post values.
uint32_t pixstoreSlots = (uint32_t)(events.size() * pixstoreStride);
// need UAV compatible format, so switch B8G8R8A8 for R8G8B8A8, everything will
@@ -4712,8 +4712,6 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
m_WrappedDevice->ReplayLog(frameID, 0, events[ev].eventID, eReplay_OnlyDraw);
m_pImmediateContext->End(occl[ev]);
m_pImmediateContext->PSSetShader(curPS, curInst, curNumInst);
// determine how many fragments returned from the shader
if(!uavOutput)
@@ -4762,7 +4760,17 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
m_pImmediateContext->OMSetRenderTargets(0, NULL, shaddepthOutputDSV);
// replay first with overlay shader. This is guaranteed to count all fragments
m_WrappedDevice->ReplayLog(frameID, 0, events[ev].eventID, eReplay_OnlyDraw);
PixelHistoryCopyPixel(params, storex*pixstoreStride + 2, storey);
m_pImmediateContext->PSSetShader(curPS, curInst, curNumInst);
m_pImmediateContext->ClearDepthStencilView(shaddepthOutputDSV, D3D11_CLEAR_STENCIL, 1.0f, 0);
// now replay with original shader. Some fragments may discard and not be counted
m_WrappedDevice->ReplayLog(frameID, 0, events[ev].eventID, eReplay_OnlyDraw);
PixelHistoryCopyPixel(params, storex*pixstoreStride + 3, storey);
UINT initCounts[D3D11_1_UAV_SLOT_COUNT];
memset(&initCounts[0], 0xff, sizeof(initCounts));
@@ -4775,8 +4783,10 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
for(int i=0; i < D3D11_1_UAV_SLOT_COUNT; i++)
SAFE_RELEASE(prevUAVs[i]);
SAFE_RELEASE(prevDSV);
PixelHistoryCopyPixel(params, storex*pixstoreStride + 2, storey);
}
else
{
m_pImmediateContext->PSSetShader(curPS, curInst, curNumInst);
}
m_pImmediateContext->RSSetState(curRS);
@@ -5504,7 +5514,10 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
mod.postMod.stencil = int32_t(data[3]);
// data[4] unused
mod.shaderOut.col.value_i[0] = int32_t(data[5]); // fragments writing to the pixel in this event
mod.shaderOut.col.value_i[0] = int32_t(data[5]); // fragments writing to the pixel in this event with overlay shader
// data[6] unused
mod.shaderOut.col.value_i[1] = int32_t(data[7]); // fragments writing to the pixel in this event with original shader
}
}
@@ -5518,6 +5531,11 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
for(size_t h=0; h < history.size(); )
{
int32_t frags = RDCMAX(1, history[h].shaderOut.col.value_i[0]);
int32_t fragsClipped = RDCCLAMP(history[h].shaderOut.col.value_i[1], 1, frags);
// if we have fewer fragments with the original shader, some discarded
// so we need to do a thorough check to see which fragments discarded
bool someFragsClipped = (fragsClipped < frags);
PixelModification mod = history[h];
@@ -5525,7 +5543,10 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
history.insert(history.begin()+h+1, mod);
for(int32_t f=0; f < frags; f++)
{
history[h+f].fragIndex = f;
history[h+f].primitiveID = someFragsClipped;
}
h += frags;
}
@@ -5781,6 +5802,14 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
shadColSlot = 0;
depthSlot = 0;
prev = 0;
// this is used to track if any previous fragments in the current draw
// discarded. If so, the shader output values will be off-by-one in the
// shader output storage due to stencil counting errors, and we need to
// offset.
uint32_t discardedOffset = 0;
for(size_t h=0; h < history.size(); h++)
{
const FetchDrawcall *draw = m_WrappedDevice->GetDrawcall(frameID, history[h].eventID);
@@ -5879,22 +5908,37 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
{
history[h].preMod = history[h-1].postMod;
}
// reset discarded offset every event
if(h > 0 && history[h].eventID != history[h-1].eventID)
{
discardedOffset = 0;
}
// fetch shader output value
{
// colour
{
// shader output is always 4 32bit components, so we can copy straight
byte *rowdata = shadoutStoreData + mappedShadout.RowPitch * (shadColSlot/2048);
byte *data = rowdata + 4 * sizeof(float) * (shadColSlot%2048);
// Note that because shader output values are interleaved with
// primitive IDs, the discardedOffset is doubled when looking at
// shader output values
uint32_t offsettedSlot = (shadColSlot - discardedOffset*2);
RDCASSERT(discardedOffset*2 <= shadColSlot);
byte *rowdata = shadoutStoreData + mappedShadout.RowPitch * (offsettedSlot/2048);
byte *data = rowdata + 4 * sizeof(float) * (offsettedSlot%2048);
memcpy(&history[h].shaderOut.col.value_u[0], data, 4*sizeof(float));
}
// depth
{
byte *rowdata = pixstoreDepthData + mappedDepth.RowPitch * (depthSlot/2048);
float *data = (float *)(rowdata + 2 * sizeof(float) * (depthSlot%2048));
uint32_t offsettedSlot = (depthSlot - discardedOffset);
RDCASSERT(discardedOffset <= depthSlot);
byte *rowdata = pixstoreDepthData + mappedDepth.RowPitch * (offsettedSlot/2048);
float *data = (float *)(rowdata + 2 * sizeof(float) * (offsettedSlot%2048));
history[h].shaderOut.depth = data[0];
if(history[h].postMod.stencil == -1)
@@ -5912,10 +5956,110 @@ vector<PixelModification> D3D11DebugManager::PixelHistory(uint32_t frameID, vect
// shader output is always 4 32bit components, so we can copy straight
byte *rowdata = shadoutStoreData + mappedShadout.RowPitch * (shadColSlot/2048);
byte *data = rowdata + 4 * sizeof(float) * (shadColSlot%2048);
bool someFragsClipped = history[h].primitiveID != 0;
memcpy(&history[h].primitiveID, data, sizeof(uint32_t));
shadColSlot++;
// if some fragments clipped in this draw, we need to check to see if this
// primitive ID was one of the ones that clipped.
// Currently the way we do that is by drawing only that primitive
// and doing a
if(someFragsClipped)
{
// don't need to worry about trashing state, since at this point we don't need to restore it anymore
if(prev != history[h].eventID)
{
m_WrappedDevice->ReplayLog(frameID, 0, history[h].eventID, eReplay_WithoutDraw);
//////////////////////////////////////////////////////////////
// Set up an identical raster state, but with scissor enabled.
// This matches the setup when we were originally fetching the
// number of fragments.
m_pImmediateContext->RSGetState(&curRS);
D3D11_RASTERIZER_DESC rsDesc = {
/*FillMode =*/ D3D11_FILL_SOLID,
/*CullMode =*/ D3D11_CULL_BACK,
/*FrontCounterClockwise =*/ FALSE,
/*DepthBias =*/ D3D11_DEFAULT_DEPTH_BIAS,
/*DepthBiasClamp =*/ D3D11_DEFAULT_DEPTH_BIAS_CLAMP,
/*SlopeScaledDepthBias =*/ D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
/*DepthClipEnable =*/ TRUE,
/*ScissorEnable =*/ FALSE,
/*MultisampleEnable =*/ FALSE,
/*AntialiasedLineEnable =*/ FALSE,
};
if(curRS)
curRS->GetDesc(&rsDesc);
SAFE_RELEASE(curRS);
rsDesc.ScissorEnable = TRUE;
// scissor to our pixel
newScissors[0].left = LONG(x);
newScissors[0].top = LONG(y);
newScissors[0].right = newScissors[0].left+1;
newScissors[0].bottom = newScissors[0].top+1;
m_pImmediateContext->RSSetScissorRects(1, newScissors);
m_pDevice->CreateRasterizerState(&rsDesc, &newRS);
m_pImmediateContext->RSSetState(newRS);
// other states can just be set to always pass, we already know this primitive ID renders
m_pImmediateContext->OMSetBlendState(m_DebugRender.NopBlendState, blendFactor, sampleMask);
m_pImmediateContext->OMSetRenderTargets(0, NULL, shaddepthOutputDSV);
m_pImmediateContext->OMSetDepthStencilState(m_DebugRender.AllPassDepthState, 0);
SAFE_RELEASE(newRS);
}
prev = history[h].eventID;
m_pImmediateContext->ClearDepthStencilView(shaddepthOutputDSV, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 0.0f, 0);
m_pImmediateContext->Begin(testQueries[0]);
// do draw
const FetchDrawcall *draw = m_WrappedDevice->GetDrawcall(frameID, history[h].eventID);
if(draw->flags & eDraw_UseIBuffer)
{
// TODO once pixel history distinguishes between instances, draw only the instance for this fragment
m_pImmediateContext->DrawIndexedInstanced(Topology_NumVerticesPerPrimitive(draw->topology),
RDCMAX(1U, draw->numInstances),
draw->indexOffset + Topology_VertexOffset(draw->topology, history[h].primitiveID),
draw->vertexOffset, draw->instanceOffset);
}
else
{
m_pImmediateContext->DrawInstanced(Topology_NumVerticesPerPrimitive(draw->topology),
RDCMAX(1U, draw->numInstances),
draw->vertexOffset + Topology_VertexOffset(draw->topology, history[h].primitiveID),
draw->instanceOffset);
}
m_pImmediateContext->End(testQueries[0]);
do
{
hr = m_pImmediateContext->GetData(testQueries[0], &occlData, sizeof(occlData), 0);
} while(hr == S_FALSE);
RDCASSERT(hr == S_OK);
if(occlData == 0)
{
history[h].shaderDiscarded = true;
discardedOffset++;
RDCEraseEl(history[h].shaderOut);
history[h].shaderOut.depth = -1.0f;
history[h].shaderOut.stencil = -1;
}
}
}
}
+128
View File
@@ -31,6 +31,134 @@
#include "replay/replay_renderer.h"
#include "api/replay/renderdoc_replay.h"
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC Topology_NumVerticesPerPrimitive(PrimitiveTopology topology)
{
// strips/loops/fans have the same number of indices for a single primitive
// as their list friends
switch(topology)
{
default:
case eTopology_Unknown:
break;
case eTopology_PointList:
return 1;
case eTopology_LineList:
case eTopology_LineStrip:
case eTopology_LineLoop:
return 2;
case eTopology_TriangleList:
case eTopology_TriangleStrip:
case eTopology_TriangleFan:
return 3;
case eTopology_LineList_Adj:
case eTopology_LineStrip_Adj:
return 4;
case eTopology_TriangleList_Adj:
case eTopology_TriangleStrip_Adj:
return 6;
case eTopology_PatchList_1CPs:
case eTopology_PatchList_2CPs:
case eTopology_PatchList_3CPs:
case eTopology_PatchList_4CPs:
case eTopology_PatchList_5CPs:
case eTopology_PatchList_6CPs:
case eTopology_PatchList_7CPs:
case eTopology_PatchList_8CPs:
case eTopology_PatchList_9CPs:
case eTopology_PatchList_10CPs:
case eTopology_PatchList_11CPs:
case eTopology_PatchList_12CPs:
case eTopology_PatchList_13CPs:
case eTopology_PatchList_14CPs:
case eTopology_PatchList_15CPs:
case eTopology_PatchList_16CPs:
case eTopology_PatchList_17CPs:
case eTopology_PatchList_18CPs:
case eTopology_PatchList_19CPs:
case eTopology_PatchList_20CPs:
case eTopology_PatchList_21CPs:
case eTopology_PatchList_22CPs:
case eTopology_PatchList_23CPs:
case eTopology_PatchList_24CPs:
case eTopology_PatchList_25CPs:
case eTopology_PatchList_26CPs:
case eTopology_PatchList_27CPs:
case eTopology_PatchList_28CPs:
case eTopology_PatchList_29CPs:
case eTopology_PatchList_30CPs:
case eTopology_PatchList_31CPs:
case eTopology_PatchList_32CPs:
return uint32_t(topology - eTopology_PatchList_1CPs + 1);
}
return 0;
}
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC Topology_VertexOffset(PrimitiveTopology topology, uint32_t primitive)
{
// strips/loops/fans have the same number of indices for a single primitive
// as their list friends
switch(topology)
{
default:
case eTopology_Unknown:
case eTopology_PointList:
case eTopology_LineList:
case eTopology_TriangleList:
case eTopology_LineList_Adj:
case eTopology_TriangleList_Adj:
case eTopology_PatchList_1CPs:
case eTopology_PatchList_2CPs:
case eTopology_PatchList_3CPs:
case eTopology_PatchList_4CPs:
case eTopology_PatchList_5CPs:
case eTopology_PatchList_6CPs:
case eTopology_PatchList_7CPs:
case eTopology_PatchList_8CPs:
case eTopology_PatchList_9CPs:
case eTopology_PatchList_10CPs:
case eTopology_PatchList_11CPs:
case eTopology_PatchList_12CPs:
case eTopology_PatchList_13CPs:
case eTopology_PatchList_14CPs:
case eTopology_PatchList_15CPs:
case eTopology_PatchList_16CPs:
case eTopology_PatchList_17CPs:
case eTopology_PatchList_18CPs:
case eTopology_PatchList_19CPs:
case eTopology_PatchList_20CPs:
case eTopology_PatchList_21CPs:
case eTopology_PatchList_22CPs:
case eTopology_PatchList_23CPs:
case eTopology_PatchList_24CPs:
case eTopology_PatchList_25CPs:
case eTopology_PatchList_26CPs:
case eTopology_PatchList_27CPs:
case eTopology_PatchList_28CPs:
case eTopology_PatchList_29CPs:
case eTopology_PatchList_30CPs:
case eTopology_PatchList_31CPs:
case eTopology_PatchList_32CPs:
// for all lists, it's just primitive * Topology_NumVerticesPerPrimitive(topology)
break;
case eTopology_LineStrip:
case eTopology_LineLoop:
case eTopology_TriangleStrip:
case eTopology_LineStrip_Adj:
// for strips, each new vertex creates a new primitive
return primitive;
case eTopology_TriangleStrip_Adj:
// triangle strip with adjacency is a special case as every other
// vert is purely for adjacency so it's doubled
return primitive*2;
case eTopology_TriangleFan:
RDCERR("Cannot get VertexOffset for triangle fan!");
break;
}
return primitive * Topology_NumVerticesPerPrimitive(topology);
}
extern "C" RENDERDOC_API float RENDERDOC_CC Maths_HalfToFloat(uint16_t half)
{
return ConvertFromHalf(half);
+10 -2
View File
@@ -284,6 +284,9 @@ namespace renderdocui.Windows
{
string name = String.Format("Primitive {0}\n", mod.primitiveID);
if (mod.shaderDiscarded)
name += FailureString(mod);
ResourceFormat fmt = new ResourceFormat(floatTex ? FormatComponentType.Float : texture.format.compType, 4, 4);
string shadOutVal = "Shader Out\n\n" + ModificationValueString(mod.shaderOut, fmt, depth);
@@ -293,6 +296,9 @@ namespace renderdocui.Windows
return null;
node = new TreelistView.Node(new object[] { name, shadOutVal, "", postModVal, "" });
if (mod.shaderDiscarded)
node.DefaultBackColor = Color.FromArgb(255, 235, 235);
}
node.Tag = new EventTag(mod.eventID, mod.uavWrite ? uint.MaxValue : mod.primitiveID);
@@ -368,9 +374,11 @@ namespace renderdocui.Windows
}
else
{
name += String.Format("EID {0}\n{1}{2}\n{3} Fragments touching pixel\n", mods[0].eventID, drawcall.name, FailureString(mods[0]), nodes.Count);
passed = mods.Any(m => m.EventPassed());
passed = mods[0].EventPassed();
string failure = passed ? "" : FailureString(mods[0]);
name += String.Format("EID {0}\n{1}{2}\n{3} Fragments touching pixel\n", mods[0].eventID, drawcall.name, failure, nodes.Count);
}
string preModVal = "Tex Before\n\n" + ModificationValueString(mods.First().preMod, texture.format, depth);