text: /*
   * Copyright 2017 Google Inc.
   *
   * Use of this source code is governed by a BSD-style license that can be
   * found in the LICENSE file.
   */
  
  // This is a GPU-backend specific test.
  
  #include "Test.h"
  
  #if SK_SUPPORT_GPU
  
  #include "GrBackendSurface.h"
  #include "GrContextPriv.h"
  #include "GrResourceCache.h"
  #include "GrResourceProvider.h"
  #include "GrTest.h"
  #include "GrTexture.h"
  #include "GrTextureProxy.h"
  
  #include "SkGr.h"
  #include "SkImage.h"
  
  int GrResourceCache::numUniqueKeyProxies_TestOnly() const {
      return fUniquelyKeyedProxies.count();
  }
  
  static GrSurfaceDesc make_desc(GrSurfaceFlags flags) {
      GrSurfaceDesc desc;
      desc.fFlags = flags;
      desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
      desc.fWidth = 64;
      desc.fHeight = 64;
      desc.fConfig = kRGBA_8888_GrPixelConfig;
      desc.fSampleCnt = 0;
  
      return desc;
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  // Basic test
  
  static sk_sp<GrTextureProxy> deferred_tex(skiatest::Reporter* reporter,
                                            GrResourceProvider* provider, SkBackingFit fit) {
      GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
  
      // Only budgeted & wrapped external proxies get to carry uniqueKeys
      sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(provider, desc, fit,
                                                                 SkBudgeted::kYes);
      REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
      return proxy;
  }
  
  static sk_sp<GrTextureProxy> deferred_texRT(skiatest::Reporter* reporter,
                                              GrResourceProvider* provider, SkBackingFit fit) {
      GrSurfaceDesc desc = make_desc(kRenderTarget_GrSurfaceFlag);
  
      // Only budgeted & wrapped external proxies get to carry uniqueKeys
      sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(provider, desc, fit,
                                                                 SkBudgeted::kYes);
      REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
      return proxy;
  }
  
  static sk_sp<GrTextureProxy> wrapped(skiatest::Reporter* reporter,
                                       GrResourceProvider* provider, SkBackingFit fit) {
      GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
  
      sk_sp<GrTexture> tex;
      if (SkBackingFit::kApprox == fit) {
          tex = sk_sp<GrTexture>(provider->createApproxTexture(desc, 0));
      } else {
          // Only budgeted & wrapped external proxies get to carry uniqueKeys
          tex = provider->createTexture(desc, SkBudgeted::kYes);
      }
  
      sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrapped(std::move(tex),
                                                                kBottomLeft_GrSurfaceOrigin);
      REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
      return proxy;
  }
  
  static sk_sp<GrTextureProxy> wrapped_with_key(skiatest::Reporter* reporter,
                                                GrResourceProvider* provider, SkBackingFit fit) {
      static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
      static int kUniqueKeyData = 0;
  
      GrUniqueKey key;
  
      GrUniqueKey::Builder builder(&key, d, 1, nullptr);
      builder[0] = kUniqueKeyData++;
      builder.finish();
  
      GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
  
      sk_sp<GrTexture> tex;
      if (SkBackingFit::kApprox == fit) {
          tex = sk_sp<GrTexture>(provider->createApproxTexture(desc, 0));
      } else {
          // Only budgeted & wrapped external proxies get to carry uniqueKeys
          tex = provider->createTexture(desc, SkBudgeted::kYes);
      }
  
      tex->resourcePriv().setUniqueKey(key);
  
      sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeWrapped(std::move(tex),
                                                                kBottomLeft_GrSurfaceOrigin);
      REPORTER_ASSERT(reporter, proxy->getUniqueKey().isValid());
      return proxy;
  }
  
  static sk_sp<GrTextureProxy> create_wrapped_backend(GrContext* context, SkBackingFit fit,
                                                      sk_sp<GrTexture>* backingSurface) {
      GrResourceProvider* provider = context->resourceProvider();
  
      GrSurfaceDesc desc = make_desc(kNone_GrSurfaceFlags);
  
      *backingSurface = provider->createTexture(desc, SkBudgeted::kNo);
      if (!(*backingSurface)) {
          return nullptr;
      }
  
      GrBackendTexture backendTex =
              GrTest::CreateBackendTexture(context->contextPriv().getBackend(),
                                           64, 64,
                                           kRGBA_8888_GrPixelConfig,
                                           (*backingSurface)->getTextureHandle());
  
      return GrSurfaceProxy::MakeWrappedBackend(context, backendTex, kBottomLeft_GrSurfaceOrigin);
  }
  
  
  // This tests the basic capabilities of the uniquely keyed texture proxies. Does assigning
  // and looking them up work, etc.
  static void basic_test(GrContext* context,
                         skiatest::Reporter* reporter,
                         sk_sp<GrTextureProxy> proxy, bool proxyIsCached) {
      static int id = 1;
  
      GrResourceProvider* provider = context->resourceProvider();
      GrResourceCache* cache = context->getResourceCache();
  
      int startCacheCount = cache->getResourceCount();
  
      GrUniqueKey key;
      if (proxy->getUniqueKey().isValid()) {
          key = proxy->getUniqueKey();
      } else {
          GrMakeKeyFromImageID(&key, id, SkIRect::MakeWH(64, 64));
          ++id;
  
          // Assigning the uniqueKey adds the proxy to the hash but doesn't force instantiation
          REPORTER_ASSERT(reporter, !cache->numUniqueKeyProxies_TestOnly());
          provider->assignUniqueKeyToProxy(key, proxy.get());
      }
  
      REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, startCacheCount == cache->getResourceCount());
  
      // setUniqueKey had better stick
      REPORTER_ASSERT(reporter, key == proxy->getUniqueKey());
  
      // We just added it, surely we can find it
      REPORTER_ASSERT(reporter, provider->findOrCreateProxyByUniqueKey(key,
                                                                       kBottomLeft_GrSurfaceOrigin));
      REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly());
  
      // Once instantiated, the backing resource should have the same key
      SkAssertResult(proxy->instantiate(provider));
      const GrUniqueKey& texKey = proxy->priv().peekSurface()->getUniqueKey();
      REPORTER_ASSERT(reporter, texKey.isValid());
      REPORTER_ASSERT(reporter, key == texKey);
      if (proxyIsCached) {
          REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
      }
  
      // deleting the proxy should delete it from the hash but not the cache
      proxy = nullptr;
      REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
  
      // If the proxy was cached refinding it should bring it back to life
      proxy = provider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin);
      if (proxyIsCached) {
          REPORTER_ASSERT(reporter, proxy);
          REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly());
      } else {
          REPORTER_ASSERT(reporter, !proxy);
          REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly());
      }
      REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
  
      // Mega-purging it should remove it from both the hash and the cache
      proxy = nullptr;
      cache->purgeAllUnlocked();
      if (proxyIsCached) {
          REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
      } else {
          REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
      }
  
      // We can bring neither the texture nor proxy back from perma-death
      proxy = provider->findOrCreateProxyByUniqueKey(key, kBottomLeft_GrSurfaceOrigin);
      REPORTER_ASSERT(reporter, !proxy);
      if (proxyIsCached) {
          REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
      } else {
          REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
      }
  }
  
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  // Invalidation test
  
  // Test if invalidating unique ids operates as expected for texture proxies.
  static void invalidation_test(GrContext* context, skiatest::Reporter* reporter) {
  
      GrResourceCache* cache = context->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 = SkImage::MakeFromBitmap(bm);
          REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly());
          REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
      }
  
      sk_sp<SkImage> textureImg = rasterImg->makeTextureImage(context, nullptr);
      REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
  
      rasterImg = nullptr;        // this invalidates the uniqueKey
  
      // this forces the cache to respond to the inval msg
      int maxNum;
      size_t maxBytes;
      context->getResourceCacheLimits(&maxNum, &maxBytes);
      context->setResourceCacheLimits(maxNum-1, maxBytes);
  
      REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
  
      textureImg = nullptr;
      context->purgeAllUnlockedResources();
  
      REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
  }
  
  #ifndef SK_DISABLE_DEFERRED_PROXIES
  // Test if invalidating unique ids prior to instantiating operates as expected
  static void invalidation_and_instantiation_test(GrContext* context, skiatest::Reporter* reporter) {
      GrResourceProvider* provider = context->resourceProvider();
      GrResourceCache* cache = context->getResourceCache();
      REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
  
      static GrUniqueKey::Domain d = GrUniqueKey::GenerateDomain();
      GrUniqueKey key;
      GrUniqueKey::Builder builder(&key, d, 1, nullptr);
      builder[0] = 0;
      builder.finish();
  
      // Create proxy, assign unique key
      sk_sp<GrTextureProxy> proxy = deferred_tex(reporter, provider, SkBackingFit::kExact);
      provider->assignUniqueKeyToProxy(key, proxy.get());
  
      // Send an invalidation message, which will be sitting in the cache's inbox
      SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(GrUniqueKeyInvalidatedMessage(key));
  
      REPORTER_ASSERT(reporter, 1 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
  
      // Instantiate the proxy. This will trigger the message to be processed, so the resulting
      // texture should *not* have the unique key on it!
      SkAssertResult(proxy->instantiate(provider));
  
      REPORTER_ASSERT(reporter, !proxy->getUniqueKey().isValid());
      REPORTER_ASSERT(reporter, !proxy->priv().peekTexture()->getUniqueKey().isValid());
      REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 1 == cache->getResourceCount());
  
      proxy = nullptr;
      context->purgeAllUnlockedResources();
  
      REPORTER_ASSERT(reporter, 0 == cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
  }
  #endif
  
  DEF_GPUTEST_FOR_RENDERING_CONTEXTS(TextureProxyTest, reporter, ctxInfo) {
      GrContext* context = ctxInfo.grContext();
      GrResourceProvider* provider = context->resourceProvider();
      GrResourceCache* cache = context->getResourceCache();
  
      REPORTER_ASSERT(reporter, !cache->numUniqueKeyProxies_TestOnly());
      REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
  
      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(context, reporter, create(reporter, provider, fit), true);
          }
  
          REPORTER_ASSERT(reporter, 0 == cache->getResourceCount());
          sk_sp<GrTexture> backingTex;
          sk_sp<GrTextureProxy> proxy = create_wrapped_backend(context, fit, &backingTex);
          basic_test(context, reporter, std::move(proxy), false);
  
          backingTex = nullptr;
          cache->purgeAllUnlocked();
      }
  
      invalidation_test(context, reporter);
  #ifndef SK_DISABLE_DEFERRED_PROXIES
      invalidation_and_instantiation_test(context, reporter);
  #endif
  }
  
  #endif
  