Consider hw render target limit for offscreen supersample and tile

Review URL: http://codereview.appspot.com/4575041/



git-svn-id: http://skia.googlecode.com/svn/trunk@1568 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrConfig.h b/gpu/include/GrConfig.h
index 9ee37c7..b3025f3 100644
--- a/gpu/include/GrConfig.h
+++ b/gpu/include/GrConfig.h
@@ -371,6 +371,22 @@
     #define GR_GEOM_BUFFER_LOCK_THRESHOLD (1 << 15)
 #endif
 
+/**
+ * Enables/disables use of offscreen AA
+ */
+#if !defined(GR_USE_OFFSCREEN_AA)
+    #define GR_USE_OFFSCREEN_AA 1
+#endif
+
+/**
+ * GR_MAX_OFFSCREEN_AA_SIZE controls the size at which offscreen AA will tile.
+ * Tiling saves GPU memory by limiting the size of the offscreen buffer. The
+ * max offscreen may be as large as (4*GR_MAX_OFFSCREEN_AA_SIZE)^2 pixels.
+ */
+#if !defined(GR_MAX_OFFSCREEN_AA_SIZE)
+    #define GR_MAX_OFFSCREEN_AA_SIZE    256
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // tail section:
 //
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 8809271..5dfe2c9 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -158,7 +158,13 @@
     /**
      *  Return the max width or height of a texture supported by the current gpu
      */
-    int getMaxTextureDimension();
+    int getMaxTextureSize() const;
+
+    /**
+     * Return the max width or height of a render target supported by the 
+     * current gpu
+     */
+    int getMaxRenderTargetSize() const;
 
     ///////////////////////////////////////////////////////////////////////////
     // Render targets
@@ -399,7 +405,6 @@
                             const TEX_SRC* texCoordSrc,
                             const COL_SRC* colorSrc);
 
-
     ///////////////////////////////////////////////////////////////////////////
     // Misc.
 
@@ -532,6 +537,7 @@
 
     GrIndexBuffer*              fAAFillRectIndexBuffer;
     GrIndexBuffer*              fAAStrokeRectIndexBuffer;
+    int                         fMaxOffscreenAASize;
 
     GrContext(GrGpu* gpu);
 
@@ -568,22 +574,34 @@
 
     struct OffscreenRecord;
 
+    // determines whether offscreen AA should be applied
     bool doOffscreenAA(GrDrawTarget* target, 
                        const GrPaint& paint,
                        bool isLines) const;
 
