[GPU] tile when large bitmap pased drawBitmap and only a small fraction is used
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 30 Nov 2011 14:13:48 +0000 (14:13 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 30 Nov 2011 14:13:48 +0000 (14:13 +0000)
Review URL: http://codereview.appspot.com/5450048/

git-svn-id: http://skia.googlecode.com/svn/trunk@2760 2bbb7eff-a529-9590-31e7-b0007b416f81

gm/drawbitmaprect.cpp [new file with mode: 0644]
gyp/gmslides.gypi
include/gpu/GrContext.h
include/gpu/SkGpuDevice.h
src/gpu/GrContext.cpp
src/gpu/GrResourceCache.cpp
src/gpu/GrResourceCache.h
src/gpu/SkGpuDevice.cpp

diff --git a/gm/drawbitmaprect.cpp b/gm/drawbitmaprect.cpp
new file mode 100644 (file)
index 0000000..1f8210b
--- /dev/null
@@ -0,0 +1,155 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+#include "SkShader.h"
+#include "SkColorPriv.h"
+
+// effects
+#include "SkGradientShader.h"
+
+
+namespace skiagm {
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+    bm->setConfig(config, w, h);
+    bm->allocPixels();
+    bm->eraseColor(0);
+
+    SkCanvas    canvas(*bm);
+
+    SkScalar wScalar = SkIntToScalar(w);
+    SkScalar hScalar = SkIntToScalar(h);
+
+    SkPoint     pt = { wScalar / 2, hScalar / 2 };
+
+    SkScalar    radius = 4 * SkMaxScalar(wScalar, hScalar);
+
+    SkColor     colors[] = { SK_ColorRED, SK_ColorYELLOW,
+                             SK_ColorGREEN, SK_ColorMAGENTA,
+                             SK_ColorBLUE, SK_ColorCYAN,
+                             SK_ColorRED};
+
+    SkScalar    pos[] = {0,
+                         SK_Scalar1 / 6,
+                         2 * SK_Scalar1 / 6,
+                         3 * SK_Scalar1 / 6,
+                         4 * SK_Scalar1 / 6,
+                         5 * SK_Scalar1 / 6,
+                         SK_Scalar1};
+
+    SkPaint     paint;
+    paint.setShader(SkGradientShader::CreateRadial(
+                    pt, radius,
+                    colors, pos,
+                    SK_ARRAY_COUNT(colors),
+                    SkShader::kRepeat_TileMode))->unref();
+    SkRect rect = SkRect::MakeWH(wScalar, hScalar);
+    SkMatrix mat = SkMatrix::I();
+    for (int i = 0; i < 4; ++i) {
+        paint.getShader()->setLocalMatrix(mat);
+        canvas.drawRect(rect, paint);
+        rect.inset(wScalar / 8, hScalar / 8);
+        mat.postScale(SK_Scalar1 / 4, SK_Scalar1 / 4);
+    }
+}
+
+static const int gSize = 1024;
+
+class DrawBitmapRectGM : public GM {
+public:
+    DrawBitmapRectGM() {
+    }
+
+    SkBitmap    fLargeBitmap;
+
+protected:
+    SkString onShortName() {
+        return SkString("drawbitmaprect");
+    }
+    
+    SkISize onISize() { return make_isize(gSize, gSize); }
+    
+    virtual void onDraw(SkCanvas* canvas) {
+        static const int kBmpSize = 2048;
+        if (fLargeBitmap.isNull()) {
+            makebm(&fLargeBitmap,
+                   SkBitmap::kARGB_8888_Config,
+                   kBmpSize, kBmpSize);
+        }
+        SkRect dstRect = { 0, 0, 64, 64};
+        static const int kMaxSrcRectSize = 1 << (SkNextLog2(kBmpSize) + 2);
+
+        static const int kPadX = 30;
+        static const int kPadY = 40;
+        SkPaint paint;
+        paint.setAlpha(0x20);
+        canvas->drawBitmapRect(fLargeBitmap, NULL,
+                               SkRect::MakeWH(gSize * SK_Scalar1,
+                                              gSize * SK_Scalar1),
+                               &paint);
+        canvas->translate(SK_Scalar1 * kPadX / 2,
+                          SK_Scalar1 * kPadY / 2);
+        SkPaint blackPaint;
+        SkScalar titleHeight = SK_Scalar1 * 24;
+        blackPaint.setColor(SK_ColorBLACK);
+        blackPaint.setTextSize(titleHeight);
+        blackPaint.setAntiAlias(true);
+        SkString title;
+        title.printf("Bitmap size: %d x %d", kBmpSize, kBmpSize);
+        canvas->drawText(title.c_str(), title.size(), 0,
+                         titleHeight, blackPaint);
+
+        canvas->translate(0, SK_Scalar1 * kPadY / 2  + titleHeight);
+        int rowCount = 0;
+        canvas->save();
+        for (int w = 1; w <= kMaxSrcRectSize; w *= 4) {
+            for (int h = 1; h <= kMaxSrcRectSize; h *= 4) {
+
+                SkIRect srcRect = SkIRect::MakeXYWH((kBmpSize - w) / 2,
+                                                    (kBmpSize - h) / 2,
+                                                    w, h);
+                canvas->drawBitmapRect(fLargeBitmap, &srcRect, dstRect);
+
+                SkString label;
+                label.appendf("%d x %d", w, h);
+                blackPaint.setAntiAlias(true);
+                blackPaint.setStyle(SkPaint::kFill_Style);
+                blackPaint.setTextSize(SK_Scalar1 * 10);
+                SkScalar baseline = dstRect.height() +
+                                    blackPaint.getTextSize() + SK_Scalar1 * 3;
+                canvas->drawText(label.c_str(), label.size(),
+                                    0, baseline,
+                                    blackPaint);
+                blackPaint.setStyle(SkPaint::kStroke_Style);
+                blackPaint.setStrokeWidth(SK_Scalar1);
+                blackPaint.setAntiAlias(false);
+                canvas->drawRect(dstRect, blackPaint);
+
+                canvas->translate(dstRect.width() + SK_Scalar1 * kPadX, 0);
+                ++rowCount;
+                if ((dstRect.width() + kPadX) * rowCount > gSize) {
+                    canvas->restore();
+                    canvas->translate(0, dstRect.height() + SK_Scalar1 * kPadY);
+                    canvas->save();
+                    rowCount = 0;
+                }
+            }
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DrawBitmapRectGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
index f5d161e335c3114908345fec1ecf3705b954903f..4c8f4aba0e2eb452eb007aa497402db9a7d10111 100644 (file)
@@ -7,6 +7,7 @@
     '../gm/blurs.cpp',
     '../gm/complexclip.cpp',
     '../gm/complexclip2.cpp',
+    '../gm/drawbitmaprect.cpp',
     '../gm/emptypath.cpp',
     '../gm/filltypes.cpp',
     '../gm/filltypespersp.cpp',
index 2106a0236c94ea79c610817d1655af722c118938..bc521590b5cd5d51c434733e007b14c01e8417cb 100644 (file)
@@ -116,6 +116,15 @@ public:
                                          int width,
                                          int height,
                                          const GrSamplerState&);
+    /**
+     * Determines whether a texture is in the cache. If the texture is found it
+     * will not be locked or returned. This call does not affect the priority of
+     * the texture for deletion.
+     */
+    bool isTextureInCache(TextureKey key,
+                          int width,
+                          int height,
+                          const GrSamplerState&) const;
 
     /**
      *  Create a new entry, based on the specified key and texture, and return
index c34cd43489f14ef47acda19ed363ab5be8bab0ee..3afea5e9dc155518ef69213a2eaa0a4ef2abde14 100644 (file)
@@ -122,6 +122,8 @@ protected:
     TexCache lockCachedTexture(const SkBitmap& bitmap,
                                const GrSamplerState& sampler,
                                TexType type = kBitmap_TexType);
+    bool isBitmapInTextureCache(const SkBitmap& bitmap,
+                                const GrSamplerState& sampler) const;
     void unlockCachedTexture(TexCache);
 
     class SkAutoCachedTexture {
@@ -196,6 +198,10 @@ private:
     bool bindDeviceAsTexture(GrPaint* paint);
 
     void prepareRenderTarget(const SkDraw&);
+    bool shouldTileBitmap(const SkBitmap& bitmap,
+                          const GrSamplerState& sampler,
+                          const SkIRect* srcRectPtr,
+                          int* tileSize) const;
     void internalDrawBitmap(const SkDraw&, const SkBitmap&,
                             const SkIRect&, const SkMatrix&, GrPaint* grPaint);
 
index c69c421ab892e0d29f5cc01e6cbc897e4b9afcf8..57f9a312b1c1c61e48ae6bf02695a188db21e039 100644 (file)
@@ -246,6 +246,16 @@ GrContext::TextureCacheEntry GrContext::findAndLockTexture(TextureKey key,
                                             GrResourceCache::kNested_LockType));
 }
 
+bool GrContext::isTextureInCache(TextureKey key,
+                                 int width,
+                                 int height,
+                                 const GrSamplerState& sampler) const {
+    uint32_t v[4];
+    gen_texture_key_values(fGpu, sampler, key, width, height, false, v);
+    GrResourceKey resourceKey(v);
+    return fTextureCache->hasKey(resourceKey);
+}
+
 GrResourceEntry* GrContext::addAndLockStencilBuffer(GrStencilBuffer* sb) {
     ASSERT_OWNED_RESOURCE(sb);
     uint32_t v[4];
index 3094721ceee158aa135b3f2cac70af2a0341e24c..afbe9b3aa197b74b2041c9b5c7e3901d3076d342 100644 (file)
@@ -172,6 +172,10 @@ GrResourceEntry* GrResourceCache::findAndLock(const GrResourceKey& key,
     return entry;
 }
 
+bool GrResourceCache::hasKey(const GrResourceKey& key) const {
+    return NULL != fCache.find(key);
+}
+
 GrResourceEntry* GrResourceCache::createAndLock(const GrResourceKey& key,
                                               GrResource* resource) {
     // we don't expect to create new resources during a purge. In theory
index d3a8f03138e1b754abf3385aa69176a58b5f74ab..e21c6050a1e04d13c7aeed41b2f8fa031cd9b00b 100644 (file)
@@ -231,6 +231,12 @@ public:
      */
     GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*);
 
+    /**
+     * Determines if the cache contains an entry matching a key. If a matching
+     * entry exists but was detached then it will not be found.
+     */
+    bool hasKey(const GrResourceKey& key) const;
+
     /**
      * Detach removes an entry from the cache. This prevents the entry from
      * being found by a subsequent findAndLock() until it is reattached. The
index 869189f28625037d2bcae398117838b2f7ece240..54965a677a4f743d315eb466dfeb76d8f4a6066a 100644 (file)
@@ -1160,6 +1160,105 @@ void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
     fContext->drawPath(grPaint, *pathPtr, fill);
 }
 
+namespace {
+
+inline int get_tile_count(int l, int t, int r, int b, int tileSize)  {
+    int tilesX = (r / tileSize) - (l / tileSize) + 1;
+    int tilesY = (b / tileSize) - (t / tileSize) + 1;
+    return tilesX * tilesY;
+}
+
+inline int determine_tile_size(const SkBitmap& bitmap, 
+                               const SkIRect* srcRectPtr,
+                               int maxTextureSize) {
+    static const int kSmallTileSize = 1 << 10;
+    if (maxTextureSize <= kSmallTileSize) {
+        return maxTextureSize;
+    }
+
+    size_t maxTexTotalTileSize;
+    size_t smallTotalTileSize;
+
+    if (NULL == srcRectPtr) {
+        int w = bitmap.width();
+        int h = bitmap.height();
+        maxTexTotalTileSize = get_tile_count(0, 0, w, h, maxTextureSize);
+        smallTotalTileSize = get_tile_count(0, 0, w, h, kSmallTileSize);
+    } else {
+        maxTexTotalTileSize = get_tile_count(srcRectPtr->fLeft,
+                                             srcRectPtr->fTop,
+                                             srcRectPtr->fRight,
+                                             srcRectPtr->fBottom,
+                                             maxTextureSize);
+        smallTotalTileSize = get_tile_count(srcRectPtr->fLeft,
+                                            srcRectPtr->fTop,
+                                            srcRectPtr->fRight,
+                                            srcRectPtr->fBottom,
+                                            kSmallTileSize);
+    }
+    maxTexTotalTileSize *= maxTextureSize * maxTextureSize;
+    smallTotalTileSize *= kSmallTileSize * kSmallTileSize;
+
+    if (maxTexTotalTileSize > 2 * smallTotalTileSize) {
+        return kSmallTileSize;
+    } else {
+        return maxTextureSize;
+    }
+}
+}
+
+bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
+                                   const GrSamplerState& sampler,
+                                   const SkIRect* srcRectPtr,
+                                   int* tileSize) const {
+    SkASSERT(NULL != tileSize);
+
+    // if bitmap is explictly texture backed then just use the texture
+    if (NULL != bitmap.getTexture()) {
+        return false;
+    }
+    // if it's larger than the max texture size, then we have no choice but
+    // tiling
+    const int maxTextureSize = fContext->getMaxTextureSize();
+    if (bitmap.width() > maxTextureSize ||
+        bitmap.height() > maxTextureSize) {
+        *tileSize = determine_tile_size(bitmap, srcRectPtr, maxTextureSize);
+        return true;
+    }
+    // if we are going to have to draw the whole thing, then don't tile
+    if (NULL == srcRectPtr) {
+        return false;
+    }
+    // if the entire texture is already in our cache then no reason to tile it
+    if (this->isBitmapInTextureCache(bitmap, sampler)) {
+        return false;
+    }
+
+    // At this point we know we could do the draw by uploading the entire bitmap
+    // as a texture. However, if the texture would be large compared to the
+    // cache size and we don't require most of it for this draw then tile to
+    // reduce the amount of upload and cache spill.
+
+    // assumption here is that sw bitmap size is a good proxy for its size as
+    // a texture
+    size_t bmpSize = bitmap.getSize();
+    size_t cacheSize;
+    fContext->getTextureCacheLimits(NULL, &cacheSize);
+    if (bmpSize < cacheSize / 2) {
+        return false;
+    }
+
+    SkFixed fracUsed =
+        SkFixedMul((srcRectPtr->width() << 16) / bitmap.width(),
+                   (srcRectPtr->height() << 16) / bitmap.height());
+    if (fracUsed <= SK_FixedHalf) {
+        *tileSize = determine_tile_size(bitmap, srcRectPtr, maxTextureSize);
+        return true;
+    } else {
+        return false;
+    }
+}
+
 void SkGpuDevice::drawBitmap(const SkDraw& draw,
                              const SkBitmap& bitmap,
                              const SkIRect* srcRectPtr,
@@ -1216,10 +1315,9 @@ void SkGpuDevice::drawBitmap(const SkDraw& draw,
         sampler->setFilter(GrSamplerState::kNearest_Filter);
     }
 
-    const int maxTextureSize = fContext->getMaxTextureSize();
-    if (bitmap.getTexture() || (bitmap.width() <= maxTextureSize &&
-                                bitmap.height() <= maxTextureSize)) {
-        // take the fast case
+    int tileSize;
+    if (!this->shouldTileBitmap(bitmap, *sampler, srcRectPtr, &tileSize)) {
+        // take the simple case
         this->internalDrawBitmap(draw, bitmap, srcRect, m, &grPaint);
         return;
     }
@@ -1243,13 +1341,13 @@ void SkGpuDevice::drawBitmap(const SkDraw& draw,
         clipRect.offset(DX, DY);
     }
 
-    int nx = bitmap.width() / maxTextureSize;
-    int ny = bitmap.height() / maxTextureSize;
+    int nx = bitmap.width() / tileSize;
+    int ny = bitmap.height() / tileSize;
     for (int x = 0; x <= nx; x++) {
         for (int y = 0; y <= ny; y++) {
             SkIRect tileR;
-            tileR.set(x * maxTextureSize, y * maxTextureSize,
-                      (x + 1) * maxTextureSize, (y + 1) * maxTextureSize);
+            tileR.set(x * tileSize, y * tileSize,
+                      (x + 1) * tileSize, (y + 1) * tileSize);
             if (!SkIRect::Intersects(tileR, clipRect)) {
                 continue;
             }
@@ -1699,6 +1797,16 @@ void SkGpuDevice::unlockCachedTexture(TexCache cache) {
     this->context()->unlockTexture(cache);
 }
 
+bool SkGpuDevice::isBitmapInTextureCache(const SkBitmap& bitmap,
+                                         const GrSamplerState& sampler) const {
+    GrContext::TextureKey key = bitmap.getGenerationID();
+    key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
+    return this->context()->isTextureInCache(key, bitmap.width(),
+                                             bitmap.height(), sampler);
+
+}
+
+
 SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config, 
                                                 int width, int height, 
                                                 bool isOpaque,