diff --git a/src/gpu/ganesh/ops/OpsTask.cpp b/src/gpu/ganesh/ops/OpsTask.cpp index d7e49fe50b6..ca522761d32 100644 --- a/src/gpu/ganesh/ops/OpsTask.cpp +++ b/src/gpu/ganesh/ops/OpsTask.cpp @@ -38,9 +38,24 @@ #include #include #include +#include #include #include +// === [STENCIL-POC] Instrumentation ========================================= +// Tracks, per stencil GrAttachment*, the union of regions that have actually +// been cleared via VK_ATTACHMENT_LOAD_OP_CLEAR (which is renderArea-scoped). +// Detects when a kLoad is issued over a region that was never cleared, which +// on a non-zeroing Vulkan driver loads uninitialised VRAM into the stencil +// buffer that the Redbook cover pass then exposes to getImageData(). +namespace { +struct StencilPocTracker { + std::map cleared; // attachment* -> union of cleared +}; +StencilPocTracker& StencilPoc() { static StencilPocTracker t; return t; } +} // namespace +// =========================================================================== + class GrDrawingManager; enum GrSurfaceOrigin : int; @@ -617,6 +632,71 @@ bool OpsTask::onExecute(GrOpFlushState* flushState) { break; } + // === [STENCIL-POC] ===================================================== + if (stencil) { + const SkISize dims = stencil->dimensions(); + const SkIRect full = SkIRect::MakeSize(dims); + const GrBackendApi api = renderTarget->backendFormat().backend(); + const char* be = (api == GrBackendApi::kVulkan) ? "Vulkan" + : (api == GrBackendApi::kOpenGL) ? "OpenGL" + : (api == GrBackendApi::kMetal) ? "Metal" + : (api == GrBackendApi::kDirect3D) ? "D3D" + : "?"; + const char* opName = (stencilLoadOp == GrLoadOp::kClear) ? "kClear" + : (stencilLoadOp == GrLoadOp::kLoad) ? "kLoad" + : (stencilLoadOp == GrLoadOp::kDiscard) ? "kDiscard" + : "?"; + fprintf(stderr, + "[STENCIL-POC] backend=%s stencil=%p rt=%p dims=%dx%d loadOp=%s " + "renderArea=[%d,%d,%d,%d] initialContent=%d hasInitClear=%d\n", + be, + (void*)stencil, (void*)renderTarget, dims.width(), dims.height(), + opName, fClippedContentBounds.fLeft, fClippedContentBounds.fTop, + fClippedContentBounds.fRight, fClippedContentBounds.fBottom, + (int)fInitialStencilContent, + stencil->hasPerformedInitialClear() ? 1 : 0); + auto& cleared = StencilPoc().cleared; + if (stencilLoadOp == GrLoadOp::kClear) { + SkIRect& region = cleared[stencil]; + if (region.isEmpty()) region = fClippedContentBounds; + else region.join(fClippedContentBounds); + if (!fClippedContentBounds.contains(full)) { + fprintf(stderr, + "[STENCIL-POC] ** PARTIAL initial clear: only " + "[%d,%d,%d,%d] of %dx%d cleared, but " + "fHasPerformedInitialClear will latch TRUE for the " + "WHOLE attachment **\n", + fClippedContentBounds.fLeft, fClippedContentBounds.fTop, + fClippedContentBounds.fRight, + fClippedContentBounds.fBottom, dims.width(), + dims.height()); + } + } else if (stencilLoadOp == GrLoadOp::kLoad && + fInitialStencilContent == StencilContent::kUserBitsCleared) { + auto it = cleared.find(stencil); + SkIRect prev = (it != cleared.end()) ? it->second : SkIRect::MakeEmpty(); + if (!prev.contains(fClippedContentBounds)) { + fprintf(stderr, + "[STENCIL-POC] ******** BUG TRIGGERED ********\n" + "[STENCIL-POC] stencilLoadOp=kLoad (fallthrough from " + "kUserBitsCleared because hasPerformedInitialClear()==" + "true) over renderArea=[%d,%d,%d,%d], but this stencil " + "attachment was only ever LOAD_OP_CLEAR'd over " + "[%d,%d,%d,%d]. The remainder is uninitialised VkImage " + "memory (vkCreateImage initialLayout=UNDEFINED, never " + "written). VK_ATTACHMENT_LOAD_OP_LOAD will read it; the " + "Redbook cover pass (kNotEqual 0) exposes it to " + "getImageData(). CWE-908.\n" + "[STENCIL-POC] ********************************\n", + fClippedContentBounds.fLeft, fClippedContentBounds.fTop, + fClippedContentBounds.fRight, + fClippedContentBounds.fBottom, prev.fLeft, prev.fTop, + prev.fRight, prev.fBottom); + } + } + } + // ======================================================================= + // NOTE: If fMustPreserveStencil is set, then we are executing a surfaceDrawContext that split // its opsTask. //