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
39static 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
43DEF_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.
124DEF_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.
285DEF_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.
338DEF_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'.
371static 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.
414DEF_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