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