-    // sets up target to draw coverage to the supersampled render target
-    bool setupOffscreenAAPass1(GrDrawTarget* target,
+    // attempts to setup offscreen AA. All paint state must be transferred to
+    // target by the time this is called.
+    bool prepareForOffscreenAA(GrDrawTarget* target,
                                bool requireStencil,
                                const GrIRect& boundRect,
                                OffscreenRecord* record);
 
+    // sets up target to draw coverage to the supersampled render target
+    void setupOffscreenAAPass1(GrDrawTarget* target,
+                               const GrIRect& boundRect,
+                               int tileX, int tileY,
+                               OffscreenRecord* record);
+
     // sets up target to sample coverage of supersampled render target back
     // to the main render target using stage kOffscreenStage.
-    void offscreenAAPass2(GrDrawTarget* target,
-                          const GrPaint& paint,
-                          const GrIRect& boundRect,
-                          OffscreenRecord* record);
+    void doOffscreenAAPass2(GrDrawTarget* target,
+                            const GrPaint& paint,
+                            const GrIRect& boundRect,
+                            int tileX, int tileY,
+                            OffscreenRecord* record);
+
+    // restored the draw target state and releases offscreen target to cache
+    void cleanupOffscreenAA(GrDrawTarget* target, OffscreenRecord* record);
     
     // computes vertex layout bits based on the paint. If paint expresses
     // a texture for a stage, the stage coords will be bound to postitions
diff --git a/gpu/include/GrContext_impl.h b/gpu/include/GrContext_impl.h
index c79a191..b33d0b4 100644
--- a/gpu/include/GrContext_impl.h
+++ b/gpu/include/GrContext_impl.h
@@ -17,20 +17,6 @@
 #ifndef GrContext_impl_DEFINED
 #define GrContext_impl_DEFINED
 
-struct GrContext::OffscreenRecord {
-    OffscreenRecord() { fEntry0 = NULL; fEntry1 = NULL; }
-    ~OffscreenRecord() { GrAssert(NULL == fEntry0 && NULL == fEntry1); }
-
-    enum Downsample {
-        k4x4TwoPass_Downsample,
-        k4x4SinglePass_Downsample,
-        kFSAA_Downsample
-    }                              fDownsample;
-    GrTextureEntry*                fEntry0;
-    GrTextureEntry*                fEntry1;
-    GrDrawTarget::SavedDrawState   fSavedState;
-};
-
 template <typename POS_SRC, typename TEX_SRC,
           typename COL_SRC, typename IDX_SRC>
 inline void GrContext::drawCustomVertices(const GrPaint& paint,
@@ -86,31 +72,14 @@
         idxSrc->writeValue(i, indices + i);
     }
 
-    bool doAA = false;
-    OffscreenRecord record;
-    GrIRect bounds;
-
-    if (-1 == texOffsets[0] && -1 == colorOffset && 
-        this->doOffscreenAA(target, paint, GrIsPrimTypeLines(primitiveType))) {
-        GrRect b;
-        b.setBounds(geo.positions(), vertexCount);
-        target->getViewMatrix().mapRect(&b);
-        b.roundOut(&bounds);
-        if (this->setupOffscreenAAPass1(target, false, bounds, &record)) {
-            doAA = true;
-        }
-    }
+    // we don't currently apply offscreen AA to this path. Need improved 
+    // management of GrDrawTarget's geometry to avoid copying points per-tile.
 
     if (NULL == idxSrc) {
         target->drawNonIndexed(primitiveType, 0, vertexCount);
     } else {
         target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
     }
-
-    if (doAA) {
-        geo.set(NULL, 0, 0, 0); // have to release geom before can draw again
-        this->offscreenAAPass2(target, paint, bounds, &record);
-    }
 }
 
 class GrNullTexCoordSource {
diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h
index 5cb885a..34ddccf 100644
--- a/gpu/include/GrGpu.h
+++ b/gpu/include/GrGpu.h
@@ -253,7 +253,14 @@
      */
     bool npotRenderTargetSupport() const { return fNPOTRenderTargetSupport; }
 
-    int maxTextureDimension() const { return fMaxTextureDimension; }
+    /**
+     * Gets the largest allowed width and height of a texture.
+     */
+    int maxTextureSize() const { return fMaxTextureSize; }
+    /**
+     * Gets the largest allowed width and height of a render target.
+     */
+    int maxRenderTargetSize() const { return fMaxRenderTargetSize; }
 
     // GrDrawTarget overrides
     virtual void drawIndexed(GrPrimitiveType type,
@@ -404,7 +411,8 @@
     // set by subclass
     int fMinRenderTargetWidth;
     int fMinRenderTargetHeight;
-    int fMaxTextureDimension;
+    int fMaxRenderTargetSize;
+    int fMaxTextureSize;
 
     GrGpuStats fStats;
 
diff --git a/gpu/include/GrTypes.h b/gpu/include/GrTypes.h
index 9348375..e6f3e2c 100644
--- a/gpu/include/GrTypes.h
+++ b/gpu/include/GrTypes.h
@@ -69,6 +69,10 @@
 /**
  *  divide, rounding up
  */
+static inline int32_t GrIDivRoundUp(int x, int y) {
+    GrAssert(y > 0);
+    return (x + (y-1)) / y;
+}
 static inline uint32_t GrUIDivRoundUp(uint32_t x, uint32_t y) {
     return (x + (y-1)) / y;
 }
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 4ebf225..773c573 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -25,10 +25,9 @@
 #include "GrBufferAllocPool.h"
 #include "GrPathRenderer.h"
 
-// larger than this, and we don't AA. set to 0 for no AA
-#ifndef GR_MAX_OFFSCREEN_AA_DIM
-    #define GR_MAX_OFFSCREEN_AA_DIM    0
-#endif
+// Using MSAA seems to be slower for some yet unknown reason.
+#define PREFER_MSAA_OFFSCREEN_AA 0
+#define OFFSCREEN_SSAA_SCALE 4 // super sample at 4x4
 
 #define DEFER_TEXT_RENDERING 1
 
@@ -361,8 +360,12 @@
     fTextureCache->setLimits(maxTextures, maxTextureBytes);
 }
 
-int GrContext::getMaxTextureDimension() {
-    return fGpu->maxTextureDimension();
+int GrContext::getMaxTextureSize() const {
+    return fGpu->maxTextureSize();
+}
+
+int GrContext::getMaxRenderTargetSize() const {
+    return fGpu->maxRenderTargetSize();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -454,10 +457,28 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+struct GrContext::OffscreenRecord {
+    OffscreenRecord() { fEntry0 = NULL; fEntry1 = NULL; }
+    ~OffscreenRecord() { GrAssert(NULL == fEntry0 && NULL == fEntry1); }
+
+    enum Downsample {
+        k4x4TwoPass_Downsample,
+        k4x4SinglePass_Downsample,
+        kFSAA_Downsample
+    }                              fDownsample;
+    int                            fTileSize;
+    int                            fTileCountX;
+    int                            fTileCountY;
+    int                            fScale;
+    GrTextureEntry*                fEntry0;
+    GrTextureEntry*                fEntry1;
+    GrDrawTarget::SavedDrawState   fSavedState;
+};
+
 bool GrContext::doOffscreenAA(GrDrawTarget* target, 
                               const GrPaint& paint,
                               bool isLines) const {
-#if GR_MAX_OFFSCREEN_AA_DIM==0
+#if !GR_USE_OFFSCREEN_AA
     return false;
 #else
     if (!paint.fAntiAlias) {
@@ -483,18 +504,26 @@
 #endif
 }
 
-bool GrContext::setupOffscreenAAPass1(GrDrawTarget* target,
+bool GrContext::prepareForOffscreenAA(GrDrawTarget* target,
                                       bool requireStencil,
                                       const GrIRect& boundRect,
                                       OffscreenRecord* record) {
-    GrAssert(GR_MAX_OFFSCREEN_AA_DIM > 0);
+
+    GrAssert(GR_USE_OFFSCREEN_AA);
 
     GrAssert(NULL == record->fEntry0);
     GrAssert(NULL == record->fEntry1);
+    GrAssert(!boundRect.isEmpty());
 
     int boundW = boundRect.width();
     int boundH = boundRect.height();
-    int size  = GrMax(64, (int)GrNextPow2(GrMax(boundW, boundH)));
+
+    record->fTileSize  = (int)GrNextPow2(GrMax(boundW, boundH));
+    record->fTileSize = GrMax(64, record->fTileSize);
+    record->fTileSize = GrMin(fMaxOffscreenAASize, record->fTileSize);
+
+    record->fTileCountX = GrIDivRoundUp(boundW, record->fTileSize);
+    record->fTileCountY = GrIDivRoundUp(boundH, record->fTileSize);
 
     GrTextureDesc desc;
     if (requireStencil) {
@@ -506,22 +535,22 @@
 
     desc.fFormat = kRGBA_8888_GrPixelConfig;
 
-    int scale;
-    // Using MSAA seems to be slower for some yet unknown reason.
-    if (false && fGpu->supportsFullsceneAA()) {
+    if (PREFER_MSAA_OFFSCREEN_AA && fGpu->supportsFullsceneAA()) {
         record->fDownsample = OffscreenRecord::kFSAA_Downsample;
-        scale = GR_Scalar1;
+        record->fScale = GR_Scalar1;
         desc.fAALevel = kMed_GrAALevel;
     } else {
         record->fDownsample = (fGpu->supports4x4DownsampleFilter()) ?
                                 OffscreenRecord::k4x4SinglePass_Downsample :
                                 OffscreenRecord::k4x4TwoPass_Downsample;
-        scale = 4;
+        record->fScale = OFFSCREEN_SSAA_SCALE;
+        // both downsample paths assume this
+        GR_STATIC_ASSERT(4 == OFFSCREEN_SSAA_SCALE);
         desc.fAALevel = kNone_GrAALevel;
     }
     
-    desc.fWidth = scale * size;
-    desc.fHeight = scale * size;
+    desc.fWidth = record->fScale * record->fTileSize;
+    desc.fHeight = record->fScale * record->fTileSize;
 
     record->fEntry0 = this->lockKeylessTexture(desc);
 
@@ -539,39 +568,73 @@
             return false;
         }
     }
+    target->saveCurrentDrawState(&record->fSavedState);
+    return true;
+}
+
+void GrContext::setupOffscreenAAPass1(GrDrawTarget* target,
+                                      const GrIRect& boundRect,
+                                      int tileX, int tileY,
+                                      OffscreenRecord* record) {
 
     GrRenderTarget* offRT0 = record->fEntry0->texture()->asRenderTarget();
     GrAssert(NULL != offRT0);
 
-    target->saveCurrentDrawState(&record->fSavedState);
-
     GrPaint tempPaint;
     tempPaint.reset();
     SetPaint(tempPaint, target);
     target->setRenderTarget(offRT0);
 
     GrMatrix transM;
-    transM.setTranslate(-boundRect.fLeft, -boundRect.fTop);
+    int left = boundRect.fLeft + tileX * record->fTileSize;
+    int top =  boundRect.fTop  + tileY * record->fTileSize;
+    transM.setTranslate(-left * GR_Scalar1, -top * GR_Scalar1);
     target->postConcatViewMatrix(transM);
     GrMatrix scaleM;
-    scaleM.setScale(scale * GR_Scalar1, scale * GR_Scalar1);
+    scaleM.setScale(record->fScale * GR_Scalar1, record->fScale * GR_Scalar1);
     target->postConcatViewMatrix(scaleM);
 
     // clip gets applied in second pass
     target->disableState(GrDrawTarget::kClip_StateBit);
 
-    GrIRect clear = SkIRect::MakeWH(scale * boundW, scale * boundH);
-    target->clear(&clear, 0x0);
+    int w = (tileX == record->fTileCountX-1) ? boundRect.fRight - left :
+                                               record->fTileSize;
+    int h = (tileY == record->fTileCountY-1) ? boundRect.fBottom - top :
+                                               record->fTileSize;
+    GrIRect clear = SkIRect::MakeWH(record->fScale * w, 
+                                    record->fScale * h);
+#if 0
+    // visualize tile boundaries by setting edges of offscreen to white
+    // and interior to tranparent. black.
+    target->clear(&clear, 0xffffffff);
 
-    return true;
+    static const int gOffset = 2;
+    GrIRect clear2 = SkIRect::MakeLTRB(gOffset, gOffset,
+                                       record->fScale * w - gOffset,
+                                       record->fScale * h - gOffset);
+    target->clear(&clear2, 0x0);
+#else
+    target->clear(&clear, 0x0);
+#endif
 }
 
-void GrContext::offscreenAAPass2(GrDrawTarget* target,
+void GrContext::doOffscreenAAPass2(GrDrawTarget* target,
                                  const GrPaint& paint,
                                  const GrIRect& boundRect,
+                                 int tileX, int tileY,
                                  OffscreenRecord* record) {
 
     GrAssert(NULL != record->fEntry0);
+    
+    GrIRect tileRect;
+    tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSize;
+    tileRect.fTop  = boundRect.fTop  + tileY * record->fTileSize,
+    tileRect.fRight = (tileX == record->fTileCountX-1) ? 
+                        boundRect.fRight :
+                        tileRect.fLeft + record->fTileSize;
+    tileRect.fBottom = (tileY == record->fTileCountY-1) ? 
+                        boundRect.fBottom :
+                        tileRect.fTop + record->fTileSize;
 
     GrSamplerState::Filter filter;
     if (OffscreenRecord::k4x4SinglePass_Downsample == record->fDownsample) {
@@ -604,14 +667,14 @@
                          scale * GR_Scalar1 / src->height());
         sampler.setMatrix(sampleM);
         target->setSamplerState(kOffscreenStage, sampler);
-        GrRect rect = SkRect::MakeWH(scale * boundRect.width(),
-                                     scale * boundRect.height());
+        GrRect rect = SkRect::MakeWH(scale * tileRect.width(),
+                                     scale * tileRect.height());
         target->drawSimpleRect(rect, NULL, 1 << kOffscreenStage);
         
         src = record->fEntry1->texture();
     } else if (OffscreenRecord::kFSAA_Downsample == record->fDownsample) {
         scale = 1;
-        GrIRect rect = SkIRect::MakeWH(boundRect.width(), boundRect.height());
+        GrIRect rect = SkIRect::MakeWH(tileRect.width(), tileRect.height());
         src->asRenderTarget()->overrideResolveRect(rect);
     } else {
         GrAssert(OffscreenRecord::k4x4SinglePass_Downsample == 
@@ -619,7 +682,10 @@
         scale = 4;
     }
 
-    // setup for draw back to main RT
+    // setup for draw back to main RT, we use the original
+    // draw state setup by the caller plus an additional coverage
+    // stage to handle the AA resolve. Also, we use an identity
+    // view matrix and so pre-concat sampler matrices with view inv.
     int stageMask = paint.getActiveStageMask();
 
     target->restoreDrawState(record->fSavedState);
@@ -630,21 +696,27 @@
             target->preConcatSamplerMatrices(stageMask, invVM);
         }
     }
+    // This is important when tiling, otherwise second tile's 
+    // pass 1 view matrix will be incorrect.
+    GrDrawTarget::AutoViewMatrixRestore avmr(target);
+
     target->setViewMatrix(GrMatrix::I());
 
     target->setTexture(kOffscreenStage, src);
     sampleM.setScale(scale * GR_Scalar1 / src->width(),
                      scale * GR_Scalar1 / src->height());
     sampler.setMatrix(sampleM);
-    sampleM.setTranslate(-boundRect.fLeft, -boundRect.fTop);
+    sampleM.setTranslate(-tileRect.fLeft, -tileRect.fTop);
     sampler.preConcatMatrix(sampleM);
     target->setSamplerState(kOffscreenStage, sampler);
 
     GrRect dstRect;
     int stages = (1 << kOffscreenStage) | stageMask;
-    dstRect.set(boundRect);
+    dstRect.set(tileRect);
     target->drawSimpleRect(dstRect, NULL, stages);
+}
 
+void GrContext::cleanupOffscreenAA(GrDrawTarget* target, OffscreenRecord* record) {
     this->unlockTexture(record->fEntry0);
     record->fEntry0 = NULL;
     if (NULL != record->fEntry1) {
@@ -1108,10 +1180,6 @@
     }
     int vertexSize = GrDrawTarget::VertexSize(layout);
 
-    bool doAA = false;
-    OffscreenRecord record;
-    GrIRect bounds;
-
     if (sizeof(GrPoint) != vertexSize) {
         if (!geo.set(target, layout, vertexCount, 0)) {
             GrPrintf("Failed to get space for vertices!");
@@ -1136,36 +1204,20 @@
             curVertex = (void*)((intptr_t)curVertex + vertexSize);
         }
     } else {
-        // we don't do offscreen AA when we have per-vertex tex coords or colors
-        if (this->doOffscreenAA(target, paint, GrIsPrimTypeLines(primitiveType))) {
-            GrRect b;
-            b.setBounds(positions, vertexCount);
-            target->getViewMatrix().mapRect(&b);
-            b.roundOut(&bounds);
-            
-            if (this->setupOffscreenAAPass1(target, false, bounds, &record)) {
-                doAA = true;
-            }
-        }
         target->setVertexSourceToArray(layout, positions, vertexCount);
     }
 
-    if (NULL != indices) {
-        target->setIndexSourceToArray(indices, indexCount);
-    }
+    // we don't currently apply offscreen AA to this path. Need improved 
+    // management of GrDrawTarget's geometry to avoid copying points per-tile.
 
     if (NULL != indices) {
+        target->setIndexSourceToArray(indices, indexCount);
         target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
     } else {
         target->drawNonIndexed(primitiveType, 0, vertexCount);
     }
-
-    if (doAA) {
-        this->offscreenAAPass2(target, paint, bounds, &record);
-    }
 }
 
-
 ///////////////////////////////////////////////////////////////////////////////
 
 void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
@@ -1178,7 +1230,6 @@
         !pr->supportsAA(target, path, fill) &&
         this->doOffscreenAA(target, paint, kHairLine_PathFill == fill)) {
 
-        OffscreenRecord record;
         bool needsStencil = pr->requiresStencilPass(target, path, fill);
 
         // compute bounds as intersection of rt size, clip, and path
@@ -1193,34 +1244,31 @@
         }
 
         GrRect pathBounds = path.getBounds();
-        GrIRect pathIBounds;
         if (!pathBounds.isEmpty()) {
             if (NULL != translate) {
                 pathBounds.offset(*translate);
             }
             target->getViewMatrix().mapRect(&pathBounds, pathBounds);
+            GrIRect pathIBounds;
             pathBounds.roundOut(&pathIBounds);
             if (!bound.intersect(pathIBounds)) {
                 return;
             }
         }
-
-        // for now, abort antialiasing if our bounds are too big, so we don't
-        // hit the FBO size limit
-        if (pathIBounds.width() > GR_MAX_OFFSCREEN_AA_DIM ||
-            pathIBounds.height() > GR_MAX_OFFSCREEN_AA_DIM) {
-            goto NO_AA;
-        }
-
-        if (this->setupOffscreenAAPass1(target, needsStencil, bound, &record)) {
-            pr->drawPath(target, 0, path, fill, translate);
-            this->offscreenAAPass2(target, paint, bound, &record);
+        OffscreenRecord record;
+        if (this->prepareForOffscreenAA(target, needsStencil, bound, &record)) {
+            for (int tx = 0; tx < record.fTileCountX; ++tx) {
+                for (int ty = 0; ty < record.fTileCountY; ++ty) {
+                    this->setupOffscreenAAPass1(target, bound, tx, ty, &record);
+                    pr->drawPath(target, 0, path, fill, translate);
+                    this->doOffscreenAAPass2(target, paint, bound, tx, ty, &record);
+                }
+            }
+            this->cleanupOffscreenAA(target, &record);
             return;
         }
     }
 
-// we can fall out of the AA section for some reasons, and land here
-NO_AA:
     GrDrawTarget::StageBitfield enabledStages = paint.getActiveStageMask();
 
     pr->drawPath(target, enabledStages, path, fill, translate);
@@ -1467,6 +1515,12 @@
 
     fAAFillRectIndexBuffer = NULL;
     fAAStrokeRectIndexBuffer = NULL;
+    
+    int gpuMaxOffscreen = fGpu->maxRenderTargetSize();
+    if (!PREFER_MSAA_OFFSCREEN_AA || !fGpu->supportsFullsceneAA()) {
+        gpuMaxOffscreen /= OFFSCREEN_SSAA_SCALE;
+    }
+    fMaxOffscreenAASize = GrMin(GR_MAX_OFFSCREEN_AA_SIZE, gpuMaxOffscreen);
 
     this->setupDrawBuffer();
 }
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index ff2d406..70686d7 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -439,24 +439,22 @@
         }
     }
 
