--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc +++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc @@ -1176,6 +1176,15 @@ const auto& caps = context_provider_wrapper_->ContextProvider().GetCapabilities(); bool use_msaa = !caps.msaa_is_slow && !caps.avoid_stencil_buffers; + // ==== POC: simulate the kNoMSAA path already taken on platforms where ==== + // ==== caps.msaa_is_slow=true. With kNoMSAA, raster_decoder.cc:2815 ==== + // ==== sets surface-props flags=0 (no kDynamicMSAA_Flag), so ==== + // ==== SurfaceDrawContext picks aaType=kCoverage and the path-renderer ==== + // ==== chain reaches AAHairLinePathRenderer. The Skia bug itself is ==== + // ==== independent of MSAA mode; this only steers PATH-RENDERER ==== + // ==== SELECTION to demonstrate it. ==== + use_msaa = false; + // ==== END POC ==== ri->BeginRasterCHROMIUM( background_color, needs_clear, /*msaa_sample_count=*/use_msaa ? 1 : 0, diff --git a/third_party/skia/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp b/third_party/skia/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp index c0413ed170..c2af73b4dc 100644 --- a/third_party/skia/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp +++ b/third_party/skia/src/gpu/ganesh/ops/AAHairLinePathRenderer.cpp @@ -614,6 +614,16 @@ bool bloat_quad(const SkPoint qpts[3], if (!abNormalized) { if (!cbNormalized) { + // ==== POC INSTRUMENTATION ==== + // Prove that bloat_quad is rejecting a primitive whose vertex slot + // has already been allocated and counted toward setIndexedPatterned. + fprintf(stderr, + "[POC] bloat_quad REJECTED: a=(%g,%g) b=(%g,%g) c=(%g,%g) " + "ab.normalize()=false cb.normalize()=false -> 5-vertex slot " + "(%zu bytes) left STALE\n", + a.fX, a.fY, b.fX, b.fY, c.fX, c.fY, + (size_t)(kQuadNumVertices * sizeof(BezierVertex))); + // ==== END POC ==== return false; // Quad is degenerate so we won't add it. } @@ -1278,6 +1288,22 @@ void AAHairlineOp::onPrepareDraws(GrMeshDrawTarget* target) { return; } + // ==== POC INSTRUMENTATION: poison the vertex buffer ==== + // makeVertexSpace returns an uninitialized slab from GrBufferAllocPool. + // Normally this contains stale prior-flush vertex data. Poison it with a + // sentinel float so we can prove later that unfilled slots retain + // whatever was here at allocation time (i.e. the fill loops below don't + // touch them). + const size_t poc_total_bytes = sizeof(BezierVertex) * (size_t)vertexCount; + const uint32_t kPocPoison = 0xDEC0ADDE; // -6.95e18f, distinguishable + { + uint32_t* p = reinterpret_cast(vertices); + for (size_t i = 0; i < poc_total_bytes / sizeof(uint32_t); ++i) { + p[i] = kPocPoison; + } + } + // ==== END POC ==== + // Setup vertices BezierVertex* bezVerts = reinterpret_cast(vertices); @@ -1295,6 +1321,54 @@ void AAHairlineOp::onPrepareDraws(GrMeshDrawTarget* target) { add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &bezVerts); } + // ==== POC INSTRUMENTATION: verify cursor reached vertexCount ==== + { + BezierVertex* base = reinterpret_cast(vertices); + ptrdiff_t actually_written = bezVerts - base; + ptrdiff_t expected = (ptrdiff_t)vertexCount; + if (actually_written != expected) { + ptrdiff_t stale_verts = expected - actually_written; + size_t stale_bytes = (size_t)stale_verts * sizeof(BezierVertex); + // Prove the unfilled tail still contains our poison: the fill + // loops never touched it. + size_t poison_survivors = 0; + const uint32_t* tail = + reinterpret_cast(bezVerts); + for (size_t i = 0; i < stale_bytes / sizeof(uint32_t); ++i) { + if (tail[i] == kPocPoison) ++poison_survivors; + } + // Sample first stale BezierVertex.fPos for the report. + float stale_x = bezVerts->fPos.fX; + float stale_y = bezVerts->fPos.fY; + uint32_t stale_x_bits, stale_y_bits; + memcpy(&stale_x_bits, &stale_x, 4); + memcpy(&stale_y_bits, &stale_y, 4); + fprintf(stderr, + "[POC] ==================================================\n" + "[POC] STALE VERTEX BUFFER DISCLOSURE TRIGGERED\n" + "[POC] vertexCount allocated: %d (%zu bytes)\n" + "[POC] vertices actually written: %td\n" + "[POC] STALE vertices submitted: %td (%zu bytes)\n" + "[POC] quadCount=%d conicCount=%d (used by setIndexedPatterned below)\n" + "[POC] poison-pattern words surviving in stale region: %zu / %zu\n" + "[POC] first stale BezierVertex.fPos = (%g, %g) [bits=0x%08x,0x%08x]\n" + "[POC] -> setIndexedPatterned will draw these uninitialized vertices\n" + "[POC] ==================================================\n", + vertexCount, poc_total_bytes, + actually_written, stale_verts, stale_bytes, + quadCount, conicCount, + poison_survivors, stale_bytes / sizeof(uint32_t), + stale_x, stale_y, stale_x_bits, stale_y_bits); + SK_ABORT("POC: AAHairlineOp submits %td stale vertices (%zu bytes) " + "to GPU via setIndexedPatterned. bloat_quad rejected " + "primitives but quadCount/conicCount were not " + "decremented. Poison-survivor rate: %zu/%zu words.", + stale_verts, stale_bytes, + poison_survivors, stale_bytes / sizeof(uint32_t)); + } + } + // ==== END POC ==== + if (quadCount > 0) { SkASSERT(predictedPrograms & Program::kQuad); actualPrograms |= Program::kQuad;