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/ganesh/GrBackendSurface.h"
24 #include "include/gpu/ganesh/GrDirectContext.h"
25 #include "include/gpu/ganesh/GrTypes.h"
26 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
27 #include "include/private/SkColorData.h"
28 #include "include/private/gpu/ganesh/GrTypesPriv.h"
29 #include "src/gpu/SkBackingFit.h"
30 #include "src/gpu/Swizzle.h"
31 #include "src/gpu/ganesh/GrCaps.h"
32 #include "src/gpu/ganesh/GrDirectContextPriv.h"
33 #include "src/gpu/ganesh/GrFragmentProcessor.h"
34 #include "src/gpu/ganesh/GrPaint.h"
35 #include "src/gpu/ganesh/GrProxyProvider.h"
36 #include "src/gpu/ganesh/GrSamplerState.h"
37 #include "src/gpu/ganesh/GrSurfaceProxy.h"
38 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
39 #include "src/gpu/ganesh/GrTextureProxy.h"
40 #include "src/gpu/ganesh/GrTextureResolveRenderTask.h"
41 #include "src/gpu/ganesh/SurfaceContext.h"
42 #include "src/gpu/ganesh/SurfaceDrawContext.h"
43 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
44 #include "src/gpu/ganesh/ops/OpsTask.h"
45 #include "tests/CtsEnforcement.h"
46 #include "tests/Test.h"
47 #include "tests/TestUtils.h"
48 #include "tools/gpu/FenceSync.h"
49 #include "tools/gpu/ManagedBackendTexture.h"
50
51 #include <functional>
52 #include <initializer_list>
53 #include <memory>
54 #include <utility>
55
56 struct GrContextOptions;
57
58 using namespace sk_gpu_test;
59
check_pixels(skiatest::Reporter * reporter,GrDirectContext * dContext,const GrBackendTexture & tex,const SkImageInfo & info,SkColor expectedColor)60 bool check_pixels(skiatest::Reporter* reporter,
61 GrDirectContext* dContext,
62 const GrBackendTexture& tex,
63 const SkImageInfo& info,
64 SkColor expectedColor) {
65 // We have to do the readback of the backend texture wrapped in a different Skia surface than
66 // the one used in the main body of the test or else the readPixels call will trigger resolves
67 // itself.
68 sk_sp<SkSurface> surface = SkSurfaces::WrapBackendTexture(dContext,
69 tex,
70 kTopLeft_GrSurfaceOrigin,
71 /*sampleCnt=*/4,
72 kRGBA_8888_SkColorType,
73 nullptr,
74 nullptr);
75 SkBitmap actual;
76 actual.allocPixels(info);
77 if (!surface->readPixels(actual, 0, 0)) {
78 return false;
79 }
80
81 SkBitmap expected;
82 expected.allocPixels(info);
83 SkCanvas tmp(expected);
84 tmp.clear(expectedColor);
85 expected.setImmutable();
86
87 const float tols[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
88
89 auto error = std::function<ComparePixmapsErrorReporter>(
90 [reporter](int x, int y, const float diffs[4]) {
91 SkASSERT(x >= 0 && y >= 0);
92 ERRORF(reporter, "mismatch at %d, %d (%f, %f, %f %f)",
93 x, y, diffs[0], diffs[1], diffs[2], diffs[3]);
94 });
95
96 return ComparePixels(expected.pixmap(), actual.pixmap(), tols, error);
97 }
98
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SurfaceResolveTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)99 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SurfaceResolveTest,
100 reporter,
101 ctxInfo,
102 CtsEnforcement::kApiLevel_T) {
103 auto dContext = ctxInfo.directContext();
104
105 SkImageInfo info = SkImageInfo::Make(8, 8, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
106
107 auto managedTex = ManagedBackendTexture::MakeFromInfo(
108 dContext, info, skgpu::Mipmapped::kNo, 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 = SkSurfaces::WrapBackendTexture(dContext,
116 tex,
117 kTopLeft_GrSurfaceOrigin,
118 /*sampleCnt=*/4,
119 kRGBA_8888_SkColorType,
120 nullptr,
121 nullptr);
122
123 if (!surface) {
124 return;
125 }
126
127 const GrCaps* caps = dContext->priv().caps();
128 // In metal and vulkan if we prefer discardable msaa attachments we will also auto resolve. The
129 // GrBackendTexture and SkSurface are set up in a way that is compatible with discardable msaa
130 // for both backends.
131 bool autoResolves = caps->msaaResolvesAutomatically() ||
132 caps->preferDiscardableMSAAAttachment();
133
134 // First do a simple test where we clear the surface than flush with SkSurface::flush. This
135 // should trigger the resolve and the texture should have the correct data.
136 surface->getCanvas()->clear(SK_ColorRED);
137 dContext->flush(surface.get());
138 dContext->submit();
139 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
140
141 // Next try doing a GrDirectContext::flush without the surface which will not trigger a resolve
142 // on gpus without automatic msaa resolves.
143 surface->getCanvas()->clear(SK_ColorBLUE);
144 dContext->flush();
145 dContext->submit();
146 if (autoResolves) {
147 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
148 } else {
149 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
150 }
151
152 // Now doing a surface flush (even without any queued up normal work) should still resolve the
153 // surface.
154 dContext->flush(surface.get());
155 dContext->submit();
156 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
157
158 // Test using SkSurface::resolve with a GrDirectContext::flush
159 surface->getCanvas()->clear(SK_ColorRED);
160 SkSurfaces::ResolveMSAA(surface);
161 dContext->flush();
162 dContext->submit();
163 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
164
165 // Calling resolve again should cause no issues as it is a no-op (there is an assert in the
166 // resolve op that the surface's msaa is dirty, we shouldn't hit that assert).
167 SkSurfaces::ResolveMSAA(surface);
168 dContext->flush();
169 dContext->submit();
170 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
171
172 // Try resolving in the middle of draw calls. Non automatic resolve gpus should only see the
173 // results of the first draw.
174 surface->getCanvas()->clear(SK_ColorGREEN);
175 SkSurfaces::ResolveMSAA(surface);
176 surface->getCanvas()->clear(SK_ColorBLUE);
177 dContext->flush();
178 dContext->submit();
179 if (autoResolves) {
180 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
181 } else {
182 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorGREEN));
183 }
184
185 // Test that a resolve between draws to a different surface doesn't cause the OpsTasks for that
186 // surface to be split. Fails if we hit validation asserts in GrDrawingManager.
187 // First clear out dirty msaa from previous test
188 dContext->flush(surface.get());
189
190 auto otherSurface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kYes, info);
191 REPORTER_ASSERT(reporter, otherSurface);
192 otherSurface->getCanvas()->clear(SK_ColorRED);
193 SkSurfaces::ResolveMSAA(surface);
194 otherSurface->getCanvas()->clear(SK_ColorBLUE);
195 dContext->flush();
196 dContext->submit();
197
198 // Make sure resolving a non-msaa surface doesn't trigger a resolve call. We'll hit an assert
199 // that the msaa is not dirty if it does.
200 REPORTER_ASSERT(reporter, otherSurface);
201 otherSurface->getCanvas()->clear(SK_ColorRED);
202 SkSurfaces::ResolveMSAA(otherSurface);
203 dContext->flush();
204 dContext->submit();
205 }
206
207 // This test comes from crbug.com/1355807 and crbug.com/1365578. The underlying issue was:
208 // * We would do a non-mipmapped draw of a proxy. This proxy would add a dependency from the ops
209 // task to the proxy's last render task, which was a copy task targetting the proxy.
210 // * We would do a mipmapped draw of the same proxy to the same ops task.
211 // GrRenderTask::addDependency would detect the pre-existing dependency and early out before
212 // adding the proxy to a resolve task.
213 // We also test the case where the first draw should add a MSAA resolve and the second draw should
214 // add a mipmap resolve.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(NonmippedDrawBeforeMippedDraw,reporter,ctxInfo,CtsEnforcement::kNever)215 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(NonmippedDrawBeforeMippedDraw,
216 reporter,
217 ctxInfo,
218 CtsEnforcement::kNever) {
219 using ResolveFlags = GrSurfaceProxy::ResolveFlags;
220 auto dc = ctxInfo.directContext();
221
222 if (!dc->priv().caps()->mipmapSupport()) {
223 return;
224 }
225
226 for (int sampleCount : {1, 4}) {
227 GrRenderable renderable = sampleCount > 1 ? GrRenderable::kYes : GrRenderable::kNo;
228
229 auto bef = dc->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888, renderable);
230 if (sampleCount > 1) {
231 if (dc->priv().caps()->msaaResolvesAutomatically()) {
232 // MSAA won't add a resolve task.
233 continue;
234 }
235 sampleCount = dc->priv().caps()->getRenderTargetSampleCount(sampleCount, bef);
236 if (!sampleCount) {
237 continue;
238 }
239 }
240
241 // Create a mipmapped proxy
242 auto mmProxy = dc->priv().proxyProvider()->createProxy(bef,
243 {64, 64},
244 renderable,
245 sampleCount,
246 skgpu::Mipmapped::kYes,
247 SkBackingFit::kExact,
248 skgpu::Budgeted::kYes,
249 GrProtected::kNo,
250 "test MM Proxy");
251 GrSurfaceProxyView mmProxyView{mmProxy,
252 kBottomLeft_GrSurfaceOrigin,
253 skgpu::Swizzle::RGBA()};
254
255 if (sampleCount > 1) {
256 // Make sure MSAA surface needs a resolve by drawing to it. This also adds a last
257 // render task to the proxy.
258 auto drawContext = skgpu::ganesh::SurfaceDrawContext::Make(dc,
259 GrColorType::kRGBA_8888,
260 mmProxy,
261 nullptr,
262 kBottomLeft_GrSurfaceOrigin,
263 SkSurfaceProps{});
264 drawContext->fillWithFP(GrFragmentProcessor::MakeColor(SK_PMColor4fWHITE));
265 } else {
266 // Use a copy, as in the original bug, to dirty the mipmap status and also install
267 // a last render task on the proxy.
268 auto src = dc->priv().proxyProvider()->createProxy(bef,
269 {64, 64},
270 GrRenderable::kNo,
271 1,
272 skgpu::Mipmapped::kNo,
273 SkBackingFit::kExact,
274 skgpu::Budgeted::kYes,
275 GrProtected::kNo,
276 "testSrc");
277 skgpu::ganesh::SurfaceContext mmSC(
278 dc, mmProxyView, {GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr});
279 mmSC.testCopy(src);
280 }
281
282 auto drawDst = skgpu::ganesh::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