• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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