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