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 "tests/Test.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkSurface.h"
13 #include "src/gpu/GrCaps.h"
14 #include "src/gpu/GrDirectContextPriv.h"
15 #include "src/gpu/GrPixmap.h"
16 #include "tests/TestUtils.h"
17 #include "tools/gpu/ManagedBackendTexture.h"
18
19 using namespace sk_gpu_test;
20
check_pixels(skiatest::Reporter * reporter,GrDirectContext * dContext,const GrBackendTexture & tex,const SkImageInfo & info,SkColor expectedColor)21 bool check_pixels(skiatest::Reporter* reporter,
22 GrDirectContext* dContext,
23 const GrBackendTexture& tex,
24 const SkImageInfo& info,
25 SkColor expectedColor) {
26 // We have to do the readback of the backend texture wrapped in a different Skia surface than
27 // the one used in the main body of the test or else the readPixels call will trigger resolves
28 // itself.
29 sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(dContext,
30 tex,
31 kTopLeft_GrSurfaceOrigin,
32 /*sampleCnt=*/4,
33 kRGBA_8888_SkColorType,
34 nullptr, nullptr);
35 SkBitmap actual;
36 actual.allocPixels(info);
37 if (!surface->readPixels(actual, 0, 0)) {
38 return false;
39 }
40
41 SkBitmap expected;
42 expected.allocPixels(info);
43 SkCanvas tmp(expected);
44 tmp.clear(expectedColor);
45 expected.setImmutable();
46
47 const float tols[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
48
49 auto error = std::function<ComparePixmapsErrorReporter>(
50 [reporter](int x, int y, const float diffs[4]) {
51 SkASSERT(x >= 0 && y >= 0);
52 ERRORF(reporter, "mismatch at %d, %d (%f, %f, %f %f)",
53 x, y, diffs[0], diffs[1], diffs[2], diffs[3]);
54 });
55
56 return ComparePixels(expected.pixmap(), actual.pixmap(), tols, error);
57 }
58
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceResolveTest,reporter,ctxInfo)59 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceResolveTest, reporter, ctxInfo) {
60 auto dContext = ctxInfo.directContext();
61
62 SkImageInfo info = SkImageInfo::Make(8, 8, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
63
64 auto managedTex = ManagedBackendTexture::MakeFromInfo(dContext,
65 info,
66 GrMipmapped::kNo,
67 GrRenderable::kYes);
68 if (!managedTex) {
69 return;
70 }
71 auto tex = managedTex->texture();
72 // Wrap the backend surface but tell it rendering with MSAA so that the wrapped texture is the
73 // resolve.
74 sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(dContext,
75 tex,
76 kTopLeft_GrSurfaceOrigin,
77 /*sampleCnt=*/4,
78 kRGBA_8888_SkColorType,
79 nullptr, nullptr);
80
81 if (!surface) {
82 return;
83 }
84
85 const GrCaps* caps = dContext->priv().caps();
86 // In metal and vulkan if we prefer discardable msaa attachments we will also auto resolve. The
87 // GrBackendTexture and SkSurface are set up in a way that is compatible with discardable msaa
88 // for both backends.
89 bool autoResolves = caps->msaaResolvesAutomatically() ||
90 caps->preferDiscardableMSAAAttachment();
91
92 // First do a simple test where we clear the surface than flush with SkSurface::flush. This
93 // should trigger the resolve and the texture should have the correct data.
94 surface->getCanvas()->clear(SK_ColorRED);
95 surface->flush();
96 dContext->submit();
97 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
98
99 // Next try doing a GrDirectContext::flush which will not trigger a resolve on gpus without
100 // automatic msaa resolves.
101 surface->getCanvas()->clear(SK_ColorBLUE);
102 dContext->flush();
103 dContext->submit();
104 if (autoResolves) {
105 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
106 } else {
107 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
108 }
109
110 // Now doing a surface flush (even without any queued up normal work) should still resolve the
111 // surface.
112 surface->flush();
113 dContext->submit();
114 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
115
116 // Test using SkSurface::resolve with a GrDirectContext::flush
117 surface->getCanvas()->clear(SK_ColorRED);
118 surface->resolveMSAA();
119 dContext->flush();
120 dContext->submit();
121 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
122
123 // Calling resolve again should cause no issues as it is a no-op (there is an assert in the
124 // resolve op that the surface's msaa is dirty, we shouldn't hit that assert).
125 surface->resolveMSAA();
126 dContext->flush();
127 dContext->submit();
128 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorRED));
129
130 // Try resolving in the middle of draw calls. Non automatic resolve gpus should only see the
131 // results of the first draw.
132 surface->getCanvas()->clear(SK_ColorGREEN);
133 surface->resolveMSAA();
134 surface->getCanvas()->clear(SK_ColorBLUE);
135 dContext->flush();
136 dContext->submit();
137 if (autoResolves) {
138 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorBLUE));
139 } else {
140 REPORTER_ASSERT(reporter, check_pixels(reporter, dContext, tex, info, SK_ColorGREEN));
141 }
142
143 // Test that a resolve between draws to a different surface doesn't cause the OpsTasks for that
144 // surface to be split. Fails if we hit validation asserts in GrDrawingManager.
145 // First clear out dirty msaa from previous test
146 surface->flush();
147
148 auto otherSurface = SkSurface::MakeRenderTarget(dContext, SkBudgeted::kYes, info);
149 REPORTER_ASSERT(reporter, otherSurface);
150 otherSurface->getCanvas()->clear(SK_ColorRED);
151 surface->resolveMSAA();
152 otherSurface->getCanvas()->clear(SK_ColorBLUE);
153 dContext->flush();
154 dContext->submit();
155
156 // Make sure resolving a non-msaa surface doesn't trigger a resolve call. We'll hit an assert
157 // that the msaa is not dirty if it does.
158 REPORTER_ASSERT(reporter, otherSurface);
159 otherSurface->getCanvas()->clear(SK_ColorRED);
160 otherSurface->resolveMSAA();
161 dContext->flush();
162 dContext->submit();
163 }
164
165