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/SkImageGenerator.h"
17 #include "include/core/SkImageInfo.h"
18 #include "include/core/SkMatrix.h"
19 #include "include/core/SkRect.h"
20 #include "include/core/SkRefCnt.h"
21 #include "include/core/SkSamplingOptions.h"
22 #include "include/core/SkSurface.h"
23 #include "include/core/SkSurfaceProps.h"
24 #include "include/core/SkTypes.h"
25 #include "include/gpu/GpuTypes.h"
26 #include "include/gpu/GrBackendSurface.h"
27 #include "include/gpu/GrContextOptions.h"
28 #include "include/gpu/GrDirectContext.h"
29 #include "include/gpu/GrRecordingContext.h"
30 #include "include/gpu/GrTypes.h"
31 #include "include/gpu/mock/GrMockTypes.h"
32 #include "include/private/SkColorData.h"
33 #include "include/private/gpu/ganesh/GrTypesPriv.h"
34 #include "src/gpu/SkBackingFit.h"
35 #include "src/gpu/Swizzle.h"
36 #include "src/gpu/ganesh/Device_v1.h"
37 #include "src/gpu/ganesh/GrBackendTextureImageGenerator.h"
38 #include "src/gpu/ganesh/GrCaps.h"
39 #include "src/gpu/ganesh/GrColorSpaceXform.h"
40 #include "src/gpu/ganesh/GrDirectContextPriv.h"
41 #include "src/gpu/ganesh/GrDrawingManager.h"
42 #include "src/gpu/ganesh/GrProxyProvider.h"
43 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
44 #include "src/gpu/ganesh/GrSamplerState.h"
45 #include "src/gpu/ganesh/GrSemaphore.h"
46 #include "src/gpu/ganesh/GrSurfaceProxy.h"
47 #include "src/gpu/ganesh/GrSurfaceProxyPriv.h"
48 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
49 #include "src/gpu/ganesh/GrTexture.h"
50 #include "src/gpu/ganesh/GrTextureProxy.h"
51 #include "src/gpu/ganesh/SkGr.h"
52 #include "src/gpu/ganesh/SurfaceDrawContext.h"
53 #include "src/gpu/ganesh/ops/OpsTask.h"
54 #include "src/image/SkSurface_Gpu.h"
55 #include "tests/CtsEnforcement.h"
56 #include "tests/Test.h"
57 #include "tools/gpu/BackendSurfaceFactory.h"
58 #include "tools/gpu/BackendTextureImageFactory.h"
59 #include "tools/gpu/ManagedBackendTexture.h"
60 #include "tools/gpu/ProxyUtils.h"
61
62 #include <initializer_list>
63 #include <memory>
64 #include <utility>
65
66 class GrRenderTask;
67
68 #if defined(SK_DIRECT3D)
69 #include "include/gpu/d3d/GrD3DTypes.h"
70 #endif
71
72 #if defined(SK_GL)
73 #include "include/gpu/gl/GrGLTypes.h"
74 #endif
75
76 #if defined(SK_VULKAN)
77 #include "include/gpu/vk/GrVkTypes.h"
78 #endif
79
80 #if defined(SK_DAWN)
81 #include "include/gpu/dawn/GrDawnTypes.h"
82 #include "dawn/webgpu_cpp.h"
83 #endif
84
85 static constexpr int kSize = 8;
86
87 // Test that the correct mip map states are on the GrTextures when wrapping GrBackendTextures in
88 // SkImages and SkSurfaces
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrWrappedMipMappedTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)89 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrWrappedMipMappedTest,
90 reporter,
91 ctxInfo,
92 CtsEnforcement::kApiLevel_T) {
93 auto dContext = ctxInfo.directContext();
94 if (!dContext->priv().caps()->mipmapSupport()) {
95 return;
96 }
97
98 for (auto mipmapped : {GrMipmapped::kNo, GrMipmapped::kYes}) {
99 for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
100 // createBackendTexture currently doesn't support uploading data to mip maps
101 // so we don't send any. However, we pretend there is data for the checks below which is
102 // fine since we are never actually using these textures for any work on the gpu.
103 auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithData(dContext,
104 kSize,
105 kSize,
106 kRGBA_8888_SkColorType,
107 SkColors::kTransparent,
108 mipmapped,
109 renderable,
110 GrProtected::kNo);
111 if (!mbet) {
112 ERRORF(reporter, "Could not make texture.");
113 return;
114 }
115
116 sk_sp<GrTextureProxy> proxy;
117 sk_sp<SkImage> image;
118 if (renderable == GrRenderable::kYes) {
119 sk_sp<SkSurface> surface = SkSurface::MakeFromBackendTexture(
120 dContext,
121 mbet->texture(),
122 kTopLeft_GrSurfaceOrigin,
123 0,
124 kRGBA_8888_SkColorType,
125 /*color space*/ nullptr,
126 /*surface props*/ nullptr,
127 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
128 mbet->releaseContext());
129
130 auto device = ((SkSurface_Gpu*)surface.get())->getDevice();
131 proxy = device->readSurfaceView().asTextureProxyRef();
132 } else {
133 image = SkImage::MakeFromTexture(dContext,
134 mbet->texture(),
135 kTopLeft_GrSurfaceOrigin,
136 kRGBA_8888_SkColorType,
137 kPremul_SkAlphaType,
138 /* color space */ nullptr,
139 sk_gpu_test::ManagedBackendTexture::ReleaseProc,
140 mbet->releaseContext());
141 REPORTER_ASSERT(reporter, (mipmapped == GrMipmapped::kYes) == image->hasMipmaps());
142 proxy = sk_ref_sp(sk_gpu_test::GetTextureImageProxy(image.get(), dContext));
143 }
144 REPORTER_ASSERT(reporter, proxy);
145 if (!proxy) {
146 continue;
147 }
148
149 REPORTER_ASSERT(reporter, proxy->isInstantiated());
150
151 GrTexture* texture = proxy->peekTexture();
152 REPORTER_ASSERT(reporter, texture);
153 if (!texture) {
154 continue;
155 }
156
157 if (mipmapped == GrMipmapped::kYes) {
158 REPORTER_ASSERT(reporter, GrMipmapped::kYes == texture->mipmapped());
159 if (GrRenderable::kYes == renderable) {
160 REPORTER_ASSERT(reporter, texture->mipmapsAreDirty());
161 } else {
162 REPORTER_ASSERT(reporter, !texture->mipmapsAreDirty());
163 }
164 } else {
165 REPORTER_ASSERT(reporter, GrMipmapped::kNo == texture->mipmapped());
166 }
167 }
168 }
169 }
170
171 // Test that we correctly copy or don't copy GrBackendTextures in the GrBackendTextureImageGenerator
172 // 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)173 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrBackendTextureImageMipMappedTest,
174 reporter,
175 ctxInfo,
176 CtsEnforcement::kApiLevel_T) {
177 auto dContext = ctxInfo.directContext();
178 if (!dContext->priv().caps()->mipmapSupport()) {
179 return;
180 }
181
182 for (auto betMipmapped : {GrMipmapped::kNo, GrMipmapped::kYes}) {
183 for (auto requestMipmapped : {GrMipmapped::kNo, GrMipmapped::kYes}) {
184 auto ii =
185 SkImageInfo::Make({kSize, kSize}, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
186 sk_sp<SkImage> image = sk_gpu_test::MakeBackendTextureImage(
187 dContext, ii, SkColors::kTransparent, betMipmapped);
188 REPORTER_ASSERT(reporter, (betMipmapped == GrMipmapped::kYes) == image->hasMipmaps());
189
190 GrTextureProxy* proxy = sk_gpu_test::GetTextureImageProxy(image.get(), dContext);
191 REPORTER_ASSERT(reporter, proxy);
192 if (!proxy) {
193 return;
194 }
195
196 REPORTER_ASSERT(reporter, proxy->isInstantiated());
197
198 sk_sp<GrTexture> texture = sk_ref_sp(proxy->peekTexture());
199 REPORTER_ASSERT(reporter, texture);
200 if (!texture) {
201 return;
202 }
203
204 std::unique_ptr<SkImageGenerator> imageGen = GrBackendTextureImageGenerator::Make(
205 texture, kTopLeft_GrSurfaceOrigin, nullptr, kRGBA_8888_SkColorType,
206 kPremul_SkAlphaType, nullptr);
207 REPORTER_ASSERT(reporter, imageGen);
208 if (!imageGen) {
209 return;
210 }
211
212 SkImageInfo imageInfo = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
213 kPremul_SkAlphaType);
214 GrSurfaceProxyView genView = imageGen->generateTexture(
215 dContext, imageInfo, requestMipmapped, GrImageTexGenPolicy::kDraw);
216 GrSurfaceProxy* genProxy = genView.proxy();
217
218 REPORTER_ASSERT(reporter, genProxy);
219 if (!genProxy) {
220 return;
221 }
222
223 if (genProxy->isLazy()) {
224 genProxy->priv().doLazyInstantiation(dContext->priv().resourceProvider());
225 } else if (!genProxy->isInstantiated()) {
226 genProxy->instantiate(dContext->priv().resourceProvider());
227 }
228
229 REPORTER_ASSERT(reporter, genProxy->isInstantiated());
230 if (!genProxy->isInstantiated()) {
231 return;
232 }
233
234 GrTexture* genTexture = genProxy->peekTexture();
235 REPORTER_ASSERT(reporter, genTexture);
236 if (!genTexture) {
237 return;
238 }
239
240 GrBackendTexture backendTex = texture->getBackendTexture();
241 GrBackendTexture genBackendTex = genTexture->getBackendTexture();
242
243 if (GrBackendApi::kOpenGL == genBackendTex.backend()) {
244 #ifdef SK_GL
245 GrGLTextureInfo genTexInfo;
246 GrGLTextureInfo origTexInfo;
247 if (genBackendTex.getGLTextureInfo(&genTexInfo) &&
248 backendTex.getGLTextureInfo(&origTexInfo)) {
249 if (requestMipmapped == GrMipmapped::kYes && betMipmapped == GrMipmapped::kNo) {
250 // We did a copy so the texture IDs should be different
251 REPORTER_ASSERT(reporter, origTexInfo.fID != genTexInfo.fID);
252 } else {
253 REPORTER_ASSERT(reporter, origTexInfo.fID == genTexInfo.fID);
254 }
255 } else {
256 ERRORF(reporter, "Failed to get GrGLTextureInfo");
257 }
258 #endif
259 #ifdef SK_VULKAN
260 } else if (GrBackendApi::kVulkan == genBackendTex.backend()) {
261 GrVkImageInfo genImageInfo;
262 GrVkImageInfo origImageInfo;
263 if (genBackendTex.getVkImageInfo(&genImageInfo) &&
264 backendTex.getVkImageInfo(&origImageInfo)) {
265 if (requestMipmapped == GrMipmapped::kYes && betMipmapped == GrMipmapped::kNo) {
266 // We did a copy so the texture IDs should be different
267 REPORTER_ASSERT(reporter, origImageInfo.fImage != genImageInfo.fImage);
268 } else {
269 REPORTER_ASSERT(reporter, origImageInfo.fImage == genImageInfo.fImage);
270 }
271 } else {
272 ERRORF(reporter, "Failed to get GrVkImageInfo");
273 }
274 #endif
275 #ifdef SK_METAL
276 } else if (GrBackendApi::kMetal == genBackendTex.backend()) {
277 GrMtlTextureInfo genImageInfo;
278 GrMtlTextureInfo origImageInfo;
279 if (genBackendTex.getMtlTextureInfo(&genImageInfo) &&
280 backendTex.getMtlTextureInfo(&origImageInfo)) {
281 if (requestMipmapped == GrMipmapped::kYes && betMipmapped == GrMipmapped::kNo) {
282 // We did a copy so the texture IDs should be different
283 REPORTER_ASSERT(reporter, origImageInfo.fTexture != genImageInfo.fTexture);
284 } else {
285 REPORTER_ASSERT(reporter, origImageInfo.fTexture == genImageInfo.fTexture);
286 }
287 } else {
288 ERRORF(reporter, "Failed to get GrMtlTextureInfo");
289 }
290 #endif
291 #ifdef SK_DIRECT3D
292 } else if (GrBackendApi::kDirect3D == genBackendTex.backend()) {
293 GrD3DTextureResourceInfo genImageInfo;
294 GrD3DTextureResourceInfo origImageInfo;
295 if (genBackendTex.getD3DTextureResourceInfo(&genImageInfo) &&
296 backendTex.getD3DTextureResourceInfo(&origImageInfo)) {
297 if (requestMipmapped == GrMipmapped::kYes && betMipmapped == GrMipmapped::kNo) {
298 // We did a copy so the texture resources should be different
299 REPORTER_ASSERT(reporter,
300 origImageInfo.fResource != genImageInfo.fResource);
301 } else {
302 REPORTER_ASSERT(reporter,
303 origImageInfo.fResource == genImageInfo.fResource);
304 }
305 } else {
306 ERRORF(reporter, "Failed to get GrMtlTextureInfo");
307 }
308 #endif
309 #ifdef SK_DAWN
310 } else if (GrBackendApi::kDawn == genBackendTex.backend()) {
311 GrDawnTextureInfo genImageInfo;
312 GrDawnTextureInfo origImageInfo;
313 if (genBackendTex.getDawnTextureInfo(&genImageInfo) &&
314 backendTex.getDawnTextureInfo(&origImageInfo)) {
315 if (requestMipmapped == GrMipmapped::kYes && betMipmapped == GrMipmapped::kNo) {
316 // We did a copy so the texture IDs should be different
317 REPORTER_ASSERT(reporter,
318 origImageInfo.fTexture.Get() != genImageInfo.fTexture.Get());
319 } else {
320 REPORTER_ASSERT(reporter,
321 origImageInfo.fTexture.Get() == genImageInfo.fTexture.Get());
322 }
323 } else {
324 ERRORF(reporter, "Failed to get GrDawnTextureInfo");
325 }
326 #endif
327 } else {
328 REPORTER_ASSERT(reporter, false);
329 }
330 }
331 }
332 }
333
334 // Test that when we call makeImageSnapshot on an SkSurface we retains the same mip status as the
335 // resource we took the snapshot of.
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrImageSnapshotMipMappedTest,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)336 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(GrImageSnapshotMipMappedTest,
337 reporter,
338 ctxInfo,
339 CtsEnforcement::kApiLevel_T) {
340 auto dContext = ctxInfo.directContext();
341 if (!dContext->priv().caps()->mipmapSupport()) {
342 return;
343 }
344
345 auto resourceProvider = dContext->priv().resourceProvider();
346
347 for (auto willUseMips : {false, true}) {
348 for (auto isWrapped : {false, true}) {
349 GrMipmapped mipmapped = willUseMips ? GrMipmapped::kYes : GrMipmapped::kNo;
350 sk_sp<SkSurface> surface;
351 SkImageInfo info =
352 SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
353 if (isWrapped) {
354 surface = sk_gpu_test::MakeBackendTextureSurface(dContext,
355 info,
356 kTopLeft_GrSurfaceOrigin,
357 /* sample count */ 1,
358 mipmapped);
359 } else {
360 surface = SkSurface::MakeRenderTarget(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_Gpu*)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 = SkSurface::MakeRenderTarget(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 surface->flushAndSubmit();
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 surface->flushAndSubmit();
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::v1::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 GrMipmapped::kNo,
441 SkBackingFit::kApprox,
442 skgpu::Budgeted::kYes,
443 GrProtected::kNo,
444 /*label=*/"DrawMipMapViewTest");
445
446 auto sdc = skgpu::v1::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 GrMipmapped::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::v1::SurfaceDrawContext::Make(
517 dContext.get(), colorType, mipmapProxy, nullptr, kTopLeft_GrSurfaceOrigin,
518 SkSurfaceProps());
519
520 mipmapSDC->clear(SkPMColor4f{.1f, .2f, .3f, .4f});
521 REPORTER_ASSERT(reporter, drawingManager->getLastRenderTask(mipmapProxy.get()));
522 // mipmapProxy's last render task should now just be the opsTask containing the clear.
523 REPORTER_ASSERT(reporter,
524 mipmapSDC->testingOnly_PeekLastOpsTask() ==
525 drawingManager->getLastRenderTask(mipmapProxy.get()));
526
527 // Mipmaps don't get marked dirty until makeClosed().
528 REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
529
530 skgpu::Swizzle swizzle = dContext->priv().caps()->getReadSwizzle(format, colorType);
531 GrSurfaceProxyView mipmapView(mipmapProxy, kTopLeft_GrSurfaceOrigin, swizzle);
532
533 // Draw the dirty mipmap texture into a render target.
534 auto sdc1 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
535 mipmapView, MipmapMode::kLinear);
536 auto sdc1Task = sk_ref_sp(sdc1->testingOnly_PeekLastOpsTask());
537
538 // Mipmaps should have gotten marked dirty during makeClosed, then marked clean again as
539 // soon as a GrTextureResolveRenderTask was inserted. The way we know they were resolved is
540 // if mipmapProxy->getLastRenderTask() has switched from the opsTask that drew to it, to the
541 // task that resolved its mips.
542 GrRenderTask* initialMipmapRegenTask = drawingManager->getLastRenderTask(mipmapProxy.get());
543 REPORTER_ASSERT(reporter, initialMipmapRegenTask);
544 REPORTER_ASSERT(reporter,
545 initialMipmapRegenTask != mipmapSDC->testingOnly_PeekLastOpsTask());
546 REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
547
548 // Draw the now-clean mipmap texture into a second target.
549 auto sdc2 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
550 mipmapView, MipmapMode::kLinear);
551 auto sdc2Task = sk_ref_sp(sdc2->testingOnly_PeekLastOpsTask());
552
553 // Make sure the mipmap texture still has the same regen task.
554 REPORTER_ASSERT(reporter,
555 drawingManager->getLastRenderTask(mipmapProxy.get()) == initialMipmapRegenTask);
556 SkASSERT(!mipmapProxy->mipmapsAreDirty());
557
558 // Reset everything so we can go again, this time with the first draw not mipmapped.
559 dContext->flushAndSubmit();
560
561 // Mip regen tasks don't get added as dependencies until makeClosed().
562 REPORTER_ASSERT(reporter, sdc1Task->dependsOn(initialMipmapRegenTask));
563 REPORTER_ASSERT(reporter, sdc2Task->dependsOn(initialMipmapRegenTask));
564
565 // Render something to dirty the mips.
566 mipmapSDC->clear(SkPMColor4f{.1f, .2f, .3f, .4f});
567 auto mipmapRTCTask = sk_ref_sp(mipmapSDC->testingOnly_PeekLastOpsTask());
568 REPORTER_ASSERT(reporter, mipmapRTCTask);
569
570 // mipmapProxy's last render task should now just be the opsTask containing the clear.
571 REPORTER_ASSERT(reporter,
572 mipmapRTCTask.get() == drawingManager->getLastRenderTask(mipmapProxy.get()));
573
574 // Mipmaps don't get marked dirty until makeClosed().
575 REPORTER_ASSERT(reporter, !mipmapProxy->mipmapsAreDirty());
576
577 // Draw the dirty mipmap texture into a render target, but don't do mipmap filtering.
578 sdc1 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
579 mipmapView, MipmapMode::kNone);
580
581 // Mipmaps should have gotten marked dirty during makeClosed() when adding the dependency.
582 // Since the last draw did not use mips, they will not have been regenerated and should
583 // therefore still be dirty.
584 REPORTER_ASSERT(reporter, mipmapProxy->mipmapsAreDirty());
585
586 // Since mips weren't regenerated, the last render task shouldn't have changed.
587 REPORTER_ASSERT(reporter,
588 mipmapRTCTask.get() == drawingManager->getLastRenderTask(mipmapProxy.get()));
589
590 // Draw the stil-dirty mipmap texture into a second target with mipmap filtering.
591 sdc2 = draw_mipmap_into_new_render_target(dContext.get(), colorType, alphaType,
592 std::move(mipmapView), MipmapMode::kLinear);
593 sdc2Task = sk_ref_sp(sdc2->testingOnly_PeekLastOpsTask());
594
595 // Make sure the mipmap texture now has a new last render task that regenerates the mips,
596 // and that the mipmaps are now clean.
597 auto mipRegenTask2 = drawingManager->getLastRenderTask(mipmapProxy.get());
598 REPORTER_ASSERT(reporter, mipRegenTask2);
599 REPORTER_ASSERT(reporter, mipmapRTCTask.get() != mipRegenTask2);
600 SkASSERT(!mipmapProxy->mipmapsAreDirty());
601
602 // Mip regen tasks don't get added as dependencies until makeClosed().
603 dContext->flushAndSubmit();
604 REPORTER_ASSERT(reporter, sdc2Task->dependsOn(mipRegenTask2));
605 }
606 }
607