-    GR_GL_GetIntegerv(GR_GL_MAX_TEXTURE_SIZE, &fMaxTextureDimension);
+    GR_GL_GetIntegerv(GR_GL_MAX_TEXTURE_SIZE, &fMaxTextureSize);
+    GR_GL_GetIntegerv(GR_GL_MAX_RENDERBUFFER_SIZE, &fMaxRenderTargetSize);
+    // Our render targets are always created with textures as the color 
+    // attachment, hence this min:
+    fMaxRenderTargetSize = GrMin(fMaxTextureSize, fMaxRenderTargetSize);
 
     /* The iPhone 4 has a restriction that for an FBO with texture color
        attachment with height <= 8 then the width must be <= height. Here
        we look for such a limitation.
      */
-    fMinRenderTargetHeight = GR_INVAL_GLINT;
-    GrGLint maxRenderSize;
-    GR_GL_GetIntegerv(GR_GL_MAX_RENDERBUFFER_SIZE, &maxRenderSize);
-    // fbo_test creates FBOs with texture bound to the color attachment
-    maxRenderSize = GrMin(maxRenderSize, fMaxTextureDimension);
-
     if (gPrintStartupSpew) {
         GrPrintf("Small height FBO texture experiments\n");
     }
-
+    fMinRenderTargetHeight = GR_INVAL_GLINT;
     for (GrGLuint i = 1; i <= 256; fNPOTRenderTargetSupport ? ++i : i *= 2) {
-        GrGLuint w = maxRenderSize;
+        GrGLuint w = fMaxRenderTargetSize;
         GrGLuint h = i;
         if (fbo_test(w, h)) {
             if (gPrintStartupSpew) {
@@ -475,10 +473,10 @@
     if (gPrintStartupSpew) {
         GrPrintf("Small width FBO texture experiments\n");
     }
-    fMinRenderTargetWidth = GR_MAX_GLUINT;
+    fMinRenderTargetWidth = GR_INVAL_GLINT;
     for (GrGLuint i = 1; i <= 256; fNPOTRenderTargetSupport ? i *= 2 : ++i) {
         GrGLuint w = i;
-        GrGLuint h = maxRenderSize;
+        GrGLuint h = fMaxRenderTargetSize;
         if (fbo_test(w, h)) {
             if (gPrintStartupSpew) {
                 GrPrintf("\t[%d, %d]: PASSED\n", w, h);
@@ -792,9 +790,17 @@
                                         glDesc.fAllocWidth);
         glDesc.fAllocHeight = GrMax<int>(fMinRenderTargetHeight,
                                          glDesc.fAllocHeight);
+        if ((int)glDesc.fAllocWidth > fMaxRenderTargetSize ||
+            (int)glDesc.fAllocHeight > fMaxRenderTargetSize) {
+            return return_null_texture();
+        }
     } else if (!this->npotTextureSupport()) {
         glDesc.fAllocWidth  = GrNextPow2(desc.fWidth);
         glDesc.fAllocHeight = GrNextPow2(desc.fHeight);
+        if ((int)glDesc.fAllocWidth > fMaxTextureSize ||
+            (int)glDesc.fAllocHeight > fMaxTextureSize) {
+            return return_null_texture();
+        }
     }
 
     GR_GL(BindTexture(GR_GL_TEXTURE_2D, glDesc.fTextureID));
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index ae8ab6c..d1cb5b9 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -914,9 +914,9 @@
         sampler->setFilter(GrSamplerState::kNearest_Filter);
     }
 
-    const int maxTextureDim = fContext->getMaxTextureDimension();
-    if (bitmap.getTexture() || (bitmap.width() <= maxTextureDim &&
-                                bitmap.height() <= maxTextureDim)) {
+    const int maxTextureSize = fContext->getMaxTextureSize();
+    if (bitmap.getTexture() || (bitmap.width() <= maxTextureSize &&
+                                bitmap.height() <= maxTextureSize)) {
         // take the fast case
         this->internalDrawBitmap(draw, bitmap, srcRect, m, &grPaint);
         return;
@@ -941,13 +941,13 @@
         clipRect.offset(DX, DY);
     }
 
-    int nx = bitmap.width() / maxTextureDim;
-    int ny = bitmap.height() / maxTextureDim;
+    int nx = bitmap.width() / maxTextureSize;
+    int ny = bitmap.height() / maxTextureSize;
     for (int x = 0; x <= nx; x++) {
         for (int y = 0; y <= ny; y++) {
             SkIRect tileR;
-            tileR.set(x * maxTextureDim, y * maxTextureDim,
-                      (x + 1) * maxTextureDim, (y + 1) * maxTextureDim);
+            tileR.set(x * maxTextureSize, y * maxTextureSize,
+                      (x + 1) * maxTextureSize, (y + 1) * maxTextureSize);
             if (!SkIRect::Intersects(tileR, clipRect)) {
                 continue;
             }
@@ -986,8 +986,8 @@
                                      const SkIRect& srcRect,
                                      const SkMatrix& m,
                                      GrPaint* grPaint) {
-    SkASSERT(bitmap.width() <= fContext->getMaxTextureDimension() &&
-             bitmap.height() <= fContext->getMaxTextureDimension());
+    SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
+             bitmap.height() <= fContext->getMaxTextureSize());
 
     SkAutoLockPixels alp(bitmap);
     if (!bitmap.getTexture() && !bitmap.readyToDraw()) {