b2c5dae65d
Now that we never re-fulfill a promise image we no longer need to deinstantiate promise image proxies. They now can use kSingleUse callback semantics. This was the only usage of the kDeinstantiate lazy callback type so it is removed. The DeinstantiateProxyTracker is also no longer required and is removed. The GrTexture idle callback mechanism now uses GrReleaseProcHelper, which has been extended to support chaining multiple callbacks together and an abandon() method that aborts calling the callback in the destructor. It has been renamed GrRefCntedCallback to reflect its more general usage. Bug: skia:8800 Change-Id: I857c9eec57fdf706631a266ec8bea682d6657a7c Reviewed-on: https://skia-review.googlesource.com/c/skia/+/196500 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
650 lines
32 KiB
C++
650 lines
32 KiB
C++
/*
|
|
* Copyright 2013 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include <set>
|
|
#include "GrClip.h"
|
|
#include "GrContext.h"
|
|
#include "GrContextPriv.h"
|
|
#include "GrGpu.h"
|
|
#include "GrProxyProvider.h"
|
|
#include "GrRenderTarget.h"
|
|
#include "GrResourceProvider.h"
|
|
#include "GrTexture.h"
|
|
#include "GrTexturePriv.h"
|
|
#include "SkAutoPixmapStorage.h"
|
|
#include "SkMipMap.h"
|
|
#include "SkSurface.h"
|
|
#include "Test.h"
|
|
|
|
// Tests that GrSurface::asTexture(), GrSurface::asRenderTarget(), and static upcasting of texture
|
|
// and render targets to GrSurface all work as expected.
|
|
DEF_GPUTEST_FOR_NULLGL_CONTEXT(GrSurface, reporter, ctxInfo) {
|
|
GrContext* context = ctxInfo.grContext();
|
|
auto resourceProvider = context->priv().resourceProvider();
|
|
GrGpu* gpu = context->priv().getGpu();
|
|
|
|
GrSurfaceDesc desc;
|
|
desc.fFlags = kRenderTarget_GrSurfaceFlag;
|
|
desc.fWidth = 256;
|
|
desc.fHeight = 256;
|
|
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
|
desc.fSampleCnt = 1;
|
|
sk_sp<GrSurface> texRT1 = resourceProvider->createTexture(desc, SkBudgeted::kNo);
|
|
|
|
REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asRenderTarget());
|
|
REPORTER_ASSERT(reporter, texRT1.get() == texRT1->asTexture());
|
|
REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) ==
|
|
texRT1->asTexture());
|
|
REPORTER_ASSERT(reporter, texRT1->asRenderTarget() ==
|
|
static_cast<GrSurface*>(texRT1->asTexture()));
|
|
REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT1->asRenderTarget()) ==
|
|
static_cast<GrSurface*>(texRT1->asTexture()));
|
|
|
|
desc.fFlags = kNone_GrSurfaceFlags;
|
|
sk_sp<GrTexture> tex1 = resourceProvider->createTexture(desc, SkBudgeted::kNo);
|
|
REPORTER_ASSERT(reporter, nullptr == tex1->asRenderTarget());
|
|
REPORTER_ASSERT(reporter, tex1.get() == tex1->asTexture());
|
|
REPORTER_ASSERT(reporter, static_cast<GrSurface*>(tex1.get()) == tex1->asTexture());
|
|
|
|
GrBackendTexture backendTex = gpu->createTestingOnlyBackendTexture(
|
|
nullptr, 256, 256, GrColorType::kRGBA_8888, false, GrMipMapped::kNo);
|
|
|
|
sk_sp<GrSurface> texRT2 = resourceProvider->wrapRenderableBackendTexture(
|
|
backendTex, 1, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo);
|
|
|
|
REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asRenderTarget());
|
|
REPORTER_ASSERT(reporter, texRT2.get() == texRT2->asTexture());
|
|
REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
|
|
texRT2->asTexture());
|
|
REPORTER_ASSERT(reporter, texRT2->asRenderTarget() ==
|
|
static_cast<GrSurface*>(texRT2->asTexture()));
|
|
REPORTER_ASSERT(reporter, static_cast<GrSurface*>(texRT2->asRenderTarget()) ==
|
|
static_cast<GrSurface*>(texRT2->asTexture()));
|
|
|
|
gpu->deleteTestingOnlyBackendTexture(backendTex);
|
|
}
|
|
|
|
// This test checks that the isConfigTexturable and isConfigRenderable are
|
|
// consistent with createTexture's result.
|
|
DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSurfaceRenderability, reporter, ctxInfo) {
|
|
GrContext* context = ctxInfo.grContext();
|
|
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
|
|
GrResourceProvider* resourceProvider = context->priv().resourceProvider();
|
|
const GrCaps* caps = context->priv().caps();
|
|
|
|
GrPixelConfig configs[] = {
|
|
kUnknown_GrPixelConfig,
|
|
kAlpha_8_GrPixelConfig,
|
|
kAlpha_8_as_Alpha_GrPixelConfig,
|
|
kAlpha_8_as_Red_GrPixelConfig,
|
|
kGray_8_GrPixelConfig,
|
|
kGray_8_as_Lum_GrPixelConfig,
|
|
kGray_8_as_Red_GrPixelConfig,
|
|
kRGB_565_GrPixelConfig,
|
|
kRGBA_4444_GrPixelConfig,
|
|
kRGBA_8888_GrPixelConfig,
|
|
kRGB_888_GrPixelConfig,
|
|
kRGB_888X_GrPixelConfig,
|
|
kRG_88_GrPixelConfig,
|
|
kBGRA_8888_GrPixelConfig,
|
|
kSRGBA_8888_GrPixelConfig,
|
|
kSBGRA_8888_GrPixelConfig,
|
|
kRGBA_1010102_GrPixelConfig,
|
|
kRGBA_float_GrPixelConfig,
|
|
kRG_float_GrPixelConfig,
|
|
kAlpha_half_GrPixelConfig,
|
|
kAlpha_half_as_Red_GrPixelConfig,
|
|
kRGBA_half_GrPixelConfig,
|
|
kRGB_ETC1_GrPixelConfig,
|
|
};
|
|
GR_STATIC_ASSERT(kGrPixelConfigCnt == SK_ARRAY_COUNT(configs));
|
|
|
|
GrSurfaceDesc desc;
|
|
desc.fWidth = 64;
|
|
desc.fHeight = 64;
|
|
|
|
for (GrPixelConfig config : configs) {
|
|
for (GrSurfaceOrigin origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin }) {
|
|
desc.fFlags = kNone_GrSurfaceFlags;
|
|
desc.fConfig = config;
|
|
desc.fSampleCnt = 1;
|
|
|
|
sk_sp<GrSurface> tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
|
|
bool ict = caps->isConfigTexturable(desc.fConfig);
|
|
REPORTER_ASSERT(reporter, SkToBool(tex) == ict,
|
|
"config:%d, tex:%d, isConfigTexturable:%d", config, SkToBool(tex), ict);
|
|
|
|
GrSRGBEncoded srgbEncoded = GrSRGBEncoded::kNo;
|
|
GrColorType colorType = GrPixelConfigToColorTypeAndEncoding(config, &srgbEncoded);
|
|
const GrBackendFormat format =
|
|
caps->getBackendFormatFromGrColorType(colorType, srgbEncoded);
|
|
|
|
sk_sp<GrTextureProxy> proxy =
|
|
proxyProvider->createMipMapProxy(format, desc, origin, SkBudgeted::kNo);
|
|
REPORTER_ASSERT(reporter, SkToBool(proxy.get()) ==
|
|
(caps->isConfigTexturable(desc.fConfig) &&
|
|
caps->mipMapSupport()));
|
|
|
|
desc.fFlags = kRenderTarget_GrSurfaceFlag;
|
|
tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
|
|
bool isRenderable = caps->isConfigRenderable(config);
|
|
REPORTER_ASSERT(reporter, SkToBool(tex) == isRenderable,
|
|
"config:%d, tex:%d, isRenderable:%d", config, SkToBool(tex),
|
|
isRenderable);
|
|
|
|
desc.fSampleCnt = 2;
|
|
tex = resourceProvider->createTexture(desc, SkBudgeted::kNo);
|
|
isRenderable = SkToBool(caps->getRenderTargetSampleCount(2, config));
|
|
REPORTER_ASSERT(reporter, SkToBool(tex) == isRenderable,
|
|
"config:%d, tex:%d, isRenderable:%d", config, SkToBool(tex),
|
|
isRenderable);
|
|
}
|
|
}
|
|
}
|
|
|
|
#include "GrDrawingManager.h"
|
|
#include "GrSurfaceProxy.h"
|
|
#include "GrTextureContext.h"
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(InitialTextureClear, reporter, context_info) {
|
|
static constexpr int kSize = 100;
|
|
GrSurfaceDesc desc;
|
|
desc.fWidth = desc.fHeight = kSize;
|
|
std::unique_ptr<uint32_t[]> data(new uint32_t[kSize * kSize]);
|
|
|
|
GrContext* context = context_info.grContext();
|
|
const GrCaps* caps = context->priv().caps();
|
|
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
|
|
|
|
for (int c = 0; c <= kLast_GrPixelConfig; ++c) {
|
|
desc.fConfig = static_cast<GrPixelConfig>(c);
|
|
if (!caps->isConfigTexturable(desc.fConfig)) {
|
|
continue;
|
|
}
|
|
desc.fFlags = kPerformInitialClear_GrSurfaceFlag;
|
|
for (bool rt : {false, true}) {
|
|
if (rt && !caps->isConfigRenderable(desc.fConfig)) {
|
|
continue;
|
|
}
|
|
desc.fFlags |= rt ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
|
|
for (GrSurfaceOrigin origin :
|
|
{kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
|
|
for (auto fit : { SkBackingFit::kApprox, SkBackingFit::kExact }) {
|
|
// Try directly creating the texture.
|
|
// Do this twice in an attempt to hit the cache on the second time through.
|
|
for (int i = 0; i < 2; ++i) {
|
|
auto proxy = proxyProvider->testingOnly_createInstantiatedProxy(
|
|
desc, origin, fit, SkBudgeted::kYes);
|
|
if (!proxy) {
|
|
continue;
|
|
}
|
|
auto texCtx = context->priv().makeWrappedSurfaceContext(std::move(proxy));
|
|
SkImageInfo info = SkImageInfo::Make(
|
|
kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
|
|
if (texCtx->readPixels(info, data.get(), 0, 0, 0)) {
|
|
uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
|
|
for (int i = 0; i < kSize * kSize; ++i) {
|
|
if (cmp != data.get()[i]) {
|
|
ERRORF(reporter, "Failed on config %d", desc.fConfig);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
memset(data.get(), 0xBC, kSize * kSize * sizeof(uint32_t));
|
|
// Here we overwrite the texture so that the second time through we
|
|
// test against recycling without reclearing.
|
|
if (0 == i) {
|
|
texCtx->writePixels(info, data.get(), 0, 0, 0);
|
|
}
|
|
}
|
|
context->priv().testingOnly_purgeAllUnlockedResources();
|
|
|
|
GrSRGBEncoded srgbEncoded = GrSRGBEncoded::kNo;
|
|
GrColorType colorType = GrPixelConfigToColorTypeAndEncoding(desc.fConfig,
|
|
&srgbEncoded);
|
|
const GrBackendFormat format =
|
|
caps->getBackendFormatFromGrColorType(colorType, srgbEncoded);
|
|
|
|
// Try creating the texture as a deferred proxy.
|
|
for (int i = 0; i < 2; ++i) {
|
|
auto surfCtx = context->priv().makeDeferredSurfaceContext(
|
|
format, desc, origin, GrMipMapped::kNo, fit, SkBudgeted::kYes);
|
|
if (!surfCtx) {
|
|
continue;
|
|
}
|
|
SkImageInfo info = SkImageInfo::Make(
|
|
kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
memset(data.get(), 0xAB, kSize * kSize * sizeof(uint32_t));
|
|
if (surfCtx->readPixels(info, data.get(), 0, 0, 0)) {
|
|
uint32_t cmp = GrPixelConfigIsOpaque(desc.fConfig) ? 0xFF000000 : 0;
|
|
for (int i = 0; i < kSize * kSize; ++i) {
|
|
if (cmp != data.get()[i]) {
|
|
ERRORF(reporter, "Failed on config %d", desc.fConfig);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Here we overwrite the texture so that the second time through we
|
|
// test against recycling without reclearing.
|
|
if (0 == i) {
|
|
surfCtx->writePixels(info, data.get(), 0, 0, 0);
|
|
}
|
|
}
|
|
context->priv().testingOnly_purgeAllUnlockedResources();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadOnlyTexture, reporter, context_info) {
|
|
auto fillPixels = [](const SkPixmap* p, const std::function<uint32_t(int x, int y)>& f) {
|
|
for (int y = 0; y < p->height(); ++y) {
|
|
for (int x = 0; x < p->width(); ++x) {
|
|
*p->writable_addr32(x, y) = f(x, y);
|
|
}
|
|
}
|
|
};
|
|
|
|
auto comparePixels = [](const SkPixmap& p1, const SkPixmap& p2, skiatest::Reporter* reporter) {
|
|
SkASSERT(p1.info() == p2.info());
|
|
for (int y = 0; y < p1.height(); ++y) {
|
|
for (int x = 0; x < p1.width(); ++x) {
|
|
REPORTER_ASSERT(reporter, p1.getColor(x, y) == p2.getColor(x, y));
|
|
if (p1.getColor(x, y) != p2.getColor(x, y)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
static constexpr int kSize = 100;
|
|
SkAutoPixmapStorage pixels;
|
|
pixels.alloc(SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
|
|
fillPixels(&pixels, [](int x, int y) {
|
|
return (0xFFU << 24) | (x << 16) | (y << 8) | uint8_t((x * y) & 0xFF);
|
|
});
|
|
|
|
GrContext* context = context_info.grContext();
|
|
GrProxyProvider* proxyProvider = context->priv().proxyProvider();
|
|
|
|
// We test both kRW in addition to kRead mostly to ensure that the calls are structured such
|
|
// that they'd succeed if the texture wasn't kRead. We want to be sure we're failing with
|
|
// kRead for the right reason.
|
|
for (auto ioType : {kRead_GrIOType, kRW_GrIOType}) {
|
|
auto backendTex = context->priv().getGpu()->createTestingOnlyBackendTexture(
|
|
pixels.addr(), kSize, kSize, kRGBA_8888_SkColorType, true, GrMipMapped::kNo);
|
|
auto proxy = proxyProvider->wrapBackendTexture(backendTex, kTopLeft_GrSurfaceOrigin,
|
|
kBorrow_GrWrapOwnership,
|
|
GrWrapCacheable::kNo, ioType);
|
|
auto surfContext = context->priv().makeWrappedSurfaceContext(proxy);
|
|
|
|
// Read pixels should work with a read-only texture.
|
|
SkAutoPixmapStorage read;
|
|
read.alloc(pixels.info());
|
|
auto readResult = surfContext->readPixels(pixels.info(), read.writable_addr(), 0, 0, 0);
|
|
REPORTER_ASSERT(reporter, readResult);
|
|
if (readResult) {
|
|
comparePixels(pixels, read, reporter);
|
|
}
|
|
|
|
// Write pixels should not work with a read-only texture.
|
|
SkAutoPixmapStorage write;
|
|
write.alloc(pixels.info());
|
|
fillPixels(&write, [&pixels](int x, int y) { return ~*pixels.addr32(); });
|
|
auto writeResult = surfContext->writePixels(pixels.info(), pixels.addr(), 0, 0, 0);
|
|
REPORTER_ASSERT(reporter, writeResult == (ioType == kRW_GrIOType));
|
|
// Try the low level write.
|
|
context->flush();
|
|
auto gpuWriteResult = context->priv().getGpu()->writePixels(
|
|
proxy->peekTexture(), 0, 0, kSize, kSize, GrColorType::kRGBA_8888, write.addr32(),
|
|
0);
|
|
REPORTER_ASSERT(reporter, gpuWriteResult == (ioType == kRW_GrIOType));
|
|
|
|
// Copies should not work with a read-only texture
|
|
auto copySrc = proxyProvider->createTextureProxy(
|
|
SkImage::MakeFromRaster(write, nullptr, nullptr), kNone_GrSurfaceFlags, 1,
|
|
SkBudgeted::kYes, SkBackingFit::kExact);
|
|
REPORTER_ASSERT(reporter, copySrc);
|
|
auto copyResult = surfContext->copy(copySrc.get());
|
|
REPORTER_ASSERT(reporter, copyResult == (ioType == kRW_GrIOType));
|
|
// Try the low level copy.
|
|
context->flush();
|
|
auto gpuCopyResult = context->priv().getGpu()->copySurface(
|
|
proxy->peekTexture(), kTopLeft_GrSurfaceOrigin, copySrc->peekTexture(),
|
|
kTopLeft_GrSurfaceOrigin, SkIRect::MakeWH(kSize, kSize), {0, 0});
|
|
REPORTER_ASSERT(reporter, gpuCopyResult == (ioType == kRW_GrIOType));
|
|
|
|
// Mip regen should not work with a read only texture.
|
|
if (context->priv().caps()->mipMapSupport()) {
|
|
backendTex = context->priv().getGpu()->createTestingOnlyBackendTexture(
|
|
nullptr, kSize, kSize, kRGBA_8888_SkColorType, true, GrMipMapped::kYes);
|
|
proxy = proxyProvider->wrapBackendTexture(backendTex, kTopLeft_GrSurfaceOrigin,
|
|
kBorrow_GrWrapOwnership, GrWrapCacheable::kNo,
|
|
ioType);
|
|
context->flush();
|
|
proxy->peekTexture()->texturePriv().markMipMapsDirty(); // avoids assert in GrGpu.
|
|
auto regenResult =
|
|
context->priv().getGpu()->regenerateMipMapLevels(proxy->peekTexture());
|
|
REPORTER_ASSERT(reporter, regenResult == (ioType == kRW_GrIOType));
|
|
}
|
|
}
|
|
}
|
|
|
|
static sk_sp<GrTexture> make_wrapped_texture(GrContext* context, bool renderable) {
|
|
auto backendTexture = context->priv().getGpu()->createTestingOnlyBackendTexture(
|
|
nullptr, 10, 10, GrColorType::kRGBA_8888, renderable, GrMipMapped::kNo);
|
|
sk_sp<GrTexture> texture;
|
|
if (renderable) {
|
|
texture = context->priv().resourceProvider()->wrapRenderableBackendTexture(
|
|
backendTexture, 1, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo);
|
|
} else {
|
|
texture = context->priv().resourceProvider()->wrapBackendTexture(
|
|
backendTexture, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRW_GrIOType);
|
|
}
|
|
// Add a release proc that deletes the GrBackendTexture.
|
|
struct ReleaseContext {
|
|
GrContext* fContext;
|
|
GrBackendTexture fBackendTexture;
|
|
};
|
|
auto release = [](void* rc) {
|
|
auto releaseContext = static_cast<ReleaseContext*>(rc);
|
|
if (!releaseContext->fContext->abandoned()) {
|
|
if (auto gpu = releaseContext->fContext->priv().getGpu()) {
|
|
gpu->deleteTestingOnlyBackendTexture(releaseContext->fBackendTexture);
|
|
}
|
|
}
|
|
delete releaseContext;
|
|
};
|
|
texture->setRelease(
|
|
sk_make_sp<GrRefCntedCallback>(release, new ReleaseContext{context, backendTexture}));
|
|
return texture;
|
|
}
|
|
|
|
static sk_sp<GrTexture> make_normal_texture(GrContext* context, bool renderable) {
|
|
GrSurfaceDesc desc;
|
|
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
|
desc.fWidth = desc.fHeight = 10;
|
|
desc.fFlags = renderable ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
|
|
return context->priv().resourceProvider()->createTexture(desc, SkBudgeted::kNo);
|
|
}
|
|
|
|
DEF_GPUTEST(TextureIdleProcTest, reporter, options) {
|
|
// Various ways of making textures.
|
|
auto makeWrapped = [](GrContext* context) { return make_wrapped_texture(context, false); };
|
|
auto makeWrappedRenderable = [](GrContext* context) {
|
|
return make_wrapped_texture(context, true);
|
|
};
|
|
auto makeNormal = [](GrContext* context) { return make_normal_texture(context, false); };
|
|
auto makeRenderable = [](GrContext* context) { return make_normal_texture(context, true); };
|
|
|
|
std::function<sk_sp<GrTexture>(GrContext*)> makers[] = {makeWrapped, makeWrappedRenderable,
|
|
makeNormal, makeRenderable};
|
|
|
|
// Add a unique key, or not.
|
|
auto addKey = [](GrTexture* texture) {
|
|
static uint32_t gN = 0;
|
|
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
|
|
GrUniqueKey key;
|
|
GrUniqueKey::Builder builder(&key, kDomain, 1);
|
|
builder[0] = gN++;
|
|
builder.finish();
|
|
texture->resourcePriv().setUniqueKey(key);
|
|
};
|
|
auto dontAddKey = [](GrTexture* texture) {};
|
|
std::function<void(GrTexture*)> keyAdders[] = {addKey, dontAddKey};
|
|
|
|
for (const auto& m : makers) {
|
|
for (const auto& keyAdder : keyAdders) {
|
|
for (int type = 0; type < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++type) {
|
|
sk_gpu_test::GrContextFactory factory;
|
|
auto contextType = static_cast<sk_gpu_test::GrContextFactory::ContextType>(type);
|
|
GrContext* context = factory.get(contextType);
|
|
if (!context) {
|
|
continue;
|
|
}
|
|
|
|
// The callback we add simply adds an integer to a set.
|
|
std::set<int> idleIDs;
|
|
struct Context {
|
|
std::set<int>* fIdleIDs;
|
|
int fNum;
|
|
};
|
|
auto proc = [](void* context) {
|
|
static_cast<Context*>(context)->fIdleIDs->insert(
|
|
static_cast<Context*>(context)->fNum);
|
|
delete static_cast<Context*>(context);
|
|
};
|
|
|
|
// Makes a texture, possibly adds a key, and sets the callback.
|
|
auto make = [&m, &keyAdder, &proc, &idleIDs](GrContext* context, int num) {
|
|
sk_sp<GrTexture> texture = m(context);
|
|
texture->addIdleProc(
|
|
sk_make_sp<GrRefCntedCallback>(proc, new Context{&idleIDs, num}));
|
|
keyAdder(texture.get());
|
|
return texture;
|
|
};
|
|
|
|
auto texture = make(context, 1);
|
|
REPORTER_ASSERT(reporter, idleIDs.find(1) == idleIDs.end());
|
|
bool isRT = SkToBool(texture->asRenderTarget());
|
|
auto backendFormat = texture->backendFormat();
|
|
texture.reset();
|
|
REPORTER_ASSERT(reporter, idleIDs.find(1) != idleIDs.end());
|
|
|
|
texture = make(context, 2);
|
|
int w = texture->width();
|
|
int h = texture->height();
|
|
SkImageInfo info =
|
|
SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0, nullptr);
|
|
auto rtc = rt->getCanvas()->internal_private_accessTopLayerRenderTargetContext();
|
|
auto singleUseLazyCB = [&texture](GrResourceProvider* rp) {
|
|
return std::move(texture);
|
|
};
|
|
GrSurfaceDesc desc;
|
|
desc.fWidth = w;
|
|
desc.fHeight = h;
|
|
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
|
if (isRT) {
|
|
desc.fFlags = kRenderTarget_GrSurfaceFlag;
|
|
}
|
|
SkBudgeted budgeted;
|
|
if (texture->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted) {
|
|
budgeted = SkBudgeted::kYes;
|
|
} else {
|
|
budgeted = SkBudgeted::kNo;
|
|
}
|
|
auto proxy = context->priv().proxyProvider()->createLazyProxy(
|
|
singleUseLazyCB, backendFormat, desc,
|
|
GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
|
|
GrInternalSurfaceFlags ::kNone, SkBackingFit::kExact, budgeted,
|
|
GrSurfaceProxy::LazyInstantiationType::kSingleUse);
|
|
rtc->drawTexture(GrNoClip(), proxy, GrSamplerState::Filter::kNearest,
|
|
SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
|
|
SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
|
|
SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
|
|
// We still have the proxy, which should remain instantiated, thereby keeping the
|
|
// texture not purgeable.
|
|
REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
|
|
context->flush();
|
|
REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
|
|
context->priv().getGpu()->testingOnly_flushGpuAndSync();
|
|
REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
|
|
|
|
// This time we move the proxy into the draw.
|
|
rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
|
|
SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
|
|
SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
|
|
SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
|
|
REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
|
|
context->flush();
|
|
context->priv().getGpu()->testingOnly_flushGpuAndSync();
|
|
// Now that the draw is fully consumed by the GPU, the texture should be idle.
|
|
REPORTER_ASSERT(reporter, idleIDs.find(2) != idleIDs.end());
|
|
|
|
// Make sure we make the call during various shutdown scenarios where the texture
|
|
// might persist after context is destroyed, abandoned, etc. We test three
|
|
// variations of each scenario. One where the texture is just created. Another,
|
|
// where the texture has been used in a draw and then the context is flushed. And
|
|
// one where the the texture was drawn but the context is not flushed.
|
|
// In each scenario we test holding a ref beyond the context shutdown and not.
|
|
|
|
// These tests are difficult to get working with Vulkan. See http://skbug.com/8705
|
|
// and http://skbug.com/8275
|
|
GrBackendApi api = sk_gpu_test::GrContextFactory::ContextTypeBackend(contextType);
|
|
if (api == GrBackendApi::kVulkan) {
|
|
continue;
|
|
}
|
|
int id = 3;
|
|
enum class DrawType {
|
|
kNoDraw,
|
|
kDraw,
|
|
kDrawAndFlush,
|
|
};
|
|
for (auto drawType :
|
|
{DrawType::kNoDraw, DrawType::kDraw, DrawType::kDrawAndFlush}) {
|
|
for (bool unrefFirst : {false, true}) {
|
|
auto possiblyDrawAndFlush = [&context, &texture, drawType, unrefFirst, w,
|
|
h] {
|
|
if (drawType == DrawType::kNoDraw) {
|
|
return;
|
|
}
|
|
SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType,
|
|
kPremul_SkAlphaType);
|
|
auto rt = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 0,
|
|
nullptr);
|
|
auto rtc = rt->getCanvas()
|
|
->internal_private_accessTopLayerRenderTargetContext();
|
|
auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
|
|
texture, kTopLeft_GrSurfaceOrigin);
|
|
rtc->drawTexture(
|
|
GrNoClip(), proxy, GrSamplerState::Filter::kNearest,
|
|
SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(w, h),
|
|
SkRect::MakeWH(w, h), GrAA::kNo, GrQuadAAFlags::kNone,
|
|
SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
|
|
if (drawType == DrawType::kDrawAndFlush) {
|
|
context->flush();
|
|
}
|
|
if (unrefFirst) {
|
|
texture.reset();
|
|
}
|
|
};
|
|
texture = make(context, id);
|
|
possiblyDrawAndFlush();
|
|
context->abandonContext();
|
|
texture.reset();
|
|
REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
|
|
factory.destroyContexts();
|
|
context = factory.get(contextType);
|
|
++id;
|
|
|
|
// Similar to previous, but reset the texture after the context was
|
|
// abandoned and then destroyed.
|
|
texture = make(context, id);
|
|
possiblyDrawAndFlush();
|
|
context->abandonContext();
|
|
factory.destroyContexts();
|
|
texture.reset();
|
|
REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
|
|
context = factory.get(contextType);
|
|
id++;
|
|
|
|
texture = make(context, id);
|
|
possiblyDrawAndFlush();
|
|
factory.destroyContexts();
|
|
texture.reset();
|
|
REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
|
|
context = factory.get(contextType);
|
|
id++;
|
|
|
|
texture = make(context, id);
|
|
possiblyDrawAndFlush();
|
|
factory.releaseResourcesAndAbandonContexts();
|
|
texture.reset();
|
|
REPORTER_ASSERT(reporter, idleIDs.find(id) != idleIDs.end());
|
|
context = factory.get(contextType);
|
|
id++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests an idle proc that unrefs another resource down to zero.
|
|
DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcCacheManipulationTest, reporter, contextInfo) {
|
|
GrContext* context = contextInfo.grContext();
|
|
|
|
// idle proc that releases another texture.
|
|
auto idleProc = [](void* texture) { reinterpret_cast<GrTexture*>(texture)->unref(); };
|
|
|
|
for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
|
|
for (const auto& otherMaker : {make_wrapped_texture, make_normal_texture}) {
|
|
auto idleTexture = idleMaker(context, false);
|
|
auto otherTexture = otherMaker(context, false);
|
|
otherTexture->ref();
|
|
idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(idleProc, otherTexture.get()));
|
|
otherTexture.reset();
|
|
idleTexture.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Similar to above but more complicated. This flushes the context from the idle proc.
|
|
// crbug.com/933526.
|
|
DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcFlushTest, reporter, contextInfo) {
|
|
GrContext* context = contextInfo.grContext();
|
|
|
|
// idle proc that flushes the context.
|
|
auto idleProc = [](void* context) { reinterpret_cast<GrContext*>(context)->flush(); };
|
|
|
|
for (const auto& idleMaker : {make_wrapped_texture, make_normal_texture}) {
|
|
auto idleTexture = idleMaker(context, false);
|
|
idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(idleProc, context));
|
|
auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
|
auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info, 1, nullptr);
|
|
// We'll draw two images to the canvas. One is a normal texture-backed image. The other is
|
|
// a wrapped-texture backed image.
|
|
surf->getCanvas()->clear(SK_ColorWHITE);
|
|
auto img1 = surf->makeImageSnapshot();
|
|
auto gpu = context->priv().getGpu();
|
|
std::unique_ptr<uint32_t[]> pixels(new uint32_t[info.width() * info.height()]);
|
|
auto backendTexture = gpu->createTestingOnlyBackendTexture(
|
|
pixels.get(), info.width(), info.height(), kRGBA_8888_SkColorType, false,
|
|
GrMipMapped::kNo);
|
|
auto img2 = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
|
|
info.colorType(), info.alphaType(), nullptr);
|
|
surf->getCanvas()->drawImage(std::move(img1), 0, 0);
|
|
surf->getCanvas()->drawImage(std::move(img2), 1, 1);
|
|
idleTexture.reset();
|
|
gpu->deleteTestingOnlyBackendTexture(backendTexture);
|
|
}
|
|
}
|
|
|
|
DEF_GPUTEST_FOR_ALL_CONTEXTS(TextureIdleProcRerefTest, reporter, contextInfo) {
|
|
GrContext* context = contextInfo.grContext();
|
|
// idle proc that refs the texture
|
|
auto idleProc = [](void* texture) { reinterpret_cast<GrTexture*>(texture)->ref(); };
|
|
// release proc to check whether the texture was released or not.
|
|
auto releaseProc = [](void* isReleased) { *reinterpret_cast<bool*>(isReleased) = true; };
|
|
bool isReleased = false;
|
|
auto idleTexture = make_normal_texture(context, false);
|
|
// This test assumes the texture won't be cached (or else the release proc doesn't get
|
|
// called).
|
|
idleTexture->resourcePriv().removeScratchKey();
|
|
context->flush();
|
|
idleTexture->addIdleProc(sk_make_sp<GrRefCntedCallback>(idleProc, idleTexture.get()));
|
|
idleTexture->setRelease(releaseProc, &isReleased);
|
|
auto* raw = idleTexture.get();
|
|
idleTexture.reset();
|
|
REPORTER_ASSERT(reporter, !isReleased);
|
|
raw->unref();
|
|
REPORTER_ASSERT(reporter, isReleased);
|
|
}
|