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