• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google Inc.
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 "tests/Test.h"
9 
10 #include "include/core/SkVertices.h"
11 #include "src/core/SkBlendModePriv.h"
12 #include "src/core/SkMatrixProvider.h"
13 #include "src/core/SkSurfacePriv.h"
14 #include "src/gpu/GrStyle.h"
15 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
16 
17 namespace {
18 
19 static SkSurfaceProps kDMSAAProps(SkSurfaceProps::kDynamicMSAA_Flag, kUnknown_SkPixelGeometry);
20 static SkSurfaceProps kBasicProps(0, kUnknown_SkPixelGeometry);
21 constexpr static SkPMColor4f kTransYellow = {.5f,.5f,.0f,.5f};
22 constexpr static SkPMColor4f kTransCyan = {.0f,.5f,.5f,.5f};
23 constexpr static int kWidth=10, kHeight=10;
24 
25 }
26 
draw_paint_with_aa(skgpu::v1::SurfaceDrawContext * sdc,const SkPMColor4f & color,SkBlendMode blendMode)27 static void draw_paint_with_aa(skgpu::v1::SurfaceDrawContext* sdc,
28                                const SkPMColor4f& color,
29                                SkBlendMode blendMode) {
30     GrPaint paint;
31     paint.setColor4f(color);
32     paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
33     sdc->drawRect(nullptr, std::move(paint), GrAA::kYes, SkMatrix::I(),
34                   SkRect::MakeIWH(kWidth, kHeight), nullptr);
35 }
36 
draw_paint_with_dmsaa(skgpu::v1::SurfaceDrawContext * sdc,const SkPMColor4f & color,SkBlendMode blendMode)37 static void draw_paint_with_dmsaa(skgpu::v1::SurfaceDrawContext* sdc,
38                                   const SkPMColor4f& color,
39                                   SkBlendMode blendMode) {
40     // drawVertices should always trigger dmsaa, but draw something non-rectangular just to be 100%
41     // certain.
42     static const SkPoint kVertices[3] = {{-.5f,-.5f}, {kWidth * 2.1f, 0}, {0, kHeight * 2.1f}};
43     SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 3, 0, 0);
44     memcpy(builder.positions(), kVertices, sizeof(kVertices));
45     auto vertices = builder.detach();
46 
47     GrPaint paint;
48     paint.setColor4f(color);
49     paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
50     sdc->drawVertices(nullptr, std::move(paint), SkSimpleMatrixProvider(SkMatrix::I()), vertices);
51 }
52 
fuzzy_equals(const float a[4],const SkPMColor4f & b)53 static bool fuzzy_equals(const float a[4], const SkPMColor4f& b) {
54     constexpr static float kTolerance = 2.5f / 256;
55     for (int i = 0; i < 4; ++i) {
56         if (!SkScalarNearlyEqual(a[i], b.vec()[i], kTolerance)) {
57             return false;
58         }
59     }
60     return true;
61 }
62 
check_sdc_color(skiatest::Reporter * reporter,skgpu::v1::SurfaceDrawContext * sdc,GrDirectContext * ctx,const SkPMColor4f & color)63 static void check_sdc_color(skiatest::Reporter* reporter,
64                             skgpu::v1::SurfaceDrawContext* sdc,
65                             GrDirectContext* ctx,
66                             const SkPMColor4f& color) {
67     auto info = SkImageInfo::Make(kWidth, kHeight, kRGBA_F32_SkColorType, kPremul_SkAlphaType);
68     GrPixmap pixmap = GrPixmap::Allocate(info);
69     sdc->readPixels(ctx, pixmap, {0, 0});
70     auto pix = static_cast<const float*>(pixmap.addr());
71     for (int y = 0; y < kHeight; ++y) {
72         for (int x = 0; x < kWidth; ++x) {
73             if (!fuzzy_equals(pix, color)) {
74                 ERRORF(reporter, "SDC color mismatch.\n"
75                                  "Got      [%0.3f, %0.3f, %0.3f, %0.3f]\n"
76                                  "Expected [%0.3f, %0.3f, %0.3f, %0.3f]",
77                        pix[0], pix[1], pix[2], pix[3], color.fR, color.fG, color.fB, color.fA);
78                 return;
79             }
80             pix += 4;
81         }
82     }
83 }
84 
85 DEF_GPUTEST_FOR_CONTEXTS(DMSAA_preserve_contents,
86                          &sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo,
87                          nullptr) {
88     auto dContext = ctxInfo.directContext();
89     auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
90                                                    SkBackingFit::kApprox, {kWidth, kHeight},
91                                                    kDMSAAProps);
92 
93     // Initialize the texture and dmsaa attachment with transparent.
94     draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
95     check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
96 
97     // Clear the main texture to yellow.
98     sdc->clear(kTransYellow);
99 
100     // Close the opsTask by doing a readback.
101     check_sdc_color(reporter, sdc.get(), dContext, kTransYellow);
102 
103     // Now the DMSAA attachment is clear and the texture is yellow. Blend cyan into the DMSAA
104     // attachment. This will fail if the yellow from the main texture doesn't get copied into the
105     // DMSAA attachment before the renderPass.
106     draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kSrcOver);
107     SkPMColor4f dstColor = SkBlendMode_Apply(SkBlendMode::kSrcOver, kTransCyan, kTransYellow);
108 
109     check_sdc_color(reporter, sdc.get(), dContext, dstColor);
110 }
111 
require_dst_reads(GrContextOptions * options)112 static void require_dst_reads(GrContextOptions* options) {
113     options->fSuppressAdvancedBlendEquations = true;
114     options->fSuppressFramebufferFetch = true;
115 }
116 
117 DEF_GPUTEST_FOR_CONTEXTS(DMSAA_dst_read, &sk_gpu_test::GrContextFactory::IsRenderingContext,
118                          reporter, ctxInfo, require_dst_reads) {
119     auto dContext = ctxInfo.directContext();
120     auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
121                                                    SkBackingFit::kApprox, {kWidth, kHeight},
122                                                    kDMSAAProps);
123 
124     // Initialize the texture and dmsaa attachment with transparent.
125     draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
126     check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
127 
128     sdc->clear(SK_PMColor4fWHITE);
129     SkPMColor4f dstColor = SK_PMColor4fWHITE;
130 
131     draw_paint_with_dmsaa(sdc.get(), kTransYellow, SkBlendMode::kDarken);
132     dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor);
133 
134     draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kDarken);
135     dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransCyan, dstColor);
136 
137     check_sdc_color(reporter, sdc.get(), dContext, dstColor);
138 }
139 
140 DEF_GPUTEST_FOR_CONTEXTS(DMSAA_aa_dst_read_after_dmsaa,
141                          &sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo,
142                          require_dst_reads) {
143     auto dContext = ctxInfo.directContext();
144     auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
145                                                    SkBackingFit::kApprox, {kWidth, kHeight},
146                                                    kDMSAAProps);
147 
148     // Initialize the texture and dmsaa attachment with transparent.
149     draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
150     check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
151 
152     sdc->clear(SK_PMColor4fWHITE);
153     SkPMColor4f dstColor = SK_PMColor4fWHITE;
154 
155     draw_paint_with_dmsaa(sdc.get(), kTransYellow, SkBlendMode::kDarken);
156     dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor);
157 
158     // Draw with aa after dmsaa. This should break up the render pass and issue a texture barrier.
159     draw_paint_with_aa(sdc.get(), kTransCyan, SkBlendMode::kDarken);
160     dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransCyan, dstColor);
161 
162     check_sdc_color(reporter, sdc.get(), dContext, dstColor);
163 }
164 
165 DEF_GPUTEST_FOR_CONTEXTS(DMSAA_dst_read_with_existing_barrier,
166                          &sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo,
167                          require_dst_reads) {
168     auto dContext = ctxInfo.directContext();
169     auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
170                                                    SkBackingFit::kApprox, {kWidth, kHeight},
171                                                    kDMSAAProps);
172 
173     // Initialize the texture and dmsaa attachment with transparent.
174     draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
175     check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
176 
177     sdc->clear(SK_PMColor4fWHITE);
178     SkPMColor4f dstColor = SK_PMColor4fWHITE;
179 
180     // Blend to the texture (not the dmsaa attachment) with a dst read. This creates a texture
181     // barrier.
182     draw_paint_with_aa(sdc.get(), kTransYellow, SkBlendMode::kDarken);
183     dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor);
184 
185     // Blend to the msaa attachment _without_ a dst read. This ensures we respect the prior texture
186     // barrier by splitting the opsTask.
187     draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kSrcOver);
188     dstColor = SkBlendMode_Apply(SkBlendMode::kSrcOver, kTransCyan, dstColor);
189 
190     check_sdc_color(reporter, sdc.get(), dContext, dstColor);
191 }
192 
193 // This test is used to test for crbug.com/1241134. The bug appears on Adreno5xx devices with OS
194 // PQ3A. It does not repro on the earlier PPR1 version since the extend blend func extension was not
195 // present on the older driver.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DMSAA_dual_source_blend_disable,reporter,ctxInfo)196 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DMSAA_dual_source_blend_disable, reporter, ctxInfo) {
197     SkISize surfaceDims = {100, 100};
198     SkISize texDims = {50, 50};
199     auto context = ctxInfo.directContext();
200 
201     auto sourceTexture = context->createBackendTexture(texDims.width(),
202                                                        texDims.height(),
203                                                        kRGBA_8888_SkColorType,
204                                                        SkColors::kBlue,
205                                                        GrMipMapped::kNo,
206                                                        GrRenderable::kYes,
207                                                        GrProtected::kNo);
208 
209     auto sourceImage = SkImage::MakeFromTexture(context,
210                                                 sourceTexture,
211                                                 kTopLeft_GrSurfaceOrigin,
212                                                 kRGBA_8888_SkColorType,
213                                                 kPremul_SkAlphaType,
214                                                 nullptr);
215 
216     auto texture1 = context->createBackendTexture(surfaceDims.width(),
217                                                   surfaceDims.height(),
218                                                   kRGBA_8888_SkColorType,
219                                                   SkColors::kRed,
220                                                   GrMipMapped::kNo,
221                                                   GrRenderable::kYes,
222                                                   GrProtected::kNo);
223 
224     auto texture2 = context->createBackendTexture(surfaceDims.width(),
225                                                   surfaceDims.height(),
226                                                   kRGBA_8888_SkColorType,
227                                                   SkColors::kYellow,
228                                                   GrMipMapped::kNo,
229                                                   GrRenderable::kYes,
230                                                   GrProtected::kNo);
231 
232     SkPaint paint;
233     paint.setBlendMode(SkBlendMode::kSrc);
234 
235     SkRect srcRect = SkRect::MakeIWH(texDims.width(), texDims.height());
236     SkRect dstRect = SkRect::MakeXYWH(texDims.width()/2, texDims.height()/2,
237                                       texDims.width(), texDims.height());
238 
239     // First we do an image draw to a DMSAA surface with kSrc blend mode. This will trigger us to
240     // use dual source blending if supported.
241     // Note: The draw here doesn't actually use the dmsaa multisampled buffer. However, by using
242     // a dmsaa surface it forces us to use the FillRRectOp instead of the normal FillQuad path. It
243     // is unclear why, but using the FillRRectOp is required to repro the bug.
244     {
245         auto surface = SkSurface::MakeFromBackendTexture(context,
246                                                          texture1,
247                                                          kTopLeft_GrSurfaceOrigin,
248                                                          1,
249                                                          kRGBA_8888_SkColorType,
250                                                          nullptr,
251                                                          &kDMSAAProps);
252 
253         surface->getCanvas()->drawImageRect(sourceImage,
254                                             srcRect,
255                                             dstRect,
256                                             SkSamplingOptions(),
257                                             &paint,
258                                             SkCanvas::kStrict_SrcRectConstraint);
259         // Make sure there isn't any batching
260         surface->flushAndSubmit();
261     }
262 
263     // Next we do an image draw to a different surface that doesn't have the dmsaa flag. This will
264     // trigger use to disable blending. However, when the bug is present the driver still seems to
265     // try and use a "src2" blend value and ends up just writing the original dst color of yellow.
266     {
267         auto surface = SkSurface::MakeFromBackendTexture(context,
268                                                          texture2,
269                                                          kTopLeft_GrSurfaceOrigin,
270                                                          1,
271                                                          kRGBA_8888_SkColorType,
272                                                          nullptr,
273                                                          &kBasicProps);
274 
275         surface->getCanvas()->drawImageRect(sourceImage,
276                                             srcRect,
277                                             dstRect,
278                                             SkSamplingOptions(),
279                                             &paint,
280                                             SkCanvas::kStrict_SrcRectConstraint);
281         surface->flushAndSubmit();
282     }
283 
284     {
285         auto readImage = SkImage::MakeFromTexture(context,
286                                                   texture2,
287                                                   kTopLeft_GrSurfaceOrigin,
288                                                   kRGBA_8888_SkColorType,
289                                                   kPremul_SkAlphaType,
290                                                   nullptr);
291         SkImageInfo dstIInfo = SkImageInfo::Make(texDims.width(),
292                                                  texDims.height(),
293                                                  kRGBA_8888_SkColorType,
294                                                  kPremul_SkAlphaType,
295                                                  nullptr);
296 
297         SkBitmap bitmap;
298         bitmap.allocPixels(dstIInfo);
299 
300         bool success = readImage->readPixels(context, bitmap.pixmap(), dstRect.fLeft, dstRect.fTop);
301         if (!success) {
302             ERRORF(reporter, "Failed to read pixels");
303             return;
304         }
305         auto pix = static_cast<const uint32_t*>(bitmap.getAddr(0, 0));
306         for (int x = 0; x < 50; ++x) {
307             for (int y = 0; y < 50; ++y) {
308                 uint32_t pixColor = pix[x + y * 50];
309                 if (pixColor != 0xFFFF0000) {
310                     ERRORF(reporter, "Didn't get a blue pixel at %d, %d. Got 0x%8X",
311                            x, y, pixColor);
312                     continue;
313                 }
314             }
315         }
316     }
317     sourceImage.reset();
318     // Need to make sure the gpu is fully finished before deleting the textures
319     context->flushAndSubmit(true);
320     context->deleteBackendTexture(sourceTexture);
321     context->deleteBackendTexture(texture1);
322     context->deleteBackendTexture(texture2);
323 }
324 
325