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