diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 70fe76bdd..2090a7bd1 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -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. // diff --git a/renderdoc/driver/d3d11/d3d11_analyse.cpp b/renderdoc/driver/d3d11/d3d11_analyse.cpp index d217b9625..97cc3fba4 100644 --- a/renderdoc/driver/d3d11/d3d11_analyse.cpp +++ b/renderdoc/driver/d3d11/d3d11_analyse.cpp @@ -3881,10 +3881,10 @@ vector 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 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 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 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 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 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 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 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 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 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; + } + } } } diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index f0bb93edb..bc236b6da 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -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); diff --git a/renderdocui/Windows/PixelHistoryView.cs b/renderdocui/Windows/PixelHistoryView.cs index e8c7f1214..15201e622 100644 --- a/renderdocui/Windows/PixelHistoryView.cs +++ b/renderdocui/Windows/PixelHistoryView.cs @@ -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);