Brian Salomon | 3595c15 | 2021-11-22 17:30:22 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021 Google LLC |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "include/core/SkCanvas.h" |
| 9 | #include "include/core/SkImageInfo.h" |
| 10 | #include "include/core/SkPaint.h" |
| 11 | #include "include/core/SkPath.h" |
| 12 | #include "include/core/SkSurface.h" |
| 13 | #include "include/gpu/GrDirectContext.h" |
| 14 | #include "include/gpu/GrRecordingContext.h" |
| 15 | #include "src/core/SkAutoPixmapStorage.h" |
| 16 | #include "tests/Test.h" |
| 17 | |
| 18 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(crbug_1271431, reporter, context_info) { |
| 19 | GrDirectContext* dc = context_info.directContext(); |
| 20 | |
| 21 | // Make sure we don't get recycled render targets that already have stencil attachments. |
| 22 | dc->freeGpuResources(); |
| 23 | |
| 24 | SkImageInfo ii = SkImageInfo::Make({100, 100}, |
| 25 | kRGBA_8888_SkColorType, |
| 26 | kPremul_SkAlphaType, |
| 27 | nullptr); |
| 28 | sk_sp<SkSurface> surfs[2] { |
| 29 | SkSurface::MakeRenderTarget(dc, SkBudgeted::kYes, ii, 1, nullptr), |
| 30 | SkSurface::MakeRenderTarget(dc, SkBudgeted::kYes, ii, 1, nullptr) |
| 31 | }; |
| 32 | |
| 33 | // Make sure the surfaces' proxies are instantiated without stencil. Creating textures lazily |
| 34 | // can invalidate the current tracked FBO since FBO state must be modified to during |
| 35 | // GrGLRenderTarget creation. |
| 36 | for (int i = 0; i < 2; ++i) { |
| 37 | surfs[i]->getCanvas()->clear(SK_ColorWHITE); |
| 38 | surfs[i]->flushAndSubmit(); |
| 39 | } |
| 40 | |
| 41 | auto drawWithStencilClip = [&](SkSurface& surf, SkColor color) { |
| 42 | SkPath clip; |
| 43 | clip.addCircle(50, 50, 50); |
| 44 | clip.addCircle(50, 50, 10, SkPathDirection::kCCW); |
| 45 | SkPaint paint; |
| 46 | paint.setColor(color); |
| 47 | surf.getCanvas()->clipPath(clip, false); |
| 48 | surf.getCanvas()->drawRect(SkRect::MakeLTRB(0,0, 100, 100), paint); |
| 49 | }; |
| 50 | |
| 51 | // Use surfs[0] create to create a cached stencil buffer that is also sized for surfs[1]. |
| 52 | drawWithStencilClip(*surfs[0], SK_ColorRED); |
| 53 | surfs[0]->flushAndSubmit(); |
| 54 | |
| 55 | // Make sure surf[1]'s FBO is bound but without using draws that would attach stencil. |
| 56 | surfs[1]->getCanvas()->clear(SK_ColorGREEN); |
| 57 | surfs[1]->flushAndSubmit(); |
| 58 | |
| 59 | // Now use stencil for clipping. We should now have the following properties: |
| 60 | // 1) surf[1]'s FBO is already bound |
| 61 | // 2) surf[1] doesn't have a stencil buffer |
| 62 | // 3) There is an appropriately sized stencil buffer in the cache (used with surf[0]). This is |
| 63 | // important because creating a new stencil buffer will invalidate the bound FBO tracking. |
| 64 | // The bug was that because the correct FBO was already bound we would not rebind and would |
| 65 | // skip the lazy stencil attachment in GrGLRenderTarget. This would cause the clip to fail. |
| 66 | drawWithStencilClip(*surfs[1], SK_ColorBLUE); |
| 67 | |
| 68 | // Check that four pixels that should have been clipped out of the blue draw are green. |
| 69 | SkAutoPixmapStorage rb; |
| 70 | rb.alloc(surfs[1]->imageInfo().makeWH(1, 1)); |
| 71 | for (int x : {5, 95}) { |
| 72 | for (int y : {5, 95}) { |
| 73 | if (!surfs[1]->readPixels(rb, x, y)) { |
| 74 | ERRORF(reporter, "readback failed"); |
| 75 | return; |
| 76 | } |
| 77 | if (*rb.addr32() != SK_ColorGREEN) { |
| 78 | ERRORF(reporter, |
| 79 | "Expected green at (%d, %d), instead got 0x%08x.", |
| 80 | x, |
| 81 | y, |
| 82 | *rb.addr32()); |
| 83 | return; |
| 84 | } |
| 85 | } |
| 86 | } |
| 87 | } |