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