1 /*
2 * Copyright 2022 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/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkMatrix.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkSamplingOptions.h"
19 #include "include/core/SkSurface.h"
20 #include "include/core/SkSurfaceProps.h"
21 #include "include/core/SkTypes.h"
22 #include "include/gpu/GpuTypes.h"
23 #include "include/gpu/GrBackendSurface.h"
24 #include "include/gpu/GrDirectContext.h"
25 #include "include/gpu/GrTypes.h"
26 #include "include/private/SkColorData.h"
27 #include "include/private/gpu/ganesh/GrTypesPriv.h"
28 #include "src/gpu/SkBackingFit.h"
29 #include "src/gpu/Swizzle.h"
30 #include "src/gpu/ganesh/GrCaps.h"
31 #include "src/gpu/ganesh/GrDirectContextPriv.h"
32 #include "src/gpu/ganesh/GrFragmentProcessor.h"
33 #include "src/gpu/ganesh/GrPaint.h"
34 #include "src/gpu/ganesh/GrProxyProvider.h"
35 #include "src/gpu/ganesh/GrSamplerState.h"
36 #include "src/gpu/ganesh/GrSurfaceProxy.h"
37 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
38 #include "src/gpu/ganesh/GrTextureProxy.h"
39 #include "src/gpu/ganesh/GrTextureResolveRenderTask.h"
40 #include "src/gpu/ganesh/SurfaceContext.h"
41 #include "src/gpu/ganesh/SurfaceDrawContext.h"
42 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
43 #include "src/gpu/ganesh/ops/OpsTask.h"
44 #include "tests/CtsEnforcement.h"
45 #include "tests/Test.h"
46 #include "tests/TestUtils.h"
47 #include "tools/gpu/FenceSync.h"
48 #include "tools/gpu/ManagedBackendTexture.h"
49
50 #include <functional>
51 #include <initializer_list>
52 #include <memory>
53 #include <utility>
54
55 struct GrContextOptions;
56
57 using namespace sk_gpu_test;
58
check_pixels(skiatest::Reporter * reporter,GrDirectContext * dContext,const GrBackendTexture & tex,const SkImageInfo & info,SkColor expectedColor)59 bool check_pixels(skiatest::Reporter* reporter,
60 GrDirectContext* dContext,
61 const GrBackendTexture& tex,
62 const SkImageInfo& info,
63 SkColor expectedColor) {
64 // We have to do the readback of the backend texture wrapped in a different Skia surface than
65 // the one used in the main body of the test or else the readPixels call will trigger resolves
66 // itself.
67 sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(dContext,
68 tex,
69 kTopLeft_GrSurfaceOrigin,
70 /*sampleCnt=*/4,
71 kRGBA_8888_SkColorType,
72 nullptr, nullptr);
73 SkBitmap actual;
74 actual.allocPixels(info);
75 if (!surface->readPixels(actual, 0, 0)) {
76 return false;
77 }
78
79 SkBitmap expected;
80 expected.allocPixels(info);
81 SkCanvas tmp(expected);
82 tmp.clear(expectedColor);
83 expected.setImmutable();
84
85 const float tols[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
86
87 auto error = std::function<ComparePixmapsErrorReporter>(
88 [reporter](int x, int y, const float diffs[4]) {
89 SkASSERT(x >= 0 && y >= 0);
90 ERRORF(reporter, "mismatch at %d, %d (%f, %f, %f %f)",
91 x, y, diffs[0], diffs[1], diffs[2], diffs[3]);
92 });
93
94 return ComparePixels(expected.pixmap(), actual.pixmap(), tols, error);
95 }
96
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SurfaceResolveTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)97 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SurfaceResolveTest,
98 reporter,
99 ctxInfo,
100 CtsEnforcement::kApiLevel_T) {
101 auto dContext = ctxInfo.directContext();
102
103 SkImageInfo info = SkImageInfo::Make(8, 8, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
104
105 auto managedTex = ManagedBackendTexture::MakeFromInfo(dContext,
106 info,
107 GrMipmapped::kNo,
108 GrRenderable::kYes);
109 if (!managedTex) {
110 return;
111 }
112 auto tex = managedTex->texture();
113 // Wrap the backend surface but tell it rendering with MSAA so that the wrapped texture is the
114 // resolve.
115 sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(dContext,
116 tex,
117 kTopLeft_GrSurfaceOrigin,
118 /*sampleCnt=*/4,
119 kRGBA_8888_SkColorType,
120 nullptr, nullptr);
121
122 if (!surface) {
123 return;
124 }
125
126 const GrCaps* caps = dContext->priv().caps();
127 // In metal and vulkan if we prefer discardable msaa attachments we will also auto resolve. The
128 // GrBackendTexture and SkSurface are set up in a way that is compatible with discardable msaa
129 // for both backends.
130 bool autoResolves = caps->msaaResolvesAutomatically() ||
131 caps->preferDiscardableMSAAAttachment();
132
133 // First do a simple test where we clear the surface than flush with SkSurface::flush. This
134 // should trigger the resolve and the texture should have the correct data.
135 surface->getCanvas()->clear(SK_ColorRED);
136 surface->flush();
137 dContext->submit();
138 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
139
140 // Next try doing a GrDirectContext::flush which will not trigger a resolve on gpus without
141 // automatic msaa resolves.
142 surface->getCanvas()->clear(SK_ColorBLUE);
143 dContext->flush();
144 dContext->submit();
145 if (autoResolves) {
146 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
147 } else {
148 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
149 }
150
151 // Now doing a surface flush (even without any queued up normal work) should still resolve the
152 // surface.
153 surface->flush();
154 dContext->submit();
155 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
156
157 // Test using SkSurface::resolve with a GrDirectContext::flush
158 surface->getCanvas()->clear(SK_ColorRED);
159 surface->resolveMSAA();
160 dContext->flush();
161 dContext->submit();
162 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
163
164 // Calling resolve again should cause no issues as it is a no-op (there is an assert in the
165 // resolve op that the surface's msaa is dirty, we shouldn't hit that assert).
166 surface->resolveMSAA();
167 dContext->flush();
168 dContext->submit();
169 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
170
171 // Try resolving in the middle of draw calls. Non automatic resolve gpus should only see the
172 // results of the first draw.
173 surface->getCanvas()->clear(SK_ColorGREEN);
174 surface->resolveMSAA();
175 surface->getCanvas()->clear(SK_ColorBLUE);
176 dContext->flush();
177 dContext->submit();
178 if (autoResolves) {
179 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
180 } else {
181 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorGREEN));
182 }
183
184 // Test that a resolve between draws to a different surface doesn't cause the OpsTasks for that
185 // surface to be split. Fails if we hit validation asserts in GrDrawingManager.
186 // First clear out dirty msaa from previous test
187 surface->flush();
188
189 auto otherSurface = SkSurface::MakeRenderTarget(dContext, skgpu::Budgeted::kYes, info);
190 REPORTER_ASSERT(reporter, otherSurface);
191 otherSurface->getCanvas()->clear(SK_ColorRED);
192 surface->resolveMSAA();
193 otherSurface->getCanvas()->clear(SK_ColorBLUE);
194 dContext->flush();
195 dContext->submit();
196
197 // Make sure resolving a non-msaa surface doesn't trigger a resolve call. We'll hit an assert
198 // that the msaa is not dirty if it does.
199 REPORTER_ASSERT(reporter, otherSurface);
200 otherSurface->getCanvas()->clear(SK_ColorRED);
201 otherSurface->resolveMSAA();
202 dContext->flush();
203 dContext->submit();
204 }
205
206 // This test comes from crbug.com/1355807 and crbug.com/1365578. The underlying issue was:
207 // * We would do a non-mipmapped draw of a proxy. This proxy would add a dependency from the ops
208 // task to the proxy's last render task, which was a copy task targetting the proxy.
209 // * We would do a mipmapped draw of the same proxy to the same ops task.
210 // GrRenderTask::addDependency would detect the pre-existing dependency and early out before
211 // adding the proxy to a resolve task.
212 // We also test the case where the first draw should add a MSAA resolve and the second draw should
213 // add a mipmap resolve.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(NonmippedDrawBeforeMippedDraw,reporter,ctxInfo,CtsEnforcement::kNever)214 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(NonmippedDrawBeforeMippedDraw,
215 reporter,
216 ctxInfo,
217 CtsEnforcement::kNever) {
218 using ResolveFlags = GrSurfaceProxy::ResolveFlags;
219 auto dc = ctxInfo.directContext();
220
221 if (!dc->priv().caps()->mipmapSupport()) {
222 return;
223 }
224
225 for (int sampleCount : {1, 4}) {
226 GrRenderable renderable = sampleCount > 1 ? GrRenderable::kYes : GrRenderable::kNo;
227
228 auto bef = dc->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888, renderable);
229 if (sampleCount > 1) {
230 if (dc->priv().caps()->msaaResolvesAutomatically()) {
231 // MSAA won't add a resolve task.
232 continue;
233 }
234 sampleCount = dc->priv().caps()->getRenderTargetSampleCount(sampleCount, bef);
235 if (!sampleCount) {
236 continue;
237 }
238 }
239
240 // Create a mipmapped proxy
241 auto mmProxy = dc->priv().proxyProvider()->createProxy(bef,
242 {64, 64},
243 renderable,
244 sampleCount,
245 GrMipmapped::kYes,
246 SkBackingFit::kExact,
247 skgpu::Budgeted::kYes,
248 GrProtected::kNo,
249 "test MM Proxy");
250 GrSurfaceProxyView mmProxyView{mmProxy,
251 kBottomLeft_GrSurfaceOrigin,
252 skgpu::Swizzle::RGBA()};
253
254 if (sampleCount > 1) {
255 // Make sure MSAA surface needs a resolve by drawing to it. This also adds a last
256 // render task to the proxy.
257 auto drawContext = skgpu::v1::SurfaceDrawContext::Make(dc,
258 GrColorType::kRGBA_8888,
259 mmProxy,
260 nullptr,
261 kBottomLeft_GrSurfaceOrigin,
262 SkSurfaceProps{});
263 drawContext->fillWithFP(GrFragmentProcessor::MakeColor(SK_PMColor4fWHITE));
264 } else {
265 // Use a copy, as in the original bug, to dirty the mipmap status and also install
266 // a last render task on the proxy.
267 auto src = dc->priv().proxyProvider()->createProxy(bef,
268 {64, 64},
269 GrRenderable::kNo,
270 1,
271 GrMipmapped::kNo,
272 SkBackingFit::kExact,
273 skgpu::Budgeted::kYes,
274 GrProtected::kNo,
275 "testSrc");
276 skgpu::v1::SurfaceContext mmSC(dc,
277 mmProxyView,
278 {GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr});
279 mmSC.testCopy(src);
280 }
281
282 auto drawDst = skgpu::v1::SurfaceDrawContext::Make(dc,
283 GrColorType::kRGBA_8888,
284 nullptr,
285 SkBackingFit::kExact,
286 {8, 8},
287 SkSurfaceProps{},
288 "testDrawDst");
289
290 // Do a non-mipmapped draw from the mipmapped texture. This should add a dependency on the
291 // copy task recorded above. If the src texture is also multisampled this should record a
292 // msaa-only resolve.
293 {
294 auto te = GrTextureEffect::Make(
295 mmProxyView,
296 kPremul_SkAlphaType,
297 SkMatrix::I(),
298 GrSamplerState{SkFilterMode::kLinear, SkMipmapMode::kNone},
299 *dc->priv().caps());
300
301 GrPaint paint;
302 paint.setColorFragmentProcessor(std::move(te));
303
304 drawDst->drawRect(nullptr,
305 std::move(paint),
306 GrAA::kNo,
307 SkMatrix::Scale(1/8.f, 1/8.f),
308 SkRect::Make(mmProxy->dimensions()));
309 if (sampleCount > 1) {
310 const GrTextureResolveRenderTask* resolveTask =
311 drawDst->getOpsTask()->resolveTask();
312 if (!resolveTask) {
313 ERRORF(reporter, "No resolve task after drawing MSAA proxy");
314 return;
315 }
316 if (resolveTask->flagsForProxy(mmProxy) != ResolveFlags::kMSAA) {
317 ERRORF(reporter, "Expected resolve flags to be kMSAA");
318 return;
319 }
320 }
321 }
322
323 // Now do a mipmapped draw from the same texture. Ensure that even though we have a
324 // dependency on the copy task we still ensure that a resolve is recorded.
325 {
326 auto te = GrTextureEffect::Make(
327 mmProxyView,
328 kPremul_SkAlphaType,
329 SkMatrix::I(),
330 GrSamplerState{SkFilterMode::kLinear, SkMipmapMode::kLinear},
331 *dc->priv().caps());
332
333 GrPaint paint;
334 paint.setColorFragmentProcessor(std::move(te));
335
336 drawDst->drawRect(nullptr,
337 std::move(paint),
338 GrAA::kNo,
339 SkMatrix::Scale(1/8.f, 1/8.f),
340 SkRect::Make(mmProxy->dimensions()));
341 }
342 const GrTextureResolveRenderTask* resolveTask = drawDst->getOpsTask()->resolveTask();
343 if (!resolveTask) {
344 ERRORF(reporter, "No resolve task after drawing mip mapped proxy");
345 return;
346 }
347
348 ResolveFlags expectedFlags = GrSurfaceProxy::ResolveFlags::kMipMaps;
349 const char* expectedStr = "kMipMaps";
350 if (sampleCount > 1) {
351 expectedFlags |= GrSurfaceProxy::ResolveFlags::kMSAA;
352 expectedStr = "kMipMaps|kMSAA";
353 }
354 if (resolveTask->flagsForProxy(mmProxy) != expectedFlags) {
355 ERRORF(reporter, "Expected resolve flags to be %s", expectedStr);
356 return;
357 }
358 }
359 }
360