| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright 2019 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 |  */ | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 7 | #include "src/core/SkBlendModePriv.h" | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 8 | #include "src/gpu/GrClip.h" | 
 | 9 | #include "src/gpu/GrContextPriv.h" | 
 | 10 | #include "src/gpu/GrRenderTargetContext.h" | 
 | 11 | #include "src/gpu/ops/GrFillRectOp.h" | 
 | 12 | #include "src/gpu/ops/GrTextureOp.h" | 
 | 13 | #include "tests/Test.h" | 
 | 14 |  | 
 | 15 | static std::unique_ptr<GrRenderTargetContext> new_RTC(GrContext* context) { | 
| Greg Daniel | e20fcad | 2020-01-08 11:52:34 -0500 | [diff] [blame] | 16 |     return GrRenderTargetContext::Make( | 
 | 17 |             context, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kExact, {128, 128}); | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 18 | } | 
 | 19 |  | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 20 | sk_sp<GrSurfaceProxy> create_proxy(GrContext* context) { | 
 | 21 |     GrSurfaceDesc desc; | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 22 |     desc.fWidth  = 128; | 
 | 23 |     desc.fHeight = 128; | 
 | 24 |  | 
 | 25 |     const GrBackendFormat format = context->priv().caps()->getDefaultBackendFormat( | 
 | 26 |                                                                            GrColorType::kRGBA_8888, | 
 | 27 |                                                                            GrRenderable::kYes); | 
| Greg Daniel | 47c20e8 | 2020-01-21 14:29:57 -0500 | [diff] [blame] | 28 |     GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(format, GrColorType::kRGBA_8888); | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 29 |  | 
 | 30 |     return context->priv().proxyProvider()->createProxy( | 
| Greg Daniel | 47c20e8 | 2020-01-21 14:29:57 -0500 | [diff] [blame] | 31 |         format, desc, swizzle, GrRenderable::kYes, 1, kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 32 |         SkBackingFit::kExact, SkBudgeted::kNo, GrProtected::kNo, GrInternalSurfaceFlags::kNone); | 
 | 33 | } | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 34 |  | 
 | 35 | typedef GrQuadAAFlags (*PerQuadAAFunc)(int i); | 
 | 36 |  | 
 | 37 | typedef void (*BulkRectTest)(skiatest::Reporter* reporter, GrContext* context, | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 38 |                              PerQuadAAFunc perQuadAA, GrAAType overallAA, SkBlendMode blendMode, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 39 |                              int requestedTotNumQuads, int expectedNumOps); | 
 | 40 |  | 
 | 41 | //------------------------------------------------------------------------------------------------- | 
 | 42 | static void bulk_fill_rect_create_test(skiatest::Reporter* reporter, GrContext* context, | 
 | 43 |                                        PerQuadAAFunc perQuadAA, GrAAType overallAA, | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 44 |                                        SkBlendMode blendMode, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 45 |                                        int requestedTotNumQuads, int expectedNumOps) { | 
 | 46 |  | 
 | 47 |     std::unique_ptr<GrRenderTargetContext> rtc = new_RTC(context); | 
 | 48 |  | 
 | 49 |     auto quads = new GrRenderTargetContext::QuadSetEntry[requestedTotNumQuads]; | 
 | 50 |  | 
 | 51 |     for (int i = 0; i < requestedTotNumQuads; ++i) { | 
 | 52 |         quads[i].fRect = SkRect::MakeWH(100.5f, 100.5f); // prevent the int non-AA optimization | 
 | 53 |         quads[i].fColor = SK_PMColor4fWHITE; | 
 | 54 |         quads[i].fLocalMatrix = SkMatrix::I(); | 
 | 55 |         quads[i].fAAFlags = perQuadAA(i); | 
 | 56 |     } | 
 | 57 |  | 
 | 58 |     GrPaint paint; | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 59 |     paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode)); | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 60 |     GrFillRectOp::AddFillRectOps(rtc.get(), GrNoClip(), context, std::move(paint), overallAA, | 
 | 61 |                                  SkMatrix::I(), quads, requestedTotNumQuads); | 
 | 62 |  | 
 | 63 |     GrOpsTask* opsTask = rtc->testingOnly_PeekLastOpsTask(); | 
 | 64 |     int actualNumOps = opsTask->numOpChains(); | 
 | 65 |  | 
 | 66 |     int actualTotNumQuads = 0; | 
 | 67 |  | 
 | 68 |     for (int i = 0; i < actualNumOps; ++i) { | 
 | 69 |         const GrOp* tmp = opsTask->getChain(i); | 
 | 70 |         REPORTER_ASSERT(reporter, tmp->classID() == GrFillRectOp::ClassID()); | 
 | 71 |         REPORTER_ASSERT(reporter, tmp->isChainTail()); | 
 | 72 |         actualTotNumQuads += ((GrDrawOp*) tmp)->numQuads(); | 
 | 73 |     } | 
 | 74 |  | 
 | 75 |     REPORTER_ASSERT(reporter, expectedNumOps == actualNumOps); | 
 | 76 |     REPORTER_ASSERT(reporter, requestedTotNumQuads == actualTotNumQuads); | 
 | 77 |  | 
 | 78 |     context->flush(); | 
 | 79 |  | 
 | 80 |     delete[] quads; | 
 | 81 | } | 
 | 82 |  | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 83 | //------------------------------------------------------------------------------------------------- | 
 | 84 | static void bulk_texture_rect_create_test(skiatest::Reporter* reporter, GrContext* context, | 
 | 85 |                                           PerQuadAAFunc perQuadAA, GrAAType overallAA, | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 86 |                                           SkBlendMode blendMode, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 87 |                                           int requestedTotNumQuads, int expectedNumOps) { | 
 | 88 |  | 
 | 89 |     std::unique_ptr<GrRenderTargetContext> rtc = new_RTC(context); | 
 | 90 |  | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 91 |     sk_sp<GrSurfaceProxy> proxyA = create_proxy(context); | 
 | 92 |     sk_sp<GrSurfaceProxy> proxyB = create_proxy(context); | 
 | 93 |     GrSurfaceProxyView proxyViewA(std::move(proxyA), kTopLeft_GrSurfaceOrigin, GrSwizzle::RGBA()); | 
 | 94 |     GrSurfaceProxyView proxyViewB(std::move(proxyB), kTopLeft_GrSurfaceOrigin, GrSwizzle::RGBA()); | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 95 |  | 
 | 96 |     auto set = new GrRenderTargetContext::TextureSetEntry[requestedTotNumQuads]; | 
 | 97 |  | 
 | 98 |     for (int i = 0; i < requestedTotNumQuads; ++i) { | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 99 |         // Alternate between two proxies to prevent op merging if the batch API was forced to submit | 
 | 100 |         // one op at a time (to work, this does require that all fDstRects overlap). | 
 | 101 |         set[i].fProxyView = i % 2 == 0 ? proxyViewA : proxyViewB; | 
| Brian Salomon | fc11844 | 2019-11-22 19:09:27 -0500 | [diff] [blame] | 102 |         set[i].fSrcAlphaType = kPremul_SkAlphaType; | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 103 |         set[i].fSrcRect = SkRect::MakeWH(100.0f, 100.0f); | 
 | 104 |         set[i].fDstRect = SkRect::MakeWH(100.5f, 100.5f); // prevent the int non-AA optimization | 
 | 105 |         set[i].fDstClipQuad = nullptr; | 
 | 106 |         set[i].fPreViewMatrix = nullptr; | 
 | 107 |         set[i].fAlpha = 1.0f; | 
 | 108 |         set[i].fAAFlags = perQuadAA(i); | 
 | 109 |     } | 
 | 110 |  | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 111 |     GrTextureOp::AddTextureSetOps(rtc.get(), GrNoClip(), context, set, requestedTotNumQuads, | 
| Michael Ludwig | 379e496 | 2019-12-06 13:21:26 -0500 | [diff] [blame] | 112 |                                   requestedTotNumQuads, // We alternate so proxyCnt == cnt | 
 | 113 |                                   GrSamplerState::Filter::kNearest, | 
 | 114 |                                   GrTextureOp::Saturate::kYes, | 
 | 115 |                                   blendMode, | 
 | 116 |                                   overallAA, | 
 | 117 |                                   SkCanvas::kStrict_SrcRectConstraint, | 
 | 118 |                                   SkMatrix::I(), nullptr); | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 119 |  | 
 | 120 |     GrOpsTask* opsTask = rtc->testingOnly_PeekLastOpsTask(); | 
 | 121 |     int actualNumOps = opsTask->numOpChains(); | 
 | 122 |  | 
 | 123 |     int actualTotNumQuads = 0; | 
 | 124 |  | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 125 |     if (blendMode != SkBlendMode::kSrcOver || | 
 | 126 |         !context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) { | 
 | 127 |         // In either of these two cases, GrTextureOp creates one op per quad instead. Since | 
 | 128 |         // each entry alternates proxies but overlaps geometrically, this will prevent the ops | 
 | 129 |         // from being merged back into fewer ops. | 
 | 130 |         expectedNumOps = requestedTotNumQuads; | 
 | 131 |     } | 
 | 132 |     uint32_t expectedOpID = blendMode == SkBlendMode::kSrcOver ? GrTextureOp::ClassID() | 
 | 133 |                                                                : GrFillRectOp::ClassID(); | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 134 |     for (int i = 0; i < actualNumOps; ++i) { | 
 | 135 |         const GrOp* tmp = opsTask->getChain(i); | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 136 |         REPORTER_ASSERT(reporter, tmp->classID() == expectedOpID); | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 137 |         REPORTER_ASSERT(reporter, tmp->isChainTail()); | 
 | 138 |         actualTotNumQuads += ((GrDrawOp*) tmp)->numQuads(); | 
 | 139 |     } | 
 | 140 |  | 
 | 141 |     REPORTER_ASSERT(reporter, expectedNumOps == actualNumOps); | 
 | 142 |     REPORTER_ASSERT(reporter, requestedTotNumQuads == actualTotNumQuads); | 
 | 143 |  | 
 | 144 |     context->flush(); | 
 | 145 |  | 
 | 146 |     delete[] set; | 
 | 147 | } | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 148 |  | 
 | 149 | //------------------------------------------------------------------------------------------------- | 
 | 150 | static void run_test(GrContext* context, skiatest::Reporter* reporter, BulkRectTest test) { | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 151 |     // This is the simple case where there is no AA at all. We expect 2 non-AA clumps of quads. | 
 | 152 |     { | 
 | 153 |         auto noAA = [](int i) -> GrQuadAAFlags { | 
 | 154 |             return GrQuadAAFlags::kNone; | 
 | 155 |         }; | 
 | 156 |  | 
 | 157 |         static const int kNumExpectedOps = 2; | 
 | 158 |  | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 159 |         test(reporter, context, noAA, GrAAType::kNone, SkBlendMode::kSrcOver, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 160 |              2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps); | 
 | 161 |     } | 
 | 162 |  | 
 | 163 |     // This is the same as the above case except the overall AA is kCoverage. However, since | 
 | 164 |     // the per-quad AA is still none, all the quads should be downgraded to non-AA. | 
 | 165 |     { | 
 | 166 |         auto noAA = [](int i) -> GrQuadAAFlags { | 
 | 167 |             return GrQuadAAFlags::kNone; | 
 | 168 |         }; | 
 | 169 |  | 
 | 170 |         static const int kNumExpectedOps = 2; | 
 | 171 |  | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 172 |         test(reporter, context, noAA, GrAAType::kCoverage, SkBlendMode::kSrcOver, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 173 |              2*GrResourceProvider::MaxNumNonAAQuads(), kNumExpectedOps); | 
 | 174 |     } | 
 | 175 |  | 
 | 176 |     // This case has an overall AA of kCoverage but the per-quad AA alternates. | 
 | 177 |     // We should end up with several aa-sized clumps | 
 | 178 |     { | 
 | 179 |         auto alternateAA = [](int i) -> GrQuadAAFlags { | 
 | 180 |             return (i % 2) ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone; | 
 | 181 |         }; | 
 | 182 |  | 
 | 183 |         int numExpectedOps = 2*GrResourceProvider::MaxNumNonAAQuads() / | 
 | 184 |                                                  GrResourceProvider::MaxNumAAQuads(); | 
 | 185 |  | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 186 |         test(reporter, context, alternateAA, GrAAType::kCoverage, SkBlendMode::kSrcOver, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 187 |              2*GrResourceProvider::MaxNumNonAAQuads(), numExpectedOps); | 
 | 188 |     } | 
 | 189 |  | 
 | 190 |     // In this case we have a run of MaxNumAAQuads non-AA quads and then AA quads. This | 
 | 191 |     // exercises the case where we have a clump of quads that can't be upgraded to AA bc of | 
 | 192 |     // its size. We expect one clump of non-AA quads followed by one clump of AA quads. | 
 | 193 |     { | 
 | 194 |         auto runOfNonAA = [](int i) -> GrQuadAAFlags { | 
 | 195 |             return (i < GrResourceProvider::MaxNumAAQuads()) ? GrQuadAAFlags::kNone | 
 | 196 |                                                              : GrQuadAAFlags::kAll; | 
 | 197 |         }; | 
 | 198 |  | 
 | 199 |         static const int kNumExpectedOps = 2; | 
 | 200 |  | 
| Michael Ludwig | fe13ca3 | 2019-11-21 10:26:41 -0500 | [diff] [blame] | 201 |         test(reporter, context, runOfNonAA, GrAAType::kCoverage, SkBlendMode::kSrcOver, | 
 | 202 |              2*GrResourceProvider::MaxNumAAQuads(), kNumExpectedOps); | 
 | 203 |     } | 
 | 204 |  | 
 | 205 |     // In this case we use a blend mode other than src-over, which hits the GrFillRectOp fallback | 
 | 206 |     // code path for GrTextureOp. We pass in the expected results if batching was successful, to | 
 | 207 |     // that bulk_fill_rect_create_test batches on all modes; bulk_texture_rect_create_test is | 
 | 208 |     // responsible for revising its expectations. | 
 | 209 |     { | 
 | 210 |         auto fixedAA = [](int i) -> GrQuadAAFlags { | 
 | 211 |             return GrQuadAAFlags::kAll; | 
 | 212 |         }; | 
 | 213 |  | 
 | 214 |         static const int kNumExpectedOps = 2; | 
 | 215 |  | 
 | 216 |         test(reporter, context, fixedAA, GrAAType::kCoverage, SkBlendMode::kSrcATop, | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 217 |              2*GrResourceProvider::MaxNumAAQuads(), kNumExpectedOps); | 
 | 218 |     } | 
 | 219 | } | 
 | 220 |  | 
 | 221 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BulkFillRectTest, reporter, ctxInfo) { | 
 | 222 |     run_test(ctxInfo.grContext(), reporter, bulk_fill_rect_create_test); | 
 | 223 | } | 
 | 224 |  | 
| Robert Phillips | e837e61 | 2019-11-15 11:02:50 -0500 | [diff] [blame] | 225 | DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BulkTextureRectTest, reporter, ctxInfo) { | 
 | 226 |     run_test(ctxInfo.grContext(), reporter, bulk_texture_rect_create_test); | 
 | 227 | } |