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