* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkAlphaType.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkColorType.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkSize.h"
#include "include/core/SkTypes.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/ganesh/GrBackendSurface.h"
#include "include/gpu/ganesh/GrDirectContext.h"
#include "include/gpu/ganesh/GrRecordingContext.h"
#include "include/gpu/ganesh/GrTypes.h"
#include "include/gpu/ganesh/SkImageGanesh.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/core/SkMessageBus.h"
#include "src/gpu/ResourceKey.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
#include "src/gpu/ganesh/GrGpuResourcePriv.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRecordingContextPriv.h"
#include "src/gpu/ganesh/GrResourceCache.h"
#include "src/gpu/ganesh/GrSurface.h"
#include "src/gpu/ganesh/GrTexture.h"
#include "src/gpu/ganesh/GrTextureProxy.h"
#include "src/gpu/ganesh/SkGr.h"
#include "tests/CtsEnforcement.h"
#include "tests/Test.h"
#include "tools/gpu/ManagedBackendTexture.h"
#include <cstddef>
#include <cstdint>
#include <initializer_list>
class GrResourceProvider;
struct GrContextOptions;
int GrProxyProvider::numUniqueKeyProxies_TestOnly() const {
return fUniquelyKeyedProxies.count();
}
static constexpr auto kColorType = GrColorType::kRGBA_8888;
static constexpr auto kSize = SkISize::Make(64, 64);
static sk_sp<GrTextureProxy> deferred_tex(skiatest::Reporter* reporter,
GrRecordingContext* rContext,
GrProxyProvider* proxyProvider,
SkBackingFit fit) {
using namespace skgpu;
const GrCaps* caps = rContext->priv().caps();
Protected isProtected = Protected(caps->supportsProtectedContent());
GrBackendFormat format = caps->getDefaultBackendFormat(kColorType, GrRenderable::kNo);
sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format,
kSize,
GrRenderable::kNo,
1,
Mipmapped::kNo,
fit,
Budgeted::kYes,
isProtected,
{});
REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
return proxy;
}
static sk_sp<GrTextureProxy> deferred_texRT(skiatest::Reporter* reporter,
GrRecordingContext* rContext,
GrProxyProvider* proxyProvider,
SkBackingFit fit) {
using namespace skgpu;
const GrCaps* caps = rContext->priv().caps();
Protected isProtected = Protected(caps->supportsProtectedContent());
GrBackendFormat format = caps->getDefaultBackendFormat(kColorType, GrRenderable::kYes);
sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(format,
kSize,
GrRenderable::kYes,
1,
Mipmapped::kNo,
fit,
Budgeted::kYes,
isProtected,
{});
REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
return proxy;
}
static sk_sp<GrTextureProxy> wrapped(skiatest::Reporter* reporter, GrRecordingContext* rContext,
GrProxyProvider* proxyProvider, SkBackingFit fit) {
using namespace skgpu;
Protected isProtected = Protected(rContext->priv().caps()->supportsProtectedContent());
sk_sp<GrTextureProxy> proxy = proxyProvider->testingOnly_createInstantiatedProxy(
kSize, kColorType, GrRenderable::kNo, 1, fit, Budgeted::kYes, isProtected);
REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
return proxy;
}
static sk_sp<GrTextureProxy> wrapped_with_key(skiatest::Reporter* reporter,
GrRecordingContext* rContext,
GrProxyProvider* proxyProvider,
SkBackingFit fit) {
using namespace skgpu;
Protected isProtected = Protected(rContext->priv().caps()->supportsProtectedContent());
static UniqueKey::Domain d = UniqueKey::GenerateDomain();
static int kUniqueKeyData = 0;
UniqueKey key;
UniqueKey::Builder builder(&key, d, 1, nullptr);
builder[0] = kUniqueKeyData++;
builder.finish();
sk_sp<GrTextureProxy> proxy = proxyProvider->testingOnly_createInstantiatedProxy(
kSize, kColorType, GrRenderable::kNo, 1, fit, Budgeted::kYes, isProtected);
SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
REPORTER_ASSERT(reporter, proxy->getUniqueKey().isValid());
return proxy;
}
static sk_sp<GrTextureProxy> create_wrapped_backend(GrDirectContext* dContext) {
using namespace skgpu;
Protected isProtected = Protected(dContext->priv().caps()->supportsProtectedContent());
auto mbet = sk_gpu_test::ManagedBackendTexture::MakeWithoutData(
dContext,
kSize.width(),
kSize.height(),
GrColorTypeToSkColorType(kColorType),
Mipmapped::kNo,
GrRenderable::kNo,
isProtected);
if (!mbet) {
return nullptr;
}
GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
return proxyProvider->wrapBackendTexture(mbet->texture(),
kBorrow_GrWrapOwnership,
GrWrapCacheable::kYes,
kRead_GrIOType,
mbet->refCountedCallback());
}
static void basic_test(GrDirectContext* dContext,
skiatest::Reporter* reporter,
sk_sp<GrTextureProxy> proxy,
int cacheEntriesPerProxy) {
static int id = 1;
GrResourceProvider* resourceProvider = dContext->priv().resourceProvider();
GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
GrResourceCache* cache = dContext->priv().getResourceCache();
int startCacheCount = cache->getResourceCount();
skgpu::UniqueKey key;
if (proxy->getUniqueKey().isValid()) {
key = proxy->getUniqueKey();
} else {
GrMakeKeyFromImageID(&key, id, SkIRect::MakeWH(64, 64));
++id;
REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
}
REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, startCacheCount == cache->getResourceCount());
REPORTER_ASSERT(reporter, key == proxy->getUniqueKey());
REPORTER_ASSERT(reporter, proxyProvider->findOrCreateProxyByUniqueKey(key));
REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
int expectedCacheCount = startCacheCount + (proxy->isInstantiated() ? 0 : cacheEntriesPerProxy);
SkAssertResult(proxy->instantiate(resourceProvider));
const skgpu::UniqueKey texKey = proxy->peekSurface()->getUniqueKey();
REPORTER_ASSERT(reporter, texKey.isValid());
REPORTER_ASSERT(reporter, key == texKey);
bool expectResourceToOutliveProxy = proxy->peekSurface()->resourcePriv().budgetedType() ==
GrBudgetedType::kUnbudgetedCacheable;
bool expectDeletingProxyToDeleteResource =
proxy->peekSurface()->resourcePriv().budgetedType() ==
GrBudgetedType::kUnbudgetedUncacheable;
proxy = nullptr;
if (expectDeletingProxyToDeleteResource) {
expectedCacheCount -= cacheEntriesPerProxy;
}
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
REPORTER_ASSERT(reporter, proxy);
REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
proxy = nullptr;
cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
if (!expectResourceToOutliveProxy) {
expectedCacheCount -= cacheEntriesPerProxy;
}
REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
REPORTER_ASSERT(reporter, expectResourceToOutliveProxy ? (bool)proxy : !proxy);
REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
if (expectResourceToOutliveProxy) {
proxy.reset();
skgpu::UniqueKeyInvalidatedMessage msg(texKey, dContext->priv().contextID());
SkMessageBus<skgpu::UniqueKeyInvalidatedMessage, uint32_t>::Post(msg);
cache->purgeAsNeeded();
expectedCacheCount -= cacheEntriesPerProxy;
proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
REPORTER_ASSERT(reporter, !proxy);
REPORTER_ASSERT(reporter, expectedCacheCount == cache->getResourceCount());
}
}
static void invalidation_test(GrDirectContext* dContext,
skiatest::Reporter* reporter,
int cacheEntriesPerProxy) {
GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
GrResourceCache* cache = dContext->priv().getResourceCache();
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
sk_sp<SkImage> rasterImg;
{
SkImageInfo ii = SkImageInfo::Make(64, 64, kRGBA_8888_SkColorType, kOpaque_SkAlphaType);
SkBitmap bm;
bm.allocPixels(ii);
rasterImg = bm.asImage();
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
}
int bufferResources = 0;
if (dContext->backend() == GrBackendApi::kVulkan ||
dContext->backend() == GrBackendApi::kDirect3D ||
dContext->backend() == GrBackendApi::kMetal) {
bufferResources = 1;
}
sk_sp<SkImage> textureImg = SkImages::TextureFromImage(dContext, rasterImg);
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, cacheEntriesPerProxy + bufferResources == cache->getResourceCount());
rasterImg = nullptr;
size_t maxBytes = dContext->getResourceCacheLimit();
dContext->setResourceCacheLimit(maxBytes-1);
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, cacheEntriesPerProxy + bufferResources == cache->getResourceCount());
textureImg = nullptr;
dContext->submit(GrSyncCpu::kYes);
dContext->priv().getResourceCache()->purgeUnlockedResources(
GrPurgeResourceOptions::kAllResources);
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
}
static void invalidation_and_instantiation_test(GrDirectContext* dContext,
skiatest::Reporter* reporter,
int cacheEntriesPerProxy) {
GrProxyProvider* proxyProvider = dContext->priv().proxyProvider();
GrResourceProvider* resourceProvider = dContext->priv().resourceProvider();
GrResourceCache* cache = dContext->priv().getResourceCache();
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
static skgpu::UniqueKey::Domain d = skgpu::UniqueKey::GenerateDomain();
skgpu::UniqueKey key;
skgpu::UniqueKey::Builder builder(&key, d, 1, nullptr);
builder[0] = 0;
builder.finish();
sk_sp<GrTextureProxy> proxy = deferred_tex(reporter, dContext, proxyProvider,
SkBackingFit::kExact);
SkAssertResult(proxyProvider->assignUniqueKeyToProxy(key, proxy.get()));
SkMessageBus<skgpu::UniqueKeyInvalidatedMessage, uint32_t>::Post(
skgpu::UniqueKeyInvalidatedMessage(key, dContext->priv().contextID()));
REPORTER_ASSERT(reporter, 1 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
SkAssertResult(proxy->instantiate(resourceProvider));
REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
REPORTER_ASSERT(reporter, !proxy->peekTexture()->getUniqueKey().isValid());
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, cacheEntriesPerProxy == cache->getResourceCount());
proxy = nullptr;
dContext->priv().getResourceCache()->purgeUnlockedResources(
GrPurgeResourceOptions::kAllResources);
REPORTER_ASSERT(reporter, 0 == proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
}
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(TextureProxyTest,
reporter,
ctxInfo,
CtsEnforcement::kApiLevel_T) {
auto direct = ctxInfo.directContext();
GrProxyProvider* proxyProvider = direct->priv().proxyProvider();
GrResourceCache* cache = direct->priv().getResourceCache();
REPORTER_ASSERT(reporter, !proxyProvider->numUniqueKeyProxies_TestOnly());
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
int cacheEntriesPerProxy = 1;
if (direct->backend() == GrBackend::kVulkan || direct->backend() == GrBackend::kMetal) {
cacheEntriesPerProxy++;
}
for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
for (auto create : { deferred_tex, deferred_texRT, wrapped, wrapped_with_key }) {
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
basic_test(direct, reporter, create(reporter, direct, proxyProvider, fit),
cacheEntriesPerProxy);
}
REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
cache->purgeUnlockedResources(GrPurgeResourceOptions::kAllResources);
}
basic_test(direct, reporter, create_wrapped_backend(direct), cacheEntriesPerProxy);
invalidation_test(direct, reporter, cacheEntriesPerProxy);
invalidation_and_instantiation_test(direct, reporter, cacheEntriesPerProxy);
}