xref: /aosp_15_r20/external/skia/tests/GrMipMappedTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2017 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/SkBlendMode.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkColorType.h"
15 #include "include/core/SkImage.h"
16 #include "include/core/SkImageInfo.h"
17 #include "include/core/SkMatrix.h"
18 #include "include/core/SkRect.h"
19 #include "include/core/SkRefCnt.h"
20 #include "include/core/SkSamplingOptions.h"
21 #include "include/core/SkSurface.h"
22 #include "include/core/SkSurfaceProps.h"
23 #include "include/core/SkTypes.h"
24 #include "include/gpu/GpuTypes.h"
25 #include "include/gpu/ganesh/GrBackendSurface.h"
26 #include "include/gpu/ganesh/GrContextOptions.h"
27 #include "include/gpu/ganesh/GrDirectContext.h"
28 #include "include/gpu/ganesh/GrRecordingContext.h"
29 #include "include/gpu/ganesh/GrTypes.h"
30 #include "include/gpu/ganesh/SkImageGanesh.h"
31 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
32 #include "include/gpu/ganesh/mock/GrMockTypes.h"
33 #include "include/private/SkColorData.h"
34 #include "include/private/gpu/ganesh/GrTextureGenerator.h"
35 #include "include/private/gpu/ganesh/GrTypesPriv.h"
36 #include "src/gpu/SkBackingFit.h"
37 #include "src/gpu/Swizzle.h"
38 #include "src/gpu/ganesh/Device.h"
39 #include "src/gpu/ganesh/GrBackendTextureImageGenerator.h"
40 #include "src/gpu/ganesh/GrCaps.h"
41 #include "src/gpu/ganesh/GrColorSpaceXform.h"
42 #include "src/gpu/ganesh/GrDirectContextPriv.h"
43 #include "src/gpu/ganesh/GrDrawingManager.h"
44 #include "src/gpu/ganesh/GrProxyProvider.h"
45 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
46 #include "src/gpu/ganesh/GrSamplerState.h"
47 #include "src/gpu/ganesh/GrSemaphore.h"
48 #include "src/gpu/ganesh/GrSurfaceProxy.h"
49 #include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
50 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
51 #include "src/gpu/ganesh/GrTexture.h"
52 #include "src/gpu/ganesh/GrTextureProxy.h"
53 #include "src/gpu/ganesh/SkGr.h"
54 #include "src/gpu/ganesh/SurfaceDrawContext.h"
55 #include "src/gpu/ganesh/ops/OpsTask.h"
56 #include "src/gpu/ganesh/surface/SkSurface_Ganesh.h"
57 #include "tests/CtsEnforcement.h"
58 #include "tests/Test.h"
59 #include "tools/gpu/BackendSurfaceFactory.h"
60 #include "tools/gpu/BackendTextureImageFactory.h"
61 #include "tools/gpu/ManagedBackendTexture.h"
62 #include "tools/gpu/ProxyUtils.h"
63 
64 #include <initializer_list>
65 #include <memory>
66 #include <utility>
67 
68 class GrRenderTask;
69 
70 #if defined(SK_DIRECT3D)
71 #include "include/gpu/ganesh/d3d/GrD3DTypes.h"
72 #endif
73 
74 #if defined(SK_METAL)
75 #include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h"
76 #endif
77 
78 #if defined(SK_GL)
79 #include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
80 #include "include/gpu/ganesh/gl/GrGLTypes.h"
81 #endif
82 
83 #if defined(SK_VULKAN)
84 #include "include/gpu/ganesh/vk/GrVkBackendSurface.h"
85 #include "include/gpu/ganesh/vk/GrVkTypes.h"
86 #endif
87 
88 static constexpr int kSize = 8;
89 
90 // Test that the correct mip map states are on the GrTextures when wrapping GrBackendTextures in
91 // SkImages and SkSurfaces
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrWrappedMipMappedTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)92 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrWrappedMipMappedTest,
93                                        reporter,
94                                        ctxInfo,
95                                        CtsEnforcement::kApiLevel_T) {
96     using namespace skgpu;
97 
98     auto dContext = ctxInfo.directContext();
99     if (!dContext->priv().caps()->mipmapSupport()) {
100         return;
101     }
102 
103     Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
104 
105     for (auto mipmapped : { Mipmapped::kNo, Mipmapped::kYes }) {
106         for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
107             // createBackendTexture currently doesn't support uploading data to mip maps
108             // so we don't send any. However, we pretend there is data for the checks below which is
109             // fine since we are never actually using these textures for any work on the gpu.
110             auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithData(dContext,
111                                                                          kSize,
112                                                                          kSize,
113                                                                          kRGBA_8888_SkColorType,
114                                                                          SkColors::kTransparent,
115                                                                          mipmapped,
116                                                                          renderable,
117                                                                          isProtected);
118             if (!mbet) {
119                 ERRORF(reporter, "Could not make texture.");
120                 return;
121             }
122 
123             sk_sp<GrTextureProxy> proxy;
124             sk_sp<SkImage> image;
125             if (renderable == GrRenderable::kYes) {
126                 sk_sp<SkSurface> surface = SkSurfaces::WrapBackendTexture(
127                         dContext,
128                         mbet->texture(),
129                         kTopLeft_GrSurfaceOrigin,
130                         0,
131                         kRGBA_8888_SkColorType,
132                         /*color space*/ nullptr,
133                         /*surface props*/ nullptr,
134                         sk_gpu_test::ManagedBackendTexture::ReleaseProc,
135                         mbet->releaseContext());
136 
137                 auto device = ((SkSurface_Ganesh*)surface.get())->getDevice();
138                 proxy = device->readSurfaceView().asTextureProxyRef();
139             } else {
140                 image = SkImages::BorrowTextureFrom(dContext,
141                                                     mbet->texture(),
142                                                     kTopLeft_GrSurfaceOrigin,
143                                                     kRGBA_8888_SkColorType,
144                                                     kPremul_SkAlphaType,
145                                                     /* color space */ nullptr,
146                                                     sk_gpu_test::ManagedBackendTexture::ReleaseProc,
147                                                     mbet->releaseContext());
148                 REPORTER_ASSERT(reporter, (mipmapped == Mipmapped::kYes) == image->hasMipmaps());
149                 proxy = sk_ref_sp(sk_gpu_test::GetTextureImageProxy(image.get(), dContext));
150             }
151             REPORTER_ASSERT(reporter, proxy);
152             if (!proxy) {
153                 continue;
154             }
155 
156             REPORTER_ASSERT(reporter, proxy->isInstantiated());
157 
158             GrTexture* texture = proxy->peekTexture();
159             REPORTER_ASSERT(reporter, texture);
160             if (!texture) {
161                 continue;
162             }
163 
164             if (mipmapped == Mipmapped::kYes) {
165                 REPORTER_ASSERT(reporter, Mipmapped::kYes == texture->mipmapped());
166                 if (GrRenderable::kYes == renderable) {
167                     REPORTER_ASSERT(reporter, texture->mipmapsAreDirty());
168                 } else {
169                     REPORTER_ASSERT(reporter, !texture->mipmapsAreDirty());
170                 }
171             } else {
172                 REPORTER_ASSERT(reporter, Mipmapped::kNo == texture->mipmapped());
173             }
174         }
175     }
176 }
177 
178 // Test that we correctly copy or don't copy GrBackendTextures in the GrBackendTextureImageGenerator
179 // based on if we will use mips in the draw and the mip status of the GrBackendTexture.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrBackendTextureImageMipMappedTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)180 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrBackendTextureImageMipMappedTest,
181                                        reporter,
182                                        ctxInfo,
183                                        CtsEnforcement::kApiLevel_T) {
184     using namespace skgpu;
185 
186     auto dContext = ctxInfo.directContext();
187     if (!dContext->priv().caps()->mipmapSupport()) {
188         return;
189     }
190 
191     Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
192 
193     for (auto betMipmapped : { Mipmapped::kNo, Mipmapped::kYes }) {
194         for (auto requestMipmapped : { Mipmapped::kNo, Mipmapped::kYes }) {
195             auto ii =
196                     SkImageInfo::Make({kSize, kSize}, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
197             sk_sp<SkImage> image = sk_gpu_test::MakeBackendTextureImage(
198                     dContext, ii, SkColors::kTransparent, betMipmapped,
199                     Renderable::kNo,
200                     GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin,
201                     isProtected);
202             REPORTER_ASSERT(reporter, (betMipmapped == Mipmapped::kYes) == image->hasMipmaps());
203 
204             GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(image.get(), dContext);
205             REPORTER_ASSERT(reporter, proxy);
206             if (!proxy) {
207                 return;
208             }
209 
210             REPORTER_ASSERT(reporter, proxy->isInstantiated());
211 
212             sk_sp<GrTexture> texture = sk_ref_sp(proxy->peekTexture());
213             REPORTER_ASSERT(reporter, texture);
214             if (!texture) {
215                 return;
216             }
217 
218             std::unique_ptr<GrTextureGenerator> textureGen = GrBackendTextureImageGenerator::Make(
219                     texture, kTopLeft_GrSurfaceOrigin, nullptr, kRGBA_8888_SkColorType,
220                     kPremul_SkAlphaType, nullptr);
221             REPORTER_ASSERT(reporter, textureGen);
222             if (!textureGen) {
223                 return;
224             }
225 
226             SkImageInfo imageInfo = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
227                                                       kPremul_SkAlphaType);
228             GrSurfaceProxyView genView = textureGen->generateTexture(
229                     dContext, imageInfo, requestMipmapped, GrImageTexGenPolicy::kDraw);
230             GrSurfaceProxy* genProxy = genView.proxy();
231 
232             REPORTER_ASSERT(reporter, genProxy);
233             if (!genProxy) {
234                 return;
235             }
236 
237             if (genProxy->isLazy()) {
238                 genProxy->priv().doLazyInstantiation(dContext->priv().resourceProvider());
239             } else if (!genProxy->isInstantiated()) {
240                 genProxy->instantiate(dContext->priv().resourceProvider());
241             }
242 
243             REPORTER_ASSERT(reporter, genProxy->isInstantiated());
244             if (!genProxy->isInstantiated()) {
245                 return;
246             }
247 
248             GrTexture* genTexture = genProxy->peekTexture();
249             REPORTER_ASSERT(reporter, genTexture);
250             if (!genTexture) {
251                 return;
252             }
253 
254             GrBackendTexture backendTex = texture->getBackendTexture();
255             GrBackendTexture genBackendTex = genTexture->getBackendTexture();
256 
257             if (GrBackendApi::kOpenGL == genBackendTex.backend()) {
258 #ifdef SK_GL
259                 GrGLTextureInfo genTexInfo;
260                 GrGLTextureInfo origTexInfo;
261                 if (GrBackendTextures::GetGLTextureInfo(genBackendTex, &genTexInfo) &&
262                     GrBackendTextures::GetGLTextureInfo(backendTex, &origTexInfo)) {
263                     if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
264                         // We did a copy so the texture IDs should be different
265                         REPORTER_ASSERT(reporter, origTexInfo.fID != genTexInfo.fID);
266                     } else {
267                         REPORTER_ASSERT(reporter, origTexInfo.fID == genTexInfo.fID);
268                     }
269                 } else {
270                     ERRORF(reporter, "Failed to get GrGLTextureInfo");
271                 }
272 #endif
273 #ifdef SK_VULKAN
274             } else if (GrBackendApi::kVulkan == genBackendTex.backend()) {
275                 GrVkImageInfo genImageInfo;
276                 GrVkImageInfo origImageInfo;
277                 if (GrBackendTextures::GetVkImageInfo(genBackendTex, &genImageInfo) &&
278                     GrBackendTextures::GetVkImageInfo(backendTex, &origImageInfo)) {
279                     if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
280                         // We did a copy so the texture IDs should be different
281                         REPORTER_ASSERT(reporter, origImageInfo.fImage != genImageInfo.fImage);
282                     } else {
283                         REPORTER_ASSERT(reporter, origImageInfo.fImage == genImageInfo.fImage);
284                     }
285                 } else {
286                     ERRORF(reporter, "Failed to get GrVkImageInfo");
287                 }
288 #endif
289 #ifdef SK_METAL
290             } else if (GrBackendApi::kMetal == genBackendTex.backend()) {
291                 GrMtlTextureInfo genImageInfo;
292                 GrMtlTextureInfo origImageInfo;
293                 if (GrBackendTextures::GetMtlTextureInfo(genBackendTex, &genImageInfo) &&
294                     GrBackendTextures::GetMtlTextureInfo(backendTex, &origImageInfo)) {
295                     if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
296                         // We did a copy so the texture IDs should be different
297                         REPORTER_ASSERT(reporter, origImageInfo.fTexture != genImageInfo.fTexture);
298                     } else {
299                         REPORTER_ASSERT(reporter, origImageInfo.fTexture == genImageInfo.fTexture);
300                     }
301                 } else {
302                     ERRORF(reporter, "Failed to get GrMtlTextureInfo");
303                 }
304 #endif
305 #ifdef SK_DIRECT3D
306             } else if (GrBackendApi::kDirect3D == genBackendTex.backend()) {
307                 GrD3DTextureResourceInfo genImageInfo;
308                 GrD3DTextureResourceInfo origImageInfo;
309                 if (genBackendTex.getD3DTextureResourceInfo(&genImageInfo) &&
310                     backendTex.getD3DTextureResourceInfo(&origImageInfo)) {
311                     if (requestMipmapped == Mipmapped::kYes && betMipmapped == Mipmapped::kNo) {
312                         // We did a copy so the texture resources should be different
313                         REPORTER_ASSERT(reporter,
314                                         origImageInfo.fResource != genImageInfo.fResource);
315                     } else {
316                         REPORTER_ASSERT(reporter,
317                                         origImageInfo.fResource == genImageInfo.fResource);
318                     }
319                 } else {
320                     ERRORF(reporter, "Failed to get GrMtlTextureInfo");
321                 }
322 #endif
323             } else {
324                 REPORTER_ASSERT(reporter, false);
325             }
326         }
327     }
328 }
329 
330 // Test that when we call makeImageSnapshot on an SkSurface we retains the same mip status as the
331 // resource we took the snapshot of.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrImageSnapshotMipMappedTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)332 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrImageSnapshotMipMappedTest,
333                                        reporter,
334                                        ctxInfo,
335                                        CtsEnforcement::kApiLevel_T) {
336     auto dContext = ctxInfo.directContext();
337     if (!dContext->priv().caps()->mipmapSupport()) {
338         return;
339     }
340 
341     GrProtected isProtected = GrProtected(dContext->priv().caps()->supportsProtectedContent());
342 
343     auto resourceProvider = dContext->priv().resourceProvider();
344 
345     for (auto willUseMips : {false, true}) {
346         for (auto isWrapped : {false, true}) {
347             skgpu::Mipmapped mipmapped =
348                     willUseMips ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
349             sk_sp<SkSurface> surface;
350             SkImageInfo info =
351                     SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
352             if (isWrapped) {
353                 surface = sk_gpu_test::MakeBackendTextureSurface(dContext,
354                                                                  info,
355                                                                  kTopLeft_GrSurfaceOrigin,
356                                                                  /* sample count */ 1,
357                                                                  mipmapped,
358                                                                  isProtected);
359             } else {
360                 surface = SkSurfaces::RenderTarget(dContext,
361                                                    skgpu::Budgeted::kYes,
362                                                    info,
363                                                    /* sample count */ 1,
364                                                    kTopLeft_GrSurfaceOrigin,
365                                                    nullptr,
366                                                    willUseMips);
367             }
368             REPORTER_ASSERT(reporter, surface);
369             auto device = ((SkSurface_Ganesh*)surface.get())->getDevice();
370             GrTextureProxy* texProxy = device->readSurfaceView().asTextureProxy();
371             REPORTER_ASSERT(reporter, mipmapped == texProxy->mipmapped());
372 
373             texProxy->instantiate(resourceProvider);
374             GrTexture* texture = texProxy->peekTexture();
375             REPORTER_ASSERT(reporter, mipmapped == texture->mipmapped());
376 
377             sk_sp<SkImage> image = surface->makeImageSnapshot();
378             REPORTER_ASSERT(reporter, willUseMips == image->hasMipmaps());
379             REPORTER_ASSERT(reporter, image);
380             texProxy = sk_gpu_test::GetTextureImageProxy(image.get(), dContext);
381             REPORTER_ASSERT(reporter, mipmapped == texProxy->mipmapped());
382 
383             texProxy->instantiate(resourceProvider);
384             texture = texProxy->peekTexture();
385             REPORTER_ASSERT(reporter, mipmapped == texture->mipmapped());
386         }
387     }
388 }
389 
390 // Test that we don't create a mip mapped texture if the size is 1x1 even if the filter mode is set
391 // to use mips. This test passes by not crashing or hitting asserts in code.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(Gr1x1TextureMipMappedTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)392 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(Gr1x1TextureMipMappedTest,
393                                        reporter,
394                                        ctxInfo,
395                                        CtsEnforcement::kApiLevel_T) {
396     auto dContext = ctxInfo.directContext();
397     if (!dContext->priv().caps()->mipmapSupport()) {
398         return;
399     }
400 
401     // Make surface to draw into
402     SkImageInfo info = SkImageInfo::MakeN32(16, 16, kPremul_SkAlphaType);
403     sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kNo, info);
404 
405     // Make 1x1 raster bitmap
406     SkBitmap bmp;
407     bmp.allocN32Pixels(1, 1);
408     SkPMColor* pixel = reinterpret_cast<SkPMColor*>(bmp.getPixels());
409     *pixel = 0;
410 
411     sk_sp<SkImage> bmpImage = bmp.asImage();
412 
413     // Make sure we scale so we don't optimize out the use of mips.
414     surface->getCanvas()->scale(0.5f, 0.5f);
415 
416     // This should upload the image to a non mipped GrTextureProxy.
417     surface->getCanvas()->drawImage(bmpImage, 0, 0);
418     dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
419 
420     // Now set the filter quality to high so we use mip maps. We should find the non mipped texture
421     // in the cache for the SkImage. Since the texture is 1x1 we should just use that texture
422     // instead of trying to do a copy to a mipped texture.
423     surface->getCanvas()->drawImage(bmpImage, 0, 0, SkSamplingOptions({1.0f/3, 1.0f/3}));
424     dContext->flushAndSubmit(surface.get(), GrSyncCpu::kNo);
425 }
426 
427 // Create a new render target and draw 'mipmapView' into it using the provided 'filter'.
draw_mipmap_into_new_render_target(GrRecordingContext * rContext,GrColorType colorType,SkAlphaType alphaType,GrSurfaceProxyView mipmapView,GrSamplerState::MipmapMode mm)428 static std::unique_ptr<skgpu::ganesh::SurfaceDrawContext> draw_mipmap_into_new_render_target(
429         GrRecordingContext* rContext,
430         GrColorType colorType,
431         SkAlphaType alphaType,
432         GrSurfaceProxyView mipmapView,
433         GrSamplerState::MipmapMode mm) {
434     auto proxyProvider = rContext->priv().proxyProvider();
435     sk_sp<GrSurfaceProxy> renderTarget =
436             proxyProvider->createProxy(mipmapView.proxy()->backendFormat(),
437                                        {1, 1},
438                                        GrRenderable::kYes,
439                                        1,
440                                        skgpu::Mipmapped::kNo,
441                                        SkBackingFit::kApprox,
442                                        skgpu::Budgeted::kYes,
443                                        GrProtected::kNo,
444                                        /*label=*/"DrawMipMapViewTest");
445 
446     auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(rContext,
447                                                        colorType,
448                                                        std::move(renderTarget),
449                                                        nullptr,
450                                                        kTopLeft_GrSurfaceOrigin,
451                                                        SkSurfaceProps());
452 
453     sdc->drawTexture(nullptr,
454                      std::move(mipmapView),
455                      alphaType,
456                      GrSamplerState::Filter::kLinear,
457                      mm,
458                      SkBlendMode::kSrcOver,
459                      {1, 1, 1, 1},
460                      SkRect::MakeWH(4, 4),
461                      SkRect::MakeWH(1, 1),
462                      GrQuadAAFlags::kAll,
463                      SkCanvas::kFast_SrcRectConstraint,
464                      SkMatrix::I(),
465                      nullptr);
466     return sdc;
467 }
468 
469 // Test that two opsTasks using the same mipmaps both depend on the same GrTextureResolveRenderTask.
470 DEF_GANESH_TEST(GrManyDependentsMipMappedTest,
471                 reporter,
472                 /* options */,
473                 CtsEnforcement::kApiLevel_T) {
474     using Enable = GrContextOptions::Enable;
475     using MipmapMode = GrSamplerState::MipmapMode;
476 
477     for (auto enableSortingAndReduction : {Enable::kYes, Enable::kNo}) {
478         GrMockOptions mockOptions;
479         mockOptions.fMipmapSupport = true;
480         GrContextOptions ctxOptions;
481         ctxOptions.fReduceOpsTaskSplitting = enableSortingAndReduction;
482         sk_sp<GrDirectContext> dContext = GrDirectContext::MakeMock(&mockOptions, ctxOptions);
483         GrDrawingManager* drawingManager = dContext->priv().drawingManager();
484         if (!dContext) {
485             ERRORF(reporter, "could not create mock dContext with fReduceOpsTaskSplitting %s.",
486                    (Enable::kYes == enableSortingAndReduction) ? "enabled" : "disabled");
487             continue;
488         }
489 
490         SkASSERT(dContext->priv().caps()->mipmapSupport());
491 
492         GrBackendFormat format = dContext->defaultBackendFormat(
493                 kRGBA_8888_SkColorType, GrRenderable::kYes);
494         GrColorType colorType = GrColorType::kRGBA_8888;
495         SkAlphaType alphaType = kPremul_SkAlphaType;
496 
497         GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
498 
499         // Create a mipmapped render target.
500 
501         sk_sp<GrTextureProxy> mipmapProxy =
502                 proxyProvider->createProxy(format,
503                                            {4, 4},
504                                            GrRenderable::kYes,
505                                            1,
506                                            skgpu::Mipmapped::kYes,
507                                            SkBackingFit::kExact,
508                                            skgpu::Budgeted::kYes,
509                                            GrProtected::kNo,
510                                            /*label=*/"ManyDependentsMipMappedTest");
511 
512         // Mark the mipmaps clean to ensure things still work properly when they won't be marked
513         // dirty again until GrRenderTask::makeClosed().
514         mipmapProxy->markMipmapsClean();
515 
516         auto mipmapSDC = skgpu::ganesh::SurfaceDrawContext::Make(dContext.get(),
517                                                                  colorType,
518                                                                  mipmapProxy,
519                                                                  nullptr,
520                                                                  kTopLeft_GrSurfaceOrigin,
521                                                                  SkSurfaceProps());
522 
523         mipmapSDC->clear(SkPMColor4f{.1f, .2f, .3f, .4f});
524         REPORTER_ASSERT(reporter, drawingManager->getLastRenderTask(mipmapProxy.get()));
525         // mipmapProxy's last render task should now just be the opsTask containing the clear.
526         REPORTER_ASSERT(reporter,
527                 mipmapSDC->testingOnly_PeekLastOpsTask() ==
528                         drawingManager->getLastRenderTask(mipmapProxy.get()));
529 
530         // Mipmaps don't get marked dirty until makeClosed().
531         REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
532 
533         skgpu::Swizzle swizzle = dContext->priv().caps()->getReadSwizzle(format, colorType);
534         GrSurfaceProxyView mipmapView(mipmapProxy, kTopLeft_GrSurfaceOrigin, swizzle);
535 
536         // Draw the dirty mipmap texture into a render target.
537         auto sdc1 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
538                                                        mipmapView, MipmapMode::kLinear);
539         auto sdc1Task = sk_ref_sp(sdc1->testingOnly_PeekLastOpsTask());
540 
541         // Mipmaps should have gotten marked dirty during makeClosed, then marked clean again as
542         // soon as a GrTextureResolveRenderTask was inserted. The way we know they were resolved is
543         // if mipmapProxy->getLastRenderTask() has switched from the opsTask that drew to it, to the
544         // task that resolved its mips.
545         GrRenderTask* initialMipmapRegenTask = drawingManager->getLastRenderTask(mipmapProxy.get());
546         REPORTER_ASSERT(reporter, initialMipmapRegenTask);
547         REPORTER_ASSERT(reporter,
548                 initialMipmapRegenTask != mipmapSDC->testingOnly_PeekLastOpsTask());
549         REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
550 
551         // Draw the now-clean mipmap texture into a second target.
552         auto sdc2 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
553                                                        mipmapView, MipmapMode::kLinear);
554         auto sdc2Task = sk_ref_sp(sdc2->testingOnly_PeekLastOpsTask());
555 
556         // Make sure the mipmap texture still has the same regen task.
557         REPORTER_ASSERT(reporter,
558                     drawingManager->getLastRenderTask(mipmapProxy.get()) == initialMipmapRegenTask);
559         SkASSERT(!mipmapProxy->mipmapsAreDirty());
560 
561         // Reset everything so we can go again, this time with the first draw not mipmapped.
562         dContext->flushAndSubmit();
563 
564         // Mip regen tasks don't get added as dependencies until makeClosed().
565         REPORTER_ASSERT(reporter, sdc1Task->dependsOn(initialMipmapRegenTask));
566         REPORTER_ASSERT(reporter, sdc2Task->dependsOn(initialMipmapRegenTask));
567 
568         // Render something to dirty the mips.
569         mipmapSDC->clear(SkPMColor4f{.1f, .2f, .3f, .4f});
570         auto mipmapRTCTask = sk_ref_sp(mipmapSDC->testingOnly_PeekLastOpsTask());
571         REPORTER_ASSERT(reporter, mipmapRTCTask);
572 
573         // mipmapProxy's last render task should now just be the opsTask containing the clear.
574         REPORTER_ASSERT(reporter,
575                     mipmapRTCTask.get() == drawingManager->getLastRenderTask(mipmapProxy.get()));
576 
577         // Mipmaps don't get marked dirty until makeClosed().
578         REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
579 
580         // Draw the dirty mipmap texture into a render target, but don't do mipmap filtering.
581         sdc1 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
582                                                   mipmapView, MipmapMode::kNone);
583 
584         // Mipmaps should have gotten marked dirty during makeClosed() when adding the dependency.
585         // Since the last draw did not use mips, they will not have been regenerated and should
586         // therefore still be dirty.
587         REPORTER_ASSERT(reporter, mipmapProxy->mipmapsAreDirty());
588 
589         // Since mips weren't regenerated, the last render task shouldn't have changed.
590         REPORTER_ASSERT(reporter,
591                     mipmapRTCTask.get() == drawingManager->getLastRenderTask(mipmapProxy.get()));
592 
593         // Draw the stil-dirty mipmap texture into a second target with mipmap filtering.
594         sdc2 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
595                                                   std::move(mipmapView), MipmapMode::kLinear);
596         sdc2Task = sk_ref_sp(sdc2->testingOnly_PeekLastOpsTask());
597 
598         // Make sure the mipmap texture now has a new last render task that regenerates the mips,
599         // and that the mipmaps are now clean.
600         auto mipRegenTask2 = drawingManager->getLastRenderTask(mipmapProxy.get());
601         REPORTER_ASSERT(reporter, mipRegenTask2);
602         REPORTER_ASSERT(reporter, mipmapRTCTask.get() != mipRegenTask2);
603         SkASSERT(!mipmapProxy->mipmapsAreDirty());
604 
605         // Mip regen tasks don't get added as dependencies until makeClosed().
606         dContext->flushAndSubmit();
607         REPORTER_ASSERT(reporter, sdc2Task->dependsOn(mipRegenTask2));
608     }
609 }
610