diff --git a/renderdoc/driver/d3d11/d3d11_analyse.cpp b/renderdoc/driver/d3d11/d3d11_analyse.cpp index 318a3647a..24cef7a72 100644 --- a/renderdoc/driver/d3d11/d3d11_analyse.cpp +++ b/renderdoc/driver/d3d11/d3d11_analyse.cpp @@ -2730,7 +2730,9 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect vector occl; occl.reserve(events.size()); - // reserve 4 pixels per draw on average. Pre/Shaderout/Post required at minimum, + ID3D11Query *testQueries[6] = {0}; // one query for each test we do per-drawcall + + // reserve 6 pixels per draw on average. Pre/Shaderout/Post required at minimum, // and hopefully overdraw within draws will average to only 2 times. uint32_t pixstoreSlots = (uint32_t)(events.size() * 6); @@ -2776,8 +2778,9 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect // additional flags we can trivially detect on the CPU for edge cases TestMustFail_Scissor = 1<<6, // if the scissor is enabled, pixel lies outside all regions (could be only one) - TestMustFail_DepthTesting = 1<<7, // if the comparison func is NEVER - TestMustFail_StencilTesting = 1<<8, // if the comparison func is NEVER for both faces, or one face is backface culled and the other is NEVER + TestMustPass_Scissor = 1<<7, // if the scissor is enabled, pixel lies inside all regions (could be only one) + TestMustFail_DepthTesting = 1<<8, // if the comparison func is NEVER + TestMustFail_StencilTesting = 1<<9, // if the comparison func is NEVER for both faces, or one face is backface culled and the other is NEVER }; #if 1 @@ -2796,6 +2799,9 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect m_pDevice->CreateQuery(&occlDesc, &q); occl.push_back(q); } + + for(size_t i=0; i < ARRAY_COUNT(testQueries); i++) + m_pDevice->CreateQuery(&occlDesc, &testQueries[i]); D3D11_BLEND_DESC nopBlendStateDesc = {0}; // no blending enabled anywhere, 0 write mask ID3D11BlendState *nopBlendState = NULL; @@ -2820,6 +2826,7 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect ID3D11RasterizerState *curRS = NULL; ID3D11RasterizerState *newRS = NULL; + ID3D11DepthStencilState *newDS = NULL; ID3D11PixelShader *curPS = NULL; ID3D11ClassInstance *curInst[D3D11_SHADER_MAX_INTERFACES] = { NULL }; UINT curNumInst = 0; @@ -2931,6 +2938,7 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect { // see if we can find at least one scissor region this pixel could fall into bool inRegion = false; + bool inAllRegions = true; for(UINT i=0; i < curNumScissors && i < curNumViews; i++) { @@ -2941,12 +2949,17 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect ) { inRegion = true; - break; + } + else + { + inAllRegions = false; } } if(!inRegion) flags[ev] |= TestMustFail_Scissor; + if(inAllRegions) + flags[ev] |= TestMustPass_Scissor; } if(curBS) @@ -3022,6 +3035,8 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect m_pImmediateContext->End(occl[ev]); + // TODO, if drawcall has side-effects (i.e UAVs bound) replay from start of log here? + m_pImmediateContext->PSSetShader(curPS, curInst, curNumInst); m_pImmediateContext->RSSetState(curRS); m_pImmediateContext->RSSetScissorRects(curNumScissors, curScissors); @@ -3214,11 +3229,399 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect } } + // complex case - need to determine how many fragments from this draw wrote to the pixel and generate a + // PixelModification event for all of them. + // + // Is there any value in doing this anyway for non-blended cases where other fragments are completely discarded? + if(flags[i] & Blending_Enabled) + { + } + + if(flags[i] & TestMustFail_DepthTesting) + mod.depthTestFailed = true; + if(flags[i] & TestMustFail_StencilTesting) + mod.stencilTestFailed = true; + if(flags[i] & TestMustFail_Scissor) + mod.scissorClipped = true; + + bool dirty = false; + + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw); + + curNumScissors = curNumViews = 16; + m_pImmediateContext->RSGetViewports(&curNumViews, curViewports); + m_pImmediateContext->RSGetScissorRects(&curNumScissors, curScissors); + m_pImmediateContext->RSGetState(&curRS); + m_pImmediateContext->OMGetDepthStencilState(&curDS, &stencilRef); + blendFactor[0] = blendFactor[1] = blendFactor[2] = blendFactor[3] = 1.0f; + curSample = ~0U; + + D3D11_RASTERIZER_DESC rdesc = { + /*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(&rdesc); + + D3D11_DEPTH_STENCIL_DESC dsdesc = { + /*DepthEnable =*/ TRUE, + /*DepthWriteMask =*/ D3D11_DEPTH_WRITE_MASK_ALL, + /*DepthFunc =*/ D3D11_COMPARISON_LESS, + /*StencilEnable =*/ FALSE, + /*StencilReadMask =*/ D3D11_DEFAULT_STENCIL_READ_MASK, + /*StencilWriteMask =*/ D3D11_DEFAULT_STENCIL_WRITE_MASK, + /*FrontFace =*/ { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS }, + /*BackFace =*/ { D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_STENCIL_OP_KEEP, D3D11_COMPARISON_ALWAYS }, + }; + + if(curDS) + curDS->GetDesc(&dsdesc); + + for(UINT v=0; v < curNumViews; v++) + { + // calculate scissor, relative to this viewport, that encloses only (x,y) pixel + + // if (x,y) pixel isn't in viewport, make empty rect) + if(xf < curViewports[v].TopLeftX || + yf < curViewports[v].TopLeftY || + xf >= curViewports[v].TopLeftX + curViewports[v].Width || + yf >= curViewports[v].TopLeftY + curViewports[v].Height) + { + newScissors[v].left = newScissors[v].top = newScissors[v].bottom = newScissors[v].right = 0; + } + else + { + newScissors[v].left = LONG(xf - curViewports[v].TopLeftX); + newScissors[v].top = LONG(yf - curViewports[v].TopLeftY); + newScissors[v].right = newScissors[v].left+1; + newScissors[v].bottom = newScissors[v].top+1; + } + } + + // for each test we only disable pipeline rejection tests that fall *after* it. + // e.g. to get an idea if a pixel failed backface culling or not, we enable only backface + // culling and disable everything else (since it happens first). + // For depth testing, we leave all tests enabled up to then - as we only want to know which + // pixels were rejected by the depth test, not pixels that might have passed the depth test + // had they not been discarded earlier by backface culling or depth clipping. + + if(flags[i] & TestEnabled_BackfaceCulling) + { + D3D11_RASTERIZER_DESC rd = rdesc; + + rd.ScissorEnable = TRUE; + rd.DepthClipEnable = FALSE; + // leave backface culling mode as normal + + m_pDevice->CreateRasterizerState(&rd, &newRS); + + if(dirty) + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw); + + m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0); + m_pImmediateContext->OMSetBlendState(nopBlendState, blendFactor, curSample); + m_pImmediateContext->OMSetDepthStencilState(nopDSState, stencilRef); + m_pImmediateContext->RSSetState(newRS); + m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors); + + m_pImmediateContext->Begin(testQueries[0]); + + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw); + dirty = true; + + m_pImmediateContext->End(testQueries[0]); + + SAFE_RELEASE(newRS); + } + + if(flags[i] & TestEnabled_DepthClip) + { + D3D11_RASTERIZER_DESC rd = rdesc; + + rd.ScissorEnable = TRUE; + // leave depth clip mode as normal + // leave backface culling mode as normal + + m_pDevice->CreateRasterizerState(&rd, &newRS); + + if(dirty) + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw); + + m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0); + m_pImmediateContext->OMSetBlendState(nopBlendState, blendFactor, curSample); + m_pImmediateContext->OMSetDepthStencilState(nopDSState, stencilRef); + m_pImmediateContext->RSSetState(newRS); + m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors); + + m_pImmediateContext->Begin(testQueries[1]); + + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw); + dirty = true; + + m_pImmediateContext->End(testQueries[1]); + + SAFE_RELEASE(newRS); + } + + // only check scissor if test is enabled and we don't know if it's pass or fail yet + if((flags[i] & (TestEnabled_Scissor|TestMustPass_Scissor|TestMustFail_Scissor)) == TestEnabled_Scissor) + { + D3D11_RASTERIZER_DESC rd = rdesc; + + rd.ScissorEnable = TRUE; + // leave depth clip mode as normal + // leave backface culling mode as normal + + // newScissors has scissor regions calculated to hit our target pixel on every viewport, but we must + // intersect that with the original scissors regions for correct testing behaviour. + // This amounts to making any scissor region that doesn't overlap with the target pixel empty. + // + // Note that in the case of only one scissor region we can trivially detect pass/fail of the test against + // our pixel on the CPU so we won't come in here (see check above against MustFail/MustPass). So we will + // only do this in the case where we have multiple scissor regions/viewports, some intersecting the pixel + // and some not. So we make the not intersecting scissor regions empty so our occlusion query tests to see + // if any pixels were written to the "passing" viewports + D3D11_RECT intersectScissors[16] = {0}; + memcpy(intersectScissors, newScissors, sizeof(intersectScissors)); + + for(UINT s=0; s < curNumScissors; s++) + { + if(curScissors[i].left > newScissors[i].left || + curScissors[i].right < newScissors[i].right || + curScissors[i].top > newScissors[i].top || + curScissors[i].bottom < newScissors[i].bottom) + { + // scissor region from the log doesn't touch our target pixel, make empty. + intersectScissors[i].left = intersectScissors[i].right = intersectScissors[i].top = intersectScissors[i].bottom = 0; + } + } + + m_pDevice->CreateRasterizerState(&rd, &newRS); + + if(dirty) + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw); + + m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0); + m_pImmediateContext->OMSetBlendState(nopBlendState, blendFactor, curSample); + m_pImmediateContext->OMSetDepthStencilState(nopDSState, stencilRef); + m_pImmediateContext->RSSetState(newRS); + m_pImmediateContext->RSSetScissorRects(curNumScissors, intersectScissors); + + m_pImmediateContext->Begin(testQueries[2]); + + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw); + dirty = true; + + m_pImmediateContext->End(testQueries[2]); + + SAFE_RELEASE(newRS); + } + + // test shader discard + { + D3D11_RASTERIZER_DESC rd = rdesc; + + rd.ScissorEnable = TRUE; + // leave depth clip mode as normal + // leave backface culling mode as normal + + m_pDevice->CreateRasterizerState(&rd, &newRS); + + if(dirty) + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw); + + m_pImmediateContext->OMSetBlendState(nopBlendState, blendFactor, curSample); + m_pImmediateContext->OMSetDepthStencilState(nopDSState, stencilRef); + m_pImmediateContext->RSSetState(newRS); + m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors); + + m_pImmediateContext->Begin(testQueries[3]); + + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw); + dirty = true; + + m_pImmediateContext->End(testQueries[3]); + + SAFE_RELEASE(newRS); + } + + if(flags[i] & TestEnabled_DepthTesting) + { + D3D11_RASTERIZER_DESC rd = rdesc; + + rd.ScissorEnable = TRUE; + // leave depth clip mode as normal + // leave backface culling mode as normal + + m_pDevice->CreateRasterizerState(&rd, &newRS); + + D3D11_DEPTH_STENCIL_DESC dsd = dsdesc; + + dsd.StencilEnable = FALSE; + dsd.StencilReadMask = 0; + dsd.StencilWriteMask = 0; + + m_pDevice->CreateDepthStencilState(&dsd, &newDS); + + if(dirty) + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw); + + m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0); + m_pImmediateContext->OMSetBlendState(nopBlendState, blendFactor, curSample); + m_pImmediateContext->OMSetDepthStencilState(newDS, stencilRef); + m_pImmediateContext->RSSetState(newRS); + m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors); + + m_pImmediateContext->Begin(testQueries[4]); + + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw); + dirty = true; + + m_pImmediateContext->End(testQueries[4]); + + SAFE_RELEASE(newRS); + SAFE_RELEASE(newDS); + } + + if(flags[i] & TestEnabled_StencilTesting) + { + D3D11_RASTERIZER_DESC rd = rdesc; + + rd.ScissorEnable = TRUE; + rd.DepthClipEnable = FALSE; + rd.CullMode = D3D11_CULL_NONE; + + m_pDevice->CreateRasterizerState(&rd, &newRS); + + // leave depthstencil testing exactly as is, because a depth-fail means + // stencil isn't run + m_pDevice->CreateDepthStencilState(&dsdesc, &newDS); + + if(dirty) + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_WithoutDraw); + + m_pImmediateContext->PSSetShader(m_DebugRender.OverlayPS, NULL, 0); + m_pImmediateContext->OMSetBlendState(nopBlendState, blendFactor, curSample); + m_pImmediateContext->OMSetDepthStencilState(newDS, stencilRef); + m_pImmediateContext->RSSetState(newRS); + m_pImmediateContext->RSSetScissorRects(curNumViews, newScissors); + + m_pImmediateContext->Begin(testQueries[5]); + + m_WrappedDevice->ReplayLog(frameID, 0, events[i], eReplay_OnlyDraw); + dirty = true; + + m_pImmediateContext->End(testQueries[5]); + + SAFE_RELEASE(newRS); + SAFE_RELEASE(newDS); + } + + SAFE_RELEASE(curRS); + mod.shaderOut.value_f[0] = mod.postMod.value_f[0]; mod.shaderOut.value_f[1] = mod.postMod.value_f[1]; mod.shaderOut.value_f[2] = mod.postMod.value_f[2]; mod.shaderOut.value_f[3] = mod.postMod.value_f[3]; + // we check these in the order defined, as a positive from the backface cull test + // will invalidate tests later (as they will also be backface culled) + + do + { + + if(flags[i] & TestEnabled_BackfaceCulling) + { + do + { + hr = m_pImmediateContext->GetData(testQueries[0], &occlData, sizeof(occlData), 0); + } while(hr == S_FALSE); + RDCASSERT(hr == S_OK); + + mod.backfaceCulled = (occlData == 0); + + if(mod.backfaceCulled) + break; + } + + if(flags[i] & TestEnabled_DepthClip) + { + do + { + hr = m_pImmediateContext->GetData(testQueries[1], &occlData, sizeof(occlData), 0); + } while(hr == S_FALSE); + RDCASSERT(hr == S_OK); + + mod.depthClipped = (occlData == 0); + + if(mod.depthClipped) + break; + } + + if(!mod.backfaceCulled && (flags[i] & (TestEnabled_Scissor|TestMustPass_Scissor|TestMustFail_Scissor)) == TestEnabled_Scissor) + { + do + { + hr = m_pImmediateContext->GetData(testQueries[2], &occlData, sizeof(occlData), 0); + } while(hr == S_FALSE); + RDCASSERT(hr == S_OK); + + mod.scissorClipped = (occlData == 0); + + if(mod.scissorClipped) + break; + } + + { + do + { + hr = m_pImmediateContext->GetData(testQueries[3], &occlData, sizeof(occlData), 0); + } while(hr == S_FALSE); + RDCASSERT(hr == S_OK); + + mod.shaderDiscarded = (occlData == 0); + + if(mod.shaderDiscarded) + break; + } + + if(flags[i] & TestEnabled_DepthTesting) + { + do + { + hr = m_pImmediateContext->GetData(testQueries[4], &occlData, sizeof(occlData), 0); + } while(hr == S_FALSE); + RDCASSERT(hr == S_OK); + + mod.depthTestFailed = (occlData == 0); + + if(mod.depthTestFailed) + break; + } + + if(flags[i] & TestEnabled_StencilTesting) + { + do + { + hr = m_pImmediateContext->GetData(testQueries[5], &occlData, sizeof(occlData), 0); + } while(hr == S_FALSE); + RDCASSERT(hr == S_OK); + + mod.stencilTestFailed = (occlData == 0); + + if(mod.stencilTestFailed) + break; + } + } while (0); + history.push_back(mod); RDCDEBUG("Event %u is visible", events[i]); @@ -3230,6 +3633,9 @@ vector D3D11DebugManager::PixelHistory(uint32_t frameID, vect } m_pImmediateContext->Unmap(pixstore, 0); + + for(size_t i=0; i < ARRAY_COUNT(testQueries); i++) + SAFE_RELEASE(testQueries[i]); SAFE_RELEASE(nopBlendState); SAFE_RELEASE(nopDSState);