• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 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 <set>
9 #include "include/core/SkSurface.h"
10 #include "include/gpu/GrContext.h"
11 #include "include/gpu/GrTexture.h"
12 #include "src/core/SkAutoPixmapStorage.h"
13 #include "src/core/SkMipMap.h"
14 #include "src/gpu/GrClip.h"
15 #include "src/gpu/GrContextPriv.h"
16 #include "src/gpu/GrDataUtils.h"
17 #include "src/gpu/GrGpu.h"
18 #include "src/gpu/GrProxyProvider.h"
19 #include "src/gpu/GrRenderTarget.h"
20 #include "src/gpu/GrResourceProvider.h"
21 #include "src/gpu/GrTexturePriv.h"
22 #include "tests/Test.h"
23 #include "tests/TestUtils.h"
24 
25 // Tests that GrSurface::asTexture(), GrSurface::asRenderTarget(), and static upcasting of texture
26 // and render targets to GrSurface all work as expected.
DEF_GPUTEST_FOR_MOCK_CONTEXT(GrSurface,reporter,ctxInfo)27 DEF_GPUTEST_FOR_MOCK_CONTEXT(GrSurface, reporter, ctxInfo) {
28     GrContext* context = ctxInfo.grContext();
29     auto resourceProvider = context->priv().resourceProvider();
30 
31     GrSurfaceDesc desc;
32     desc.fWidth = 256;
33     desc.fHeight = 256;
34     desc.fConfig = kRGBA_8888_GrPixelConfig;
35     auto format = context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
36                                                                   GrRenderable::kYes);
37     sk_sp<GrSurface> texRT1 = resourceProvider->createTexture(
38             desc, format, GrRenderable::kYes, 1, SkBudgeted::kNo, GrProtected::kNo,
39             GrResourceProvider::Flags::kNoPendingIO);
40 
41     REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asRenderTarget());
42     REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asTexture());
43     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) ==
44                     texRT1->asTexture());
45     REPORTER_ASSERT(reporter, texRT1->asRenderTarget() ==
46                     static_cast<GrSurface*>(texRT1->asTexture()));
47     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) ==
48                     static_cast<GrSurface*>(texRT1->asTexture()));
49 
50     sk_sp<GrTexture> tex1 = resourceProvider->createTexture(
51             desc, format, GrRenderable::kNo, 1, SkBudgeted::kNo, GrProtected::kNo,
52             GrResourceProvider::Flags::kNoPendingIO);
53     REPORTER_ASSERT(reporter, nullptr == tex1->asRenderTarget());
54     REPORTER_ASSERT(reporter, tex1.get() == tex1->asTexture());
55     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1.get()) == tex1->asTexture());
56 
57     GrBackendTexture backendTex = context->createBackendTexture(
58         256, 256, kRGBA_8888_SkColorType,
59         SkColors::kTransparent, GrMipMapped::kNo, GrRenderable::kNo, GrProtected::kNo);
60 
61     sk_sp<GrSurface> texRT2 = resourceProvider->wrapRenderableBackendTexture(
62             backendTex, 1, GrColorType::kRGBA_8888, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo);
63 
64     REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asRenderTarget());
65     REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asTexture());
66     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
67                     texRT2->asTexture());
68     REPORTER_ASSERT(reporter, texRT2->asRenderTarget() ==
69                     static_cast<GrSurface*>(texRT2->asTexture()));
70     REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
71                     static_cast<GrSurface*>(texRT2->asTexture()));
72 
73     context->deleteBackendTexture(backendTex);
74 }
75 
76 // This test checks that the isFormatTexturable and isFormatRenderable are
77 // consistent with createTexture's result.
DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSurfaceRenderability,reporter,ctxInfo)78 DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSurfaceRenderability, reporter, ctxInfo) {
79     GrContext* context = ctxInfo.grContext();
80     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
81     GrResourceProvider* resourceProvider = context->priv().resourceProvider();
82     const GrCaps* caps = context->priv().caps();
83 
84     // TODO: Should only need format here but need to determine compression type from format
85     // without config.
86     auto createTexture = [](int width, int height, GrColorType colorType,
87                             const GrBackendFormat& format, GrRenderable renderable,
88                             GrResourceProvider* rp) -> sk_sp<GrTexture> {
89         GrPixelConfig config = rp->caps()->getConfigFromBackendFormat(format, colorType);
90         bool compressed = rp->caps()->isFormatCompressed(format);
91         if (compressed) {
92             if (renderable == GrRenderable::kYes) {
93                 return nullptr;
94             }
95             SkImage::CompressionType type;
96             switch (config) {
97                 case kRGB_ETC1_GrPixelConfig:
98                     type = SkImage::kETC1_CompressionType;
99                     break;
100                 default:
101                     SK_ABORT("Unexpected config");
102             }
103             // Only supported compression type right now.
104             SkASSERT(config == kRGB_ETC1_GrPixelConfig);
105             auto size = GrCompressedDataSize(type, width, height);
106             auto data = SkData::MakeUninitialized(size);
107             SkColor4f color = {0, 0, 0, 0};
108             GrFillInCompressedData(type, width, height, (char*)data->writable_data(), color);
109             return rp->createCompressedTexture(width, height, format,
110                                                SkImage::kETC1_CompressionType,
111                                                SkBudgeted::kNo, data.get());
112         } else {
113             GrSurfaceDesc desc;
114             desc.fWidth = width;
115             desc.fHeight = height;
116             desc.fConfig = config;
117             return rp->createTexture(desc, format, renderable, 1, SkBudgeted::kNo, GrProtected::kNo,
118                                      GrResourceProvider::Flags::kNone);
119         }
120     };
121 
122     static constexpr int kW = 64;
123     static constexpr int kH = 64;
124 
125     const std::vector<GrCaps::TestFormatColorTypeCombination>& combos =
126                                                                     caps->getTestingCombinations();
127 
128     for (auto combo : combos) {
129 
130         SkASSERT(combo.fColorType != GrColorType::kUnknown);
131         SkASSERT(combo.fFormat.isValid());
132 
133         // Right now Vulkan has two backend formats that support ABGR_4444 (R4G4B4A4 and B4G4R4A4).
134         // Until we can create textures directly from the backend format this yields some
135         // ambiguity in what is actually supported and which textures can be created.
136         if (ctxInfo.backend() == kVulkan_GrBackend && combo.fColorType == GrColorType::kABGR_4444) {
137             continue;
138         }
139 
140         for (GrSurfaceOrigin origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
141             GrSurfaceDesc desc;
142             desc.fWidth = kW;
143             desc.fHeight = kH;
144             desc.fConfig = caps->getConfigFromBackendFormat(combo.fFormat, combo.fColorType);
145             SkASSERT(desc.fConfig != kUnknown_GrPixelConfig);
146 
147             // Check if 'isFormatTexturable' agrees with 'createTexture' and that the mipmap
148             // support check is working
149             {
150 
151                 bool compressed = caps->isFormatCompressed(combo.fFormat);
152                 bool isTexturable;
153                 if (compressed) {
154                     isTexturable = caps->isFormatTexturable(combo.fFormat);
155                 } else {
156                     isTexturable = caps->isFormatTexturableAndUploadable(combo.fColorType,
157                                                                          combo.fFormat);
158                 }
159 
160                 sk_sp<GrSurface> tex = createTexture(kW, kH, combo.fColorType, combo.fFormat,
161                                                      GrRenderable::kNo, resourceProvider);
162                 REPORTER_ASSERT(reporter, SkToBool(tex) == isTexturable,
163                                 "ct:%s format:%s, tex:%d, isTexturable:%d",
164                                 GrColorTypeToStr(combo.fColorType),
165                                 combo.fFormat.toStr().c_str(),
166                                 SkToBool(tex), isTexturable);
167 
168                 // Check that the lack of mipmap support blocks the creation of mipmapped
169                 // proxies
170                 bool expectedMipMapability = isTexturable && caps->mipMapSupport() &&
171                                               !caps->isFormatCompressed(combo.fFormat);
172 
173                 sk_sp<GrTextureProxy> proxy = proxyProvider->createMipMapProxy(
174                             combo.fFormat, desc, GrRenderable::kNo, 1, origin,
175                             SkBudgeted::kNo, GrProtected::kNo);
176                 REPORTER_ASSERT(reporter, SkToBool(proxy.get()) == expectedMipMapability,
177                                 "ct:%s format:%s, tex:%d, expectedMipMapability:%d",
178                                 GrColorTypeToStr(combo.fColorType),
179                                 combo.fFormat.toStr().c_str(),
180                                 SkToBool(proxy.get()), expectedMipMapability);
181             }
182 
183             // Check if 'isFormatAsColorTypeRenderable' agrees with 'createTexture' (w/o MSAA)
184             {
185                 bool isRenderable = caps->isFormatRenderable(combo.fFormat, 1);
186 
187                 sk_sp<GrSurface> tex = resourceProvider->createTexture(
188                         desc, combo.fFormat, GrRenderable::kYes, 1, SkBudgeted::kNo,
189                         GrProtected::kNo, GrResourceProvider::Flags::kNone);
190                 REPORTER_ASSERT(reporter, SkToBool(tex) == isRenderable,
191                                 "ct:%s format:%s, tex:%d, isRenderable:%d",
192                                 GrColorTypeToStr(combo.fColorType),
193                                 combo.fFormat.toStr().c_str(),
194                                 SkToBool(tex), isRenderable);
195             }
196 
197             // Check if 'isFormatAsColorTypeRenderable' agrees with 'createTexture' w/ MSAA
198             {
199                 bool isRenderable = caps->isFormatRenderable(combo.fFormat, 2);
200 
201                 sk_sp<GrSurface> tex = resourceProvider->createTexture(
202                         desc, combo.fFormat, GrRenderable::kYes, 2, SkBudgeted::kNo,
203                         GrProtected::kNo, GrResourceProvider::Flags::kNone);
204                 REPORTER_ASSERT(reporter, SkToBool(tex) == isRenderable,
205                                 "ct:%s format:%s, tex:%d, isRenderable:%d",
206                                 GrColorTypeToStr(combo.fColorType),
207                                 combo.fFormat.toStr().c_str(),
208                                 SkToBool(tex), isRenderable);
209             }
210         }
211     }
212 }
213 
214 #include "src/gpu/GrDrawingManager.h"
215 #include "src/gpu/GrSurfaceProxy.h"
216 #include "src/gpu/GrTextureContext.h"
217 
218 // For each context, set it to always clear the textures and then run through all the
219 // supported formats checking that the textures are actually cleared
DEF_GPUTEST(InitialTextureClear,reporter,baseOptions)220 DEF_GPUTEST(InitialTextureClear, reporter, baseOptions) {
221     GrContextOptions options = baseOptions;
222     options.fClearAllTextures = true;
223 
224     static constexpr int kSize = 100;
225     static constexpr SkColor kClearColor = 0xABABABAB;
226 
227     const SkImageInfo info = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType,
228                                                kPremul_SkAlphaType);
229 
230     SkAutoPixmapStorage readback;
231     readback.alloc(info);
232 
233     GrSurfaceDesc desc;
234     desc.fWidth = desc.fHeight = kSize;
235 
236     for (int ct = 0; ct < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++ct) {
237         sk_gpu_test::GrContextFactory factory(options);
238         auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(ct);
239         if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) {
240             continue;
241         }
242         auto context = factory.get(contextType);
243         if (!context) {
244             continue;
245         }
246 
247         GrProxyProvider* proxyProvider = context->priv().proxyProvider();
248         const GrCaps* caps = context->priv().caps();
249 
250         const std::vector<GrCaps::TestFormatColorTypeCombination>& combos =
251                                                                     caps->getTestingCombinations();
252 
253         for (auto combo : combos) {
254 
255             SkASSERT(combo.fColorType != GrColorType::kUnknown);
256             SkASSERT(combo.fFormat.isValid());
257 
258             if (!caps->isFormatTexturableAndUploadable(combo.fColorType, combo.fFormat)) {
259                 continue;
260             }
261 
262             {
263                 GrPixelConfig config = caps->getConfigFromBackendFormat(combo.fFormat,
264                                                                         combo.fColorType);
265                 SkASSERT(config != kUnknown_GrPixelConfig);
266 
267                 desc.fConfig = config;
268             }
269 
270             uint32_t components = GrColorTypeComponentFlags(combo.fColorType);
271             uint32_t expectedClearColor =
272                     (components & kAlpha_SkColorTypeComponentFlag) ? 0 : 0xFF000000;
273 
274             for (auto renderable : {GrRenderable::kNo, GrRenderable::kYes}) {
275                 if (renderable == GrRenderable::kYes &&
276                     !caps->isFormatAsColorTypeRenderable(combo.fColorType, combo.fFormat)) {
277                     continue;
278                 }
279 
280                 for (auto fit : {SkBackingFit::kApprox, SkBackingFit::kExact}) {
281 
282                     // Does directly allocating a texture clear it?
283                     {
284                         auto proxy = proxyProvider->testingOnly_createInstantiatedProxy(
285                                 {kSize, kSize}, combo.fColorType, combo.fFormat, renderable, 1,
286                                 kTopLeft_GrSurfaceOrigin, fit, SkBudgeted::kYes, GrProtected::kNo);
287                         if (proxy) {
288                             auto texCtx = context->priv().makeWrappedSurfaceContext(
289                                     std::move(proxy), combo.fColorType, kPremul_SkAlphaType);
290 
291                             readback.erase(kClearColor);
292                             if (texCtx->readPixels(readback.info(), readback.writable_addr(),
293                                                    readback.rowBytes(), {0, 0})) {
294                                 for (int i = 0; i < kSize * kSize; ++i) {
295                                     if (expectedClearColor != readback.addr32()[i]) {
296                                         ERRORF(reporter,
297                                                "Failed on ct %s format %s 0x%x != 0x%x",
298                                                GrColorTypeToStr(combo.fColorType),
299                                                combo.fFormat.toStr().c_str(),
300                                                expectedClearColor, readback.addr32()[i]);
301                                         break;
302                                     }
303                                 }
304                             }
305                         }
306 
307                         context->priv().testingOnly_purgeAllUnlockedResources();
308                     }
309 
310                     // Try creating the texture as a deferred proxy.
311                     {
312                         sk_sp<GrSurfaceContext> surfCtx;
313                         if (renderable == GrRenderable::kYes) {
314                             surfCtx = context->priv().makeDeferredRenderTargetContext(
315                                     fit, desc.fWidth, desc.fHeight, combo.fColorType, nullptr,
316                                     1, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin, nullptr);
317                         } else {
318                             surfCtx = context->priv().makeDeferredTextureContext(
319                                     fit, desc.fWidth, desc.fHeight, combo.fColorType,
320                                     kUnknown_SkAlphaType, nullptr, GrMipMapped::kNo,
321                                     kTopLeft_GrSurfaceOrigin);
322                         }
323                         if (!surfCtx) {
324                             continue;
325                         }
326 
327                         readback.erase(kClearColor);
328                         if (surfCtx->readPixels(readback.info(), readback.writable_addr(),
329                                                 readback.rowBytes(), {0, 0})) {
330                             for (int i = 0; i < kSize * kSize; ++i) {
331                                 if (expectedClearColor != readback.addr32()[i]) {
332                                     ERRORF(reporter, "Failed on ct %s format %s 0x%x != 0x%x",
333                                            GrColorTypeToStr(combo.fColorType),
334                                            combo.fFormat.toStr().c_str(),
335                                            expectedClearColor, readback.addr32()[i]);
336                                     break;
337                                 }
338                             }
339                         }
340                         context->priv().testingOnly_purgeAllUnlockedResources();
341                     }
342                 }
343             }
344         }
345     }
346 }
347 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadOnlyTexture,reporter,context_info)348 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadOnlyTexture, reporter, context_info) {
349     auto fillPixels = [](SkPixmap* p, const std::function<uint32_t(int x, int y)>& f) {
350         for (int y = 0; y < p->height(); ++y) {
351             for (int x = 0; x < p->width(); ++x) {
352                 *p->writable_addr32(x, y) = f(x, y);
353             }
354         }
355     };
356 
357     auto comparePixels = [](const SkPixmap& p1, const SkPixmap& p2, skiatest::Reporter* reporter) {
358         SkASSERT(p1.info() == p2.info());
359         for (int y = 0; y < p1.height(); ++y) {
360             for (int x = 0; x < p1.width(); ++x) {
361                 REPORTER_ASSERT(reporter, p1.getColor(x, y) == p2.getColor(x, y));
362                 if (p1.getColor(x, y) != p2.getColor(x, y)) {
363                     return;
364                 }
365             }
366         }
367     };
368 
369     static constexpr int kSize = 100;
370     SkImageInfo ii = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
371     SkAutoPixmapStorage srcPixmap;
372     srcPixmap.alloc(ii);
373     fillPixels(&srcPixmap,
374                [](int x, int y) {
375                     return (0xFFU << 24) | (x << 16) | (y << 8) | uint8_t((x * y) & 0xFF);
376                });
377 
378     GrContext* context = context_info.grContext();
379     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
380 
381     // We test both kRW in addition to kRead mostly to ensure that the calls are structured such
382     // that they'd succeed if the texture wasn't kRead. We want to be sure we're failing with
383     // kRead for the right reason.
384     for (auto ioType : {kRead_GrIOType, kRW_GrIOType}) {
385         auto backendTex = context->priv().createBackendTexture(&srcPixmap, 1,
386                                                                GrRenderable::kYes,
387                                                                GrProtected::kNo);
388 
389         auto proxy = proxyProvider->wrapBackendTexture(backendTex, GrColorType::kRGBA_8888,
390                                                        kTopLeft_GrSurfaceOrigin,
391                                                        kBorrow_GrWrapOwnership,
392                                                        GrWrapCacheable::kNo, ioType);
393         auto surfContext = context->priv().makeWrappedSurfaceContext(proxy, GrColorType::kRGBA_8888,
394                                                                      kPremul_SkAlphaType);
395 
396         // Read pixels should work with a read-only texture.
397         {
398             SkAutoPixmapStorage read;
399             read.alloc(srcPixmap.info());
400             auto readResult = surfContext->readPixels(srcPixmap.info(), read.writable_addr(),
401                                                       0, { 0, 0 });
402             REPORTER_ASSERT(reporter, readResult);
403             if (readResult) {
404                 comparePixels(srcPixmap, read, reporter);
405             }
406         }
407 
408         // Write pixels should not work with a read-only texture.
409         SkAutoPixmapStorage write;
410         write.alloc(srcPixmap.info());
411         fillPixels(&write, [&srcPixmap](int x, int y) { return ~*srcPixmap.addr32(); });
412         auto writeResult = surfContext->writePixels(srcPixmap.info(), write.addr(), 0, {0, 0});
413         REPORTER_ASSERT(reporter, writeResult == (ioType == kRW_GrIOType));
414         // Try the low level write.
415         context->flush();
416         auto gpuWriteResult = context->priv().getGpu()->writePixels(
417                 proxy->peekTexture(), 0, 0, kSize, kSize, GrColorType::kRGBA_8888,
418                 GrColorType::kRGBA_8888, write.addr32(),
419                 kSize * GrColorTypeBytesPerPixel(GrColorType::kRGBA_8888));
420         REPORTER_ASSERT(reporter, gpuWriteResult == (ioType == kRW_GrIOType));
421 
422         // Copies should not work with a read-only texture
423         auto copySrc =
424                 proxyProvider->createTextureProxy(SkImage::MakeFromRaster(write, nullptr, nullptr),
425                                                   1, SkBudgeted::kYes, SkBackingFit::kExact);
426         REPORTER_ASSERT(reporter, copySrc);
427         auto copyResult = surfContext->testCopy(copySrc.get());
428         REPORTER_ASSERT(reporter, copyResult == (ioType == kRW_GrIOType));
429         // Try the low level copy.
430         context->flush();
431         auto gpuCopyResult = context->priv().getGpu()->copySurface(
432                 proxy->peekTexture(), copySrc->peekTexture(), SkIRect::MakeWH(kSize, kSize),
433                 {0, 0});
434         REPORTER_ASSERT(reporter, gpuCopyResult == (ioType == kRW_GrIOType));
435 
436         // Mip regen should not work with a read only texture.
437         if (context->priv().caps()->mipMapSupport()) {
438             delete_backend_texture(context, backendTex);
439             backendTex = context->createBackendTexture(
440                     kSize, kSize, kRGBA_8888_SkColorType,
441                     SkColors::kTransparent, GrMipMapped::kYes, GrRenderable::kYes,
442                     GrProtected::kNo);
443             proxy = proxyProvider->wrapBackendTexture(backendTex, GrColorType::kRGBA_8888,
444                                                       kTopLeft_GrSurfaceOrigin,
445                                                       kBorrow_GrWrapOwnership, GrWrapCacheable::kNo,
446                                                       ioType);
447             context->flush();
448             proxy->peekTexture()->texturePriv().markMipMapsDirty();  // avoids assert in GrGpu.
449             auto regenResult =
450                     context->priv().getGpu()->regenerateMipMapLevels(proxy->peekTexture());
451             REPORTER_ASSERT(reporter, regenResult == (ioType == kRW_GrIOType));
452         }
453         delete_backend_texture(context, backendTex);
454     }
455 }
456 
457 static const int kSurfSize = 10;
458 
make_wrapped_texture(GrContext * context,GrRenderable renderable)459 static sk_sp<GrTexture> make_wrapped_texture(GrContext* context, GrRenderable renderable) {
460     auto backendTexture = context->createBackendTexture(
461             kSurfSize, kSurfSize, kRGBA_8888_SkColorType, SkColors::kTransparent, GrMipMapped::kNo,
462             renderable, GrProtected::kNo);
463     sk_sp<GrTexture> texture;
464     if (GrRenderable::kYes == renderable) {
465         texture = context->priv().resourceProvider()->wrapRenderableBackendTexture(
466                 backendTexture, 1, GrColorType::kRGBA_8888, kBorrow_GrWrapOwnership,
467                 GrWrapCacheable::kNo);
468     } else {
469         texture = context->priv().resourceProvider()->wrapBackendTexture(
470                 backendTexture, GrColorType::kRGBA_8888, kBorrow_GrWrapOwnership,
471                 GrWrapCacheable::kNo, kRW_GrIOType);
472     }
473     // Add a release proc that deletes the GrBackendTexture.
474     struct ReleaseContext {
475         GrContext* fContext;
476         GrBackendTexture fBackendTexture;
477     };
478     auto release = [](void* rc) {
479         auto releaseContext = static_cast<ReleaseContext*>(rc);
480         auto context = releaseContext->fContext;
481         context->deleteBackendTexture(releaseContext->fBackendTexture);
482         delete releaseContext;
483     };
484     texture->setRelease(release, new ReleaseContext{context, backendTexture});
485     return texture;
486 }
487 
make_normal_texture(GrContext * context,GrRenderable renderable)488 static sk_sp<GrTexture> make_normal_texture(GrContext* context, GrRenderable renderable) {
489     GrSurfaceDesc desc;
490     desc.fConfig = kRGBA_8888_GrPixelConfig;
491     desc.fWidth = desc.fHeight = kSurfSize;
492     auto format =
493             context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888, renderable);
494     return context->priv().resourceProvider()->createTexture(
495             desc, format, renderable, 1, SkBudgeted::kNo, GrProtected::kNo,
496             GrResourceProvider::Flags::kNoPendingIO);
497 }
498 
DEF_GPUTEST(TextureIdleProcTest,reporter,options)499 DEF_GPUTEST(TextureIdleProcTest, reporter, options) {
500     // Various ways of making textures.
501     auto makeWrapped = [](GrContext* context) {
502         return make_wrapped_texture(context, GrRenderable::kNo);
503     };
504     auto makeWrappedRenderable = [](GrContext* context) {
505         return make_wrapped_texture(context, GrRenderable::kYes);
506     };
507     auto makeNormal = [](GrContext* context) {
508         return make_normal_texture(context, GrRenderable::kNo);
509     };
510     auto makeRenderable = [](GrContext* context) {
511         return make_normal_texture(context, GrRenderable::kYes);
512     };
513 
514     std::function<sk_sp<GrTexture>(GrContext*)> makers[] = {makeWrapped, makeWrappedRenderable,
515                                                             makeNormal, makeRenderable};
516 
517     // Add a unique key, or not.
518     auto addKey = [](GrTexture* texture) {
519         static uint32_t gN = 0;
520         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
521         GrUniqueKey key;
522         GrUniqueKey::Builder builder(&key, kDomain, 1);
523         builder[0] = gN++;
524         builder.finish();
525         texture->resourcePriv().setUniqueKey(key);
526     };
527     auto dontAddKey = [](GrTexture* texture) {};
528     std::function<void(GrTexture*)> keyAdders[] = {addKey, dontAddKey};
529 
530     for (const auto& m : makers) {
531         for (const auto& keyAdder : keyAdders) {
532             for (int type = 0; type < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++type) {
533                 sk_gpu_test::GrContextFactory factory;
534                 auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(type);
535                 GrContext* context = factory.get(contextType);
536                 if (!context) {
537                     continue;
538                 }
539 
540                 // The callback we add simply adds an integer to a set.
541                 std::set<int> idleIDs;
542                 struct Context {
543                     std::set<int>* fIdleIDs;
544                     int fNum;
545                 };
546                 auto proc = [](void* context) {
547                     static_cast<Context*>(context)->fIdleIDs->insert(
548                             static_cast<Context*>(context)->fNum);
549                     delete static_cast<Context*>(context);
550                 };
551 
552                 // Makes a texture, possibly adds a key, and sets the callback.
553                 auto make = [&m, &keyAdder, &proc, &idleIDs](GrContext* context, int num) {
554                     sk_sp<GrTexture> texture = m(context);
555                     texture->addIdleProc(proc, new Context{&idleIDs, num},
556                                          GrTexture::IdleState::kFinished);
557                     keyAdder(texture.get());
558                     return texture;
559                 };
560 
561                 auto texture = make(context, 1);
562                 REPORTER_ASSERT(reporter, idleIDs.find(1) == idleIDs.end());
563                 auto renderable = GrRenderable(SkToBool(texture->asRenderTarget()));
564                 auto backendFormat = texture->backendFormat();
565                 texture.reset();
566                 REPORTER_ASSERT(reporter, idleIDs.find(1) != idleIDs.end());
567 
568                 texture = make(context, 2);
569                 int w = texture->width();
570                 int h = texture->height();
571                 SkImageInfo info =
572                         SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
573                 auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, nullptr);
574                 auto rtc = rt->getCanvas()->internal_private_accessTopLayerRenderTargetContext();
575                 auto singleUseLazyCB = [&texture](GrResourceProvider* rp) {
576                     auto mode = GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
577                     if (texture->getUniqueKey().isValid()) {
578                         mode = GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced;
579                     }
580                     return GrSurfaceProxy::LazyInstantiationResult{std::move(texture), mode};
581                 };
582                 GrSurfaceDesc desc;
583                 desc.fWidth = w;
584                 desc.fHeight = h;
585                 desc.fConfig = kRGBA_8888_GrPixelConfig;
586                 SkBudgeted budgeted;
587                 if (texture->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
588                     budgeted = SkBudgeted::kYes;
589                 } else {
590                     budgeted = SkBudgeted::kNo;
591                 }
592                 auto proxy = context->priv().proxyProvider()->createLazyProxy(
593                         singleUseLazyCB, backendFormat, desc, renderable, 1,
594                         GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
595                         GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags ::kNone,
596                         SkBackingFit::kExact, budgeted, GrProtected::kNo,
597                         GrSurfaceProxy::LazyInstantiationType::kSingleUse);
598                 rtc->drawTexture(GrNoClip(), proxy, GrSamplerState::Filter::kNearest,
599                                  SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
600                                  SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
601                                  SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
602                 // We still have the proxy, which should remain instantiated, thereby keeping the
603                 // texture not purgeable.
604                 REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
605                 context->flush();
606                 REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
607                 context->priv().getGpu()->testingOnly_flushGpuAndSync();
608                 REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
609 
610                 // This time we move the proxy into the draw.
611                 rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
612                                  SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
613                                  SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
614                                  SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
615                 REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
616                 context->flush();
617                 context->priv().getGpu()->testingOnly_flushGpuAndSync();
618                 // Now that the draw is fully consumed by the GPU, the texture should be idle.
619                 REPORTER_ASSERT(reporter, idleIDs.find(2) != idleIDs.end());
620 
621                 // Make a proxy that should deinstantiate even if we keep a ref on it.
622                 auto deinstantiateLazyCB = [&make, &context](GrResourceProvider* rp) {
623                     auto texture = make(context, 3);
624                     auto mode = GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
625                     if (texture->getUniqueKey().isValid()) {
626                         mode = GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced;
627                     }
628                     return GrSurfaceProxy::LazyInstantiationResult{std::move(texture), mode};
629                 };
630                 proxy = context->priv().proxyProvider()->createLazyProxy(
631                         deinstantiateLazyCB, backendFormat, desc, renderable, 1,
632                         GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
633                         GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags ::kNone,
634                         SkBackingFit::kExact, budgeted, GrProtected::kNo,
635                         GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
636                 rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
637                                  SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
638                                  SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
639                                  SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
640                 // At this point the proxy shouldn't even be instantiated, there is no texture with
641                 // id 3.
642                 REPORTER_ASSERT(reporter, idleIDs.find(3) == idleIDs.end());
643                 context->flush();
644                 context->priv().getGpu()->testingOnly_flushGpuAndSync();
645                 // Now that the draw is fully consumed, we should have deinstantiated the proxy and
646                 // the texture it made should be idle.
647                 REPORTER_ASSERT(reporter, idleIDs.find(3) != idleIDs.end());
648 
649                 // Make sure we make the call during various shutdown scenarios where the texture
650                 // might persist after context is destroyed, abandoned, etc. We test three
651                 // variations of each scenario. One where the texture is just created. Another,
652                 // where the texture has been used in a draw and then the context is flushed. And
653                 // one where the the texture was drawn but the context is not flushed.
654                 // In each scenario we test holding a ref beyond the context shutdown and not.
655 
656                 // These tests are difficult to get working with Vulkan. See http://skbug.com/8705
657                 // and http://skbug.com/8275
658                 GrBackendApi api = sk_gpu_test::GrContextFactory::ContextTypeBackend(contextType);
659                 if (api == GrBackendApi::kVulkan) {
660                     continue;
661                 }
662                 int id = 4;
663                 enum class DrawType {
664                     kNoDraw,
665                     kDraw,
666                     kDrawAndFlush,
667                 };
668                 for (auto drawType :
669                      {DrawType::kNoDraw, DrawType::kDraw, DrawType::kDrawAndFlush}) {
670                     for (bool unrefFirst : {false, true}) {
671                         auto possiblyDrawAndFlush = [&context, &texture, drawType, unrefFirst, w,
672                                                      h] {
673                             if (drawType == DrawType::kNoDraw) {
674                                 return;
675                             }
676                             SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType,
677                                                                  kPremul_SkAlphaType);
678                             auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0,
679                                                                   nullptr);
680                             auto rtc = rt->getCanvas()
681                                             ->internal_private_accessTopLayerRenderTargetContext();
682                             auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
683                                     texture, GrColorType::kRGBA_8888, kTopLeft_GrSurfaceOrigin);
684                             rtc->drawTexture(
685                                     GrNoClip(), proxy, GrSamplerState::Filter::kNearest,
686                                     SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
687                                     SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
688                                     SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
689                             if (drawType == DrawType::kDrawAndFlush) {
690                                 context->flush();
691                             }
692                             if (unrefFirst) {
693                                 texture.reset();
694                             }
695                         };
696                         texture = make(context, id);
697                         possiblyDrawAndFlush();
698                         context->abandonContext();
699                         texture.reset();
700                         REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
701                         factory.destroyContexts();
702                         context = factory.get(contextType);
703                         ++id;
704 
705                         // Similar to previous, but reset the texture after the context was
706                         // abandoned and then destroyed.
707                         texture = make(context, id);
708                         possiblyDrawAndFlush();
709                         context->abandonContext();
710                         factory.destroyContexts();
711                         texture.reset();
712                         REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
713                         context = factory.get(contextType);
714                         id++;
715 
716                         texture = make(context, id);
717                         possiblyDrawAndFlush();
718                         factory.destroyContexts();
719                         texture.reset();
720                         REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
721                         context = factory.get(contextType);
722                         id++;
723 
724                         texture = make(context, id);
725                         possiblyDrawAndFlush();
726                         factory.releaseResourcesAndAbandonContexts();
727                         texture.reset();
728                         REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
729                         context = factory.get(contextType);
730                         id++;
731                     }
732                 }
733             }
734         }
735     }
736 }
737 
738 // Tests an idle proc that unrefs another resource down to zero.
DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcCacheManipulationTest,reporter,contextInfo)739 DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcCacheManipulationTest, reporter, contextInfo) {
740     GrContext* context = contextInfo.grContext();
741 
742     // idle proc that releases another texture.
743     auto idleProc = [](void* texture) { reinterpret_cast<GrTexture*>(texture)->unref(); };
744 
745     for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
746         for (const auto& otherMaker : {make_wrapped_texture, make_normal_texture}) {
747             for (auto idleState :
748                  {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) {
749                 auto idleTexture = idleMaker(context, GrRenderable::kNo);
750                 auto otherTexture = otherMaker(context, GrRenderable::kNo);
751                 otherTexture->ref();
752                 idleTexture->addIdleProc(idleProc, otherTexture.get(), idleState);
753                 otherTexture.reset();
754                 idleTexture.reset();
755             }
756         }
757     }
758 }
759 
760 // Similar to above but more complicated. This flushes the context from the idle proc.
761 // crbug.com/933526.
DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcFlushTest,reporter,contextInfo)762 DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcFlushTest, reporter, contextInfo) {
763     GrContext* context = contextInfo.grContext();
764 
765     // idle proc that flushes the context.
766     auto idleProc = [](void* context) { reinterpret_cast<GrContext*>(context)->flush(); };
767 
768     for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
769         for (auto idleState : {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) {
770             auto idleTexture = idleMaker(context, GrRenderable::kNo);
771             idleTexture->addIdleProc(idleProc, context, idleState);
772             auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
773             auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 1, nullptr);
774             // We'll draw two images to the canvas. One is a normal texture-backed image. The other
775             // is a wrapped-texture backed image.
776             surf->getCanvas()->clear(SK_ColorWHITE);
777             auto img1 = surf->makeImageSnapshot();
778 
779             GrBackendTexture backendTexture;
780 
781             if (!create_backend_texture(context, &backendTexture, info, SkColors::kBlack,
782                                         GrMipMapped::kNo, GrRenderable::kNo)) {
783                 REPORTER_ASSERT(reporter, false);
784                 continue;
785             }
786 
787             auto img2 = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
788                                                  info.colorType(), info.alphaType(), nullptr);
789             surf->getCanvas()->drawImage(std::move(img1), 0, 0);
790             surf->getCanvas()->drawImage(std::move(img2), 1, 1);
791             idleTexture.reset();
792 
793             delete_backend_texture(context, backendTexture);
794         }
795     }
796 }
797 
DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcRerefTest,reporter,contextInfo)798 DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcRerefTest, reporter, contextInfo) {
799     GrContext* context = contextInfo.grContext();
800     // idle proc that refs the texture
801     auto idleProc = [](void* texture) { reinterpret_cast<GrTexture*>(texture)->ref(); };
802     // release proc to check whether the texture was released or not.
803     auto releaseProc = [](void* isReleased) { *reinterpret_cast<bool*>(isReleased) = true; };
804     for (auto idleState : {GrTexture::IdleState::kFlushed, GrTexture::IdleState::kFinished}) {
805         bool isReleased = false;
806         auto idleTexture = make_normal_texture(context, GrRenderable::kNo);
807         // This test assumes the texture won't be cached (or else the release proc doesn't get
808         // called).
809         idleTexture->resourcePriv().removeScratchKey();
810         context->flush();
811         idleTexture->addIdleProc(idleProc, idleTexture.get(), idleState);
812         idleTexture->setRelease(releaseProc, &isReleased);
813         auto* raw = idleTexture.get();
814         idleTexture.reset();
815         REPORTER_ASSERT(reporter, !isReleased);
816         raw->unref();
817         REPORTER_ASSERT(reporter, isReleased);
818     }
819 }
820 
DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleStateTest,reporter,contextInfo)821 DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleStateTest, reporter, contextInfo) {
822     GrContext* context = contextInfo.grContext();
823     for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
824         auto idleTexture = idleMaker(context, GrRenderable::kNo);
825 
826         uint32_t flags = 0;
827         static constexpr uint32_t kFlushFlag = 0x1;
828         static constexpr uint32_t kFinishFlag = 0x2;
829         auto flushProc = [](void* flags) { *static_cast<uint32_t*>(flags) |= kFlushFlag; };
830         auto finishProc = [](void* flags) { *static_cast<uint32_t*>(flags) |= kFinishFlag; };
831         idleTexture->addIdleProc(flushProc, &flags, GrTexture::IdleState::kFlushed);
832         idleTexture->addIdleProc(finishProc, &flags, GrTexture::IdleState::kFinished);
833 
834         // Insert a copy from idleTexture to another texture so that we have some queued IO on
835         // idleTexture.
836         SkImageInfo info = SkImageInfo::Make(kSurfSize, kSurfSize, kRGBA_8888_SkColorType,
837                                              kPremul_SkAlphaType);
838         auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, nullptr);
839         auto rtc = rt->getCanvas()->internal_private_accessTopLayerRenderTargetContext();
840         auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
841                 std::move(idleTexture), GrColorType::kRGBA_8888, rtc->asSurfaceProxy()->origin());
842         context->flush();
843         SkAssertResult(rtc->testCopy(proxy.get()));
844         proxy.reset();
845         REPORTER_ASSERT(reporter, flags == 0);
846 
847         // After a flush we expect idleTexture to have reached the kFlushed state on all backends.
848         // On "managed" backends we expect it to reach kFinished as well. On Vulkan, the only
849         // current "unmanaged" backend, we *may* need a sync to reach kFinished.
850         context->flush();
851         if (contextInfo.backend() == kVulkan_GrBackend) {
852             REPORTER_ASSERT(reporter, flags & kFlushFlag);
853         } else {
854             REPORTER_ASSERT(reporter, flags == (kFlushFlag | kFinishFlag));
855         }
856         context->priv().getGpu()->testingOnly_flushGpuAndSync();
857         REPORTER_ASSERT(reporter, flags == (kFlushFlag | kFinishFlag));
858     }
859 }
860