Implement a benchmark for GrResourceCache

Adds "grresourcecache_add" and "grresourcecache_find" bench tests to test
GrResourceCache::add and GrResourceCache::find. The tests work only
with GPU backends, since GrResourceCache needs an GrGpu.

Modifies bench tests to override SkBenchmark::isSuitableFor(Backend)
function that specifies what kind of backend the test is inteded
for. This replaces the previous "fIsRendering" flag that would
indicate test that did no rendering.

Adds SkCanvas::getGrContext() call to get the GrContext that the
canvas ends up drawing to. The member function solves a common
use-case that is also used in the benchmark added here.

R=mtklein@google.com, bsalomon@google.com

Author: kkinnunen@nvidia.com

Review URL: https://codereview.chromium.org/73643005

git-svn-id: http://skia.googlecode.com/svn/trunk@12334 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/GrResourceCacheBench.cpp b/bench/GrResourceCacheBench.cpp
new file mode 100644
index 0000000..ea8297d
--- /dev/null
+++ b/bench/GrResourceCacheBench.cpp
@@ -0,0 +1,242 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrResource.h"
+#include "GrResourceCache.h"
+#include "GrStencilBuffer.h"
+#include "GrTexture.h"
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+
+enum {
+    CACHE_SIZE_COUNT = 2048,
+    CACHE_SIZE_BYTES = 2 * 1024 * 1024,
+};
+
+class StencilResource : public GrResource {
+public:
+    SK_DECLARE_INST_COUNT(StencilResource);
+    StencilResource(GrGpu* gpu, int id)
+        : INHERITED(gpu, false),
+          fID(id) {
+    }
+    ~StencilResource() {
+        this->release();
+    }
+
+    virtual size_t sizeInBytes() const SK_OVERRIDE {
+        return 100 + ((fID % 1 == 0) ? -5 : 6);
+    }
+
+    static GrResourceKey ComputeKey(int width, int height, int sampleCnt) {
+        return GrStencilBuffer::ComputeKey(width, height, sampleCnt);
+    }
+
+    int fID;
+
+private:
+    typedef GrResource INHERITED;
+};
+
+class TextureResource : public GrResource {
+public:
+    SK_DECLARE_INST_COUNT(TextureResource);
+    TextureResource(GrGpu* gpu, int id)
+        : INHERITED(gpu, false),
+          fID(id) {
+    }
+    ~TextureResource() {
+        this->release();
+    }
+
+    virtual size_t sizeInBytes() const SK_OVERRIDE {
+        return 100 + ((fID % 1 == 0) ? -40 : 33);
+    }
+
+    static GrResourceKey ComputeKey(const GrTextureDesc& desc) {
+        return GrTexture::ComputeScratchKey(desc);
+    }
+
+    int fID;
+
+private:
+    typedef GrResource INHERITED;
+};
+
+SK_DEFINE_INST_COUNT(StencilResource)
+SK_DEFINE_INST_COUNT(TextureResource)
+
+static void get_stencil(int i, int* w, int* h, int* s) {
+    *w = i % 1024;
+    *h = i * 2 % 1024;
+    *s = i % 1 == 0 ? 0 : 4;
+}
+
+static void get_texture_desc(int i, GrTextureDesc* desc) {
+    desc->fFlags = kRenderTarget_GrTextureFlagBit |
+        kNoStencil_GrTextureFlagBit;
+    desc->fWidth  = i % 1024;
+    desc->fHeight = i * 2 % 1024;
+    desc->fConfig = static_cast<GrPixelConfig>(i % (kLast_GrPixelConfig + 1));
+    desc->fSampleCnt = i % 1 == 0 ? 0 : 4;
+}
+
+static void populate_cache(GrResourceCache* cache, GrGpu* gpu, int resourceCount) {
+    for (int i = 0; i < resourceCount; ++i) {
+        int w, h, s;
+        get_stencil(i, &w, &h, &s);
+        GrResourceKey key = GrStencilBuffer::ComputeKey(w, h, s);
+        GrResource* resource = SkNEW_ARGS(StencilResource, (gpu, i));
+        cache->purgeAsNeeded(1, resource->sizeInBytes());
+        cache->addResource(key, resource);
+        resource->unref();
+    }
+
+    for (int i = 0; i < resourceCount; ++i) {
+        GrTextureDesc desc;
+        get_texture_desc(i, &desc);
+        GrResourceKey key =  TextureResource::ComputeKey(desc);
+        GrResource* resource = SkNEW_ARGS(TextureResource, (gpu, i));
+        cache->purgeAsNeeded(1, resource->sizeInBytes());
+        cache->addResource(key, resource);
+        resource->unref();
+    }
+}
+
+static void check_cache_contents_or_die(GrResourceCache* cache, int k) {
+    // Benchmark find calls that succeed.
+    {
+        GrTextureDesc desc;
+        get_texture_desc(k, &desc);
+        GrResourceKey key = TextureResource::ComputeKey(desc);
+        GrResource* item = cache->find(key);
+        if (NULL == item) {
+            GrCrash("cache add does not work as expected");
+            return;
+        }
+        if (static_cast<TextureResource*>(item)->fID != k) {
+            GrCrash("cache add does not work as expected");
+            return;
+        }
+    }
+    {
+        int w, h, s;
+        get_stencil(k, &w, &h, &s);
+        GrResourceKey key = StencilResource::ComputeKey(w, h, s);
+        GrResource* item = cache->find(key);
+        if (NULL == item) {
+            GrCrash("cache add does not work as expected");
+            return;
+        }
+        if (static_cast<TextureResource*>(item)->fID != k) {
+            GrCrash("cache add does not work as expected");
+            return;
+        }
+    }
+
+    // Benchmark also find calls that always fail.
+    {
+        GrTextureDesc desc;
+        get_texture_desc(k, &desc);
+        desc.fHeight |= 1;
+        GrResourceKey key = TextureResource::ComputeKey(desc);
+        GrResource* item = cache->find(key);
+        if (NULL != item) {
+            GrCrash("cache add does not work as expected");
+            return;
+        }
+    }
+    {
+        int w, h, s;
+        get_stencil(k, &w, &h, &s);
+        h |= 1;
+        GrResourceKey key = StencilResource::ComputeKey(w, h, s);
+        GrResource* item = cache->find(key);
+        if (NULL != item) {
+            GrCrash("cache add does not work as expected");
+            return;
+        }
+    }
+}
+
+class GrResourceCacheBenchAdd : public SkBenchmark {
+    enum {
+        RESOURCE_COUNT = CACHE_SIZE_COUNT / 2,
+        DUPLICATE_COUNT = CACHE_SIZE_COUNT / 4,
+    };
+
+public:
+    virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
+        return backend == kGPU_Backend;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "grresourcecache_add";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        GrGpu* gpu = canvas->getGrContext()->getGpu();
+
+        for (int i = 0; i < this->getLoops(); ++i) {
+            GrResourceCache cache(CACHE_SIZE_COUNT, CACHE_SIZE_BYTES);
+            populate_cache(&cache, gpu, DUPLICATE_COUNT);
+            populate_cache(&cache, gpu, RESOURCE_COUNT);
+
+            // Check that cache works.
+            for (int k = 0; k < RESOURCE_COUNT; k += 33) {
+                check_cache_contents_or_die(&cache, k);
+            }
+            cache.purgeAllUnlocked();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class GrResourceCacheBenchFind : public SkBenchmark {
+    enum {
+        RESOURCE_COUNT = (CACHE_SIZE_COUNT / 2) - 100,
+        DUPLICATE_COUNT = 100
+    };
+
+public:
+    virtual bool isSuitableFor(Backend backend) SK_OVERRIDE {
+        return backend == kGPU_Backend;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "grresourcecache_find";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        GrGpu* gpu = canvas->getGrContext()->getGpu();
+        GrResourceCache cache(CACHE_SIZE_COUNT, CACHE_SIZE_BYTES);
+        populate_cache(&cache, gpu, DUPLICATE_COUNT);
+        populate_cache(&cache, gpu, RESOURCE_COUNT);
+
+        for (int i = 0; i < this->getLoops(); ++i) {
+            for (int k = 0; k < RESOURCE_COUNT; ++k) {
+                check_cache_contents_or_die(&cache, k);
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+DEF_BENCH( return new GrResourceCacheBenchAdd(); )
+DEF_BENCH( return new GrResourceCacheBenchFind(); )
+
+#endif