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