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