Put caps in a struct, move up to GrDrawTarget

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



git-svn-id: http://skia.googlecode.com/svn/trunk@2314 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrAAHairLinePathRenderer.cpp b/gpu/src/GrAAHairLinePathRenderer.cpp
index 8349fa7..4d05a6b 100644
--- a/gpu/src/GrAAHairLinePathRenderer.cpp
+++ b/gpu/src/GrAAHairLinePathRenderer.cpp
@@ -87,7 +87,7 @@
 }
 
 bool GrAAHairLinePathRenderer::CanBeUsed(const GrContext* context) {
-    return context->getGpu()->supportsShaderDerivatives();
+    return context->getGpu()->getCaps().fShaderDerivativeSupport;
 
 }
 
diff --git a/gpu/src/GrBufferAllocPool.cpp b/gpu/src/GrBufferAllocPool.cpp
index 667e437..715d4a9 100644
--- a/gpu/src/GrBufferAllocPool.cpp
+++ b/gpu/src/GrBufferAllocPool.cpp
@@ -91,7 +91,7 @@
         fFirstPreallocBuffer = (fFirstPreallocBuffer + fPreallocBuffersInUse) %
                                fPreallocBuffers.count();
     }
-    fCpuData.reset(fGpu->supportsBufferLocking() ? 0 : fMinBlockSize);
+    fCpuData.reset(fGpu->getCaps().fBufferLockSupport ? 0 : fMinBlockSize);
     GrAssert(0 == fPreallocBuffersInUse);
     VALIDATE();
 }
@@ -276,7 +276,7 @@
 
     GrAssert(NULL == fBufferPtr);
 
-    if (fGpu->supportsBufferLocking() &&
+    if (fGpu->getCaps().fBufferLockSupport &&
         size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
         (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
         fBufferPtr = block.fBuffer->lock();
@@ -318,7 +318,7 @@
     GrAssert(flushSize <= buffer->sizeInBytes());
 
     bool updated = false;
-    if (fGpu->supportsBufferLocking() &&
+    if (fGpu->getCaps().fBufferLockSupport &&
         flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
         void* data = buffer->lock();
         if (NULL != data) {
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 6de3169..ee38d72 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -166,13 +166,13 @@
     // we assume we only need 16 bits of width and height
     // assert that texture creation will fail anyway if this assumption
     // would cause key collisions.
-    GrAssert(gpu->maxTextureSize() <= SK_MaxU16);
+    GrAssert(gpu->getCaps().fMaxTextureSize <= SK_MaxU16);
     v[0] = clientKey & 0xffffffffUL;
     v[1] = (clientKey >> 32) & 0xffffffffUL;
     v[2] = width | (height << 16);
 
     v[3] = 0;
-    if (!gpu->npotTextureTileSupport()) {
+    if (!gpu->getCaps().fNPOTTextureTileSupport) {
         bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
 
         bool tiled = (sampler.getWrapX() != GrSamplerState::kClamp_WrapMode) ||
@@ -310,10 +310,12 @@
         rtDesc.fFlags =  rtDesc.fFlags |
                          kRenderTarget_GrTextureFlagBit |
                          kNoStencil_GrTextureFlagBit;
-        rtDesc.fWidth  = GrNextPow2(GrMax<int>(desc.fWidth,
-                                               fGpu->minRenderTargetWidth()));
-        rtDesc.fHeight = GrNextPow2(GrMax<int>(desc.fHeight,
-                                               fGpu->minRenderTargetHeight()));
+        rtDesc.fWidth  =
+            GrNextPow2(GrMax<int>(desc.fWidth,
+                                  fGpu->getCaps().fMinRenderTargetWidth));
+        rtDesc.fHeight =
+            GrNextPow2(GrMax<int>(desc.fHeight,
+                                  fGpu->getCaps().fMinRenderTargetHeight));
 
         GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);
 
@@ -510,11 +512,11 @@
 }
 
 int GrContext::getMaxTextureSize() const {
-    return fGpu->maxTextureSize();
+    return fGpu->getCaps().fMaxTextureSize;
 }
 
 int GrContext::getMaxRenderTargetSize() const {
-    return fGpu->maxRenderTargetSize();
+    return fGpu->getCaps().fMaxRenderTargetSize;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -541,21 +543,21 @@
 
 bool GrContext::supportsIndex8PixelConfig(const GrSamplerState& sampler,
                                           int width, int height) const {
-    if (!fGpu->supports8BitPalette()) {
+    const GrDrawTarget::Caps& caps = fGpu->getCaps();
+    if (!caps.f8BitPaletteSupport) {
         return false;
     }
 
-
     bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
 
     if (!isPow2) {
-        if (!fGpu->npotTextureSupport()) {
+        if (!caps.fNPOTTextureSupport) {
             return false;
         }
 
         bool tiled = sampler.getWrapX() != GrSamplerState::kClamp_WrapMode ||
                      sampler.getWrapY() != GrSamplerState::kClamp_WrapMode;
-        if (tiled && !fGpu->npotTextureTileSupport()) {
+        if (tiled && !caps.fNPOTTextureTileSupport) {
             return false;
         }
     }
@@ -659,7 +661,7 @@
     // Line primitves are always rasterized as 1 pixel wide.
     // Super-sampling would make them too thin but MSAA would be OK.
     if (isHairLines &&
-        (!PREFER_MSAA_OFFSCREEN_AA || !fGpu->supportsFullsceneAA())) {
+        (!PREFER_MSAA_OFFSCREEN_AA || !fGpu->getCaps().fFSAASupport)) {
         return false;
     }
     if (target->getRenderTarget()->isMultisampled()) {
@@ -708,12 +710,12 @@
 
     desc.fFormat = kRGBA_8888_GrPixelConfig;
 
-    if (PREFER_MSAA_OFFSCREEN_AA && fGpu->supportsFullsceneAA()) {
+    if (PREFER_MSAA_OFFSCREEN_AA && fGpu->getCaps().fFSAASupport) {
         record->fDownsample = OffscreenRecord::kFSAA_Downsample;
         record->fScale = 1;
         desc.fAALevel = kMed_GrAALevel;
     } else {
-        record->fDownsample = (fGpu->supportsShaders()) ?
+        record->fDownsample = fGpu->getCaps().fShaderSupport ?
                                 OffscreenRecord::k4x4SinglePass_Downsample :
                                 OffscreenRecord::k4x4TwoPass_Downsample;
         record->fScale = OFFSCREEN_SSAA_SCALE;
@@ -1519,7 +1521,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool GrContext::supportsShaders() const {
-    return fGpu->supportsShaders();
+    return fGpu->getCaps().fShaderSupport;
 }
 
 void GrContext::flush(int flagsBitfield) {
@@ -1773,8 +1775,8 @@
     fAAFillRectIndexBuffer = NULL;
     fAAStrokeRectIndexBuffer = NULL;
     
-    int gpuMaxOffscreen = fGpu->maxRenderTargetSize();
-    if (!PREFER_MSAA_OFFSCREEN_AA || !fGpu->supportsFullsceneAA()) {
+    int gpuMaxOffscreen = gpu->getCaps().fMaxRenderTargetSize;
+    if (!PREFER_MSAA_OFFSCREEN_AA || !gpu->getCaps().fFSAASupport) {
         gpuMaxOffscreen /= OFFSCREEN_SSAA_SCALE;
     }
     fMaxOffscreenAASize = GrMin(GR_MAX_OFFSCREEN_AA_SIZE, gpuMaxOffscreen);
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 51cff6d..bfcc07d 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -1041,3 +1041,23 @@
     fIndices = NULL;
 }
 
+void GrDrawTarget::Caps::print() const {
+    static const char* gNY[] = {"NO", "YES"};
+    GrPrintf("8 Bit Palette Support       : %s\n", gNY[f8BitPaletteSupport]);
+    GrPrintf("NPOT Texture Support        : %s\n", gNY[fNPOTTextureSupport]);
+    GrPrintf("NPOT Texture Tile Support   : %s\n", gNY[fNPOTTextureTileSupport]);
+    GrPrintf("NPOT Render Target Support  : %s\n", gNY[fNPOTRenderTargetSupport]);
+    GrPrintf("Two Sided Stencil Support   : %s\n", gNY[fTwoSidedStencilSupport]);
+    GrPrintf("Stencil Wrap Ops  Support   : %s\n", gNY[fStencilWrapOpsSupport]);
+    GrPrintf("HW AA Lines Support         : %s\n", gNY[fHWAALineSupport]);
+    GrPrintf("Shader Support              : %s\n", gNY[fShaderSupport]);
+    GrPrintf("Shader Derivative Support   : %s\n", gNY[fShaderDerivativeSupport]);
+    GrPrintf("FSAA Support                : %s\n", gNY[fFSAASupport]);
+    GrPrintf("Dual Source Blending Support: %s\n", gNY[fDualSourceBlendingSupport]);
+    GrPrintf("Buffer Lock Support         : %s\n", gNY[fBufferLockSupport]);
+    GrPrintf("Min Render Target Width     : %d\n", fMinRenderTargetWidth);
+    GrPrintf("Min Render Target Height    : %d\n", fMinRenderTargetHeight);
+    GrPrintf("Max Texture Size            : %d\n", fMaxTextureSize);
+    GrPrintf("Max Render Target Size      : %d\n", fMaxRenderTargetSize);
+}
+
diff --git a/gpu/src/GrDrawTarget.h b/gpu/src/GrDrawTarget.h
index 1d61a18..a57ff81 100644
--- a/gpu/src/GrDrawTarget.h
+++ b/gpu/src/GrDrawTarget.h
@@ -30,6 +30,35 @@
 class GrDrawTarget : public GrRefCnt {
 public:
     /**
+     * Represents the draw target capabilities.
+     */
+    struct Caps {
+        Caps() { memset(this, 0, sizeof(Caps)); }
+        Caps(const Caps& c) { *this = c; }
+        Caps& operator= (const Caps& c) {
+            memcpy(this, &c, sizeof(Caps));
+            return *this;
+        }
+        void print() const;
+        bool f8BitPaletteSupport        : 1;
+        bool fNPOTTextureSupport        : 1;
+        bool fNPOTTextureTileSupport    : 1;
+        bool fNPOTRenderTargetSupport   : 1;
+        bool fTwoSidedStencilSupport    : 1;
+        bool fStencilWrapOpsSupport     : 1;
+        bool fHWAALineSupport           : 1;
+        bool fShaderSupport             : 1;
+        bool fShaderDerivativeSupport   : 1;
+        bool fFSAASupport               : 1;
+        bool fDualSourceBlendingSupport : 1;
+        bool fBufferLockSupport         : 1;
+        int fMinRenderTargetWidth;
+        int fMinRenderTargetHeight;
+        int fMaxRenderTargetSize;
+        int fMaxTextureSize;
+    };
+
+    /**
      * Number of texture stages. Each stage takes as input a color and
      * 2D texture coordinates. The color input to the first enabled stage is the
      * per-vertex color or the constant color (setColor/setAlpha) if there are
@@ -48,7 +77,6 @@
         kMaxTexCoords = kNumStages
     };
 
-
     /**
      * The absolute maximum number of edges that may be specified for
      * a single draw call when performing edge antialiasing.  This is used for
@@ -197,6 +225,11 @@
     virtual ~GrDrawTarget();
 
     /**
+     * Gets the capabilities of the draw target.
+     */
+    const Caps& getCaps() const { return fCaps; }
+
+    /**
      * Sets the current clip to the region specified by clip. All draws will be
      * clipped against this clip if kClip_StateBit is enabled.
      *
@@ -1307,6 +1340,8 @@
 
     DrState fCurrDrawState;
 
+    Caps fCaps;
+
 private:
     // called when setting a new vert/idx source to unref prev vb/ib
     void releasePreviousVertexSource();
diff --git a/gpu/src/GrGLIndexBuffer.cpp b/gpu/src/GrGLIndexBuffer.cpp
index 2791527..084a4c6 100644
--- a/gpu/src/GrGLIndexBuffer.cpp
+++ b/gpu/src/GrGLIndexBuffer.cpp
@@ -51,7 +51,7 @@
 void* GrGLIndexBuffer::lock() {
     GrAssert(fBufferID);
     GrAssert(!isLocked());
-    if (GPUGL->supportsBufferLocking()) {
+    if (this->getGpu()->getCaps().fBufferLockSupport) {
         this->bind();
         // Let driver know it can discard the old data
         GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
@@ -76,7 +76,7 @@
 void GrGLIndexBuffer::unlock() {
     GrAssert(fBufferID);
     GrAssert(isLocked());
-    GrAssert(GPUGL->supportsBufferLocking());
+    GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
 
     this->bind();
     GL_CALL(UnmapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER));
@@ -85,7 +85,7 @@
 
 bool GrGLIndexBuffer::isLocked() const {
 #if GR_DEBUG
-    if (this->isValid() && GPUGL->supportsBufferLocking()) {
+    if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
         this->bind();
         GrGLint mapped;
         GL_CALL(GetBufferParameteriv(GR_GL_ELEMENT_ARRAY_BUFFER,
diff --git a/gpu/src/GrGLVertexBuffer.cpp b/gpu/src/GrGLVertexBuffer.cpp
index 2d5aee0..c542602 100644
--- a/gpu/src/GrGLVertexBuffer.cpp
+++ b/gpu/src/GrGLVertexBuffer.cpp
@@ -50,7 +50,7 @@
 void* GrGLVertexBuffer::lock() {
     GrAssert(fBufferID);
     GrAssert(!isLocked());
-    if (GPUGL->supportsBufferLocking()) {
+    if (this->getGpu()->getCaps().fBufferLockSupport) {
         this->bind();
         // Let driver know it can discard the old data
         GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, this->sizeInBytes(), NULL,
@@ -72,7 +72,7 @@
 
     GrAssert(fBufferID);
     GrAssert(isLocked());
-    GrAssert(GPUGL->supportsBufferLocking());
+    GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
 
     this->bind();
     GL_CALL(UnmapBuffer(GR_GL_ARRAY_BUFFER));
@@ -82,7 +82,7 @@
 bool GrGLVertexBuffer::isLocked() const {
     GrAssert(!this->isValid() || fBufferID);
 #if GR_DEBUG
-    if (this->isValid() && GPUGL->supportsBufferLocking()) {
+    if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
         GrGLint mapped;
         this->bind();
         GL_CALL(GetBufferParameteriv(GR_GL_ARRAY_BUFFER, 
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index b338d05..91e659d 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -31,8 +31,7 @@
 #define DEBUG_INVAL_START_IDX -1
 
 GrGpu::GrGpu()
-    : f8bitPaletteSupport(false)
-    , fContext(NULL)
+    : fContext(NULL)
     , fVertexPool(NULL)
     , fIndexPool(NULL)
     , fVertexPoolUseCnt(0)
diff --git a/gpu/src/GrGpu.h b/gpu/src/GrGpu.h
index e970554..9ecb4b0 100644
--- a/gpu/src/GrGpu.h
+++ b/gpu/src/GrGpu.h
@@ -54,6 +54,7 @@
 class GrGpu : public GrDrawTarget {
 
 public:
+
     /**
      * Additional blend coeffecients for dual source blending, not exposed
      * through GrPaint/GrContext.
@@ -147,118 +148,6 @@
     GrIndexBuffer* createIndexBuffer(uint32_t size, bool dynamic);
 
     /**
-     * Are 8 bit paletted textures supported.
-     *
-     * @return    true if 8bit palette textures are supported, false otherwise
-     */
-    bool supports8BitPalette() const { return f8bitPaletteSupport; }
-
-    /**
-     * returns true if two sided stenciling is supported. If false then only
-     * the front face values of the GrStencilSettings
-     * @return    true if only a single stencil pass is needed.
-     */
-    bool supportsTwoSidedStencil() const
-                                        { return fTwoSidedStencilSupport; }
-
-    /**
-     * returns true if stencil wrap is supported. If false then
-     * kIncWrap_StencilOp and kDecWrap_StencilOp are treated as
-     * kIncClamp_StencilOp and kDecClamp_StencilOp, respectively.
-     * @return    true if stencil wrap ops are supported.
-     */
-    bool supportsStencilWrapOps() const
-                                        { return fStencilWrapOpsSupport; }
-
-    /**
-     * Checks whether locking vertex and index buffers is supported.
-     *
-     * @return true if locking is supported.
-     */
-    bool supportsBufferLocking() const { return fBufferLockSupport; }
-
-    /**
-     * Does the 3D API support anti-aliased lines. If so then line primitive
-     * types will use this functionality when the AA state flag is set.
-     */
-    bool supportsHWAALines() const { return fAALineSupport; }
-
-    /**
-     * Are shaders supported.
-     */
-    bool supportsShaders() const { return fShaderSupport; }
-
-    /**
-     * Are derivative instructions supported in fragment shaders
-     */
-    bool supportsShaderDerivatives() const { return fShaderDerivativeSupport; }
-
-    /**
-     * Does the subclass support GrSamplerState::k4x4Downsample_Filter
-     */
-    bool supports4x4DownsampleFilter() const { return f4X4DownsampleFilterSupport; }
-
-    /**
-     * Does this instance support dual-source blending? Required for proper
-     * blending with partial coverage with certain blend modes (dst coeff is
-     * not 1, ISA, or ISC)
-     */
-    bool supportsDualSourceBlending() const {
-        return fDualSourceBlendingSupport;
-    }
-
-    /**
-     * Gets the minimum width of a render target. If a texture/rt is created
-     * with a width less than this size the GrGpu object will clamp it to this
-     * value.
-     */
-    int minRenderTargetWidth() const { return fMinRenderTargetWidth; }
-
-    /**
-     * Gets the minimum width of a render target. If a texture/rt is created
-     * with a height less than this size the GrGpu object will clamp it to this
-     * value.
-     */
-    int minRenderTargetHeight() const  { return fMinRenderTargetHeight; }
-    
-    /**
-     * Reports whether full scene anti-aliasing is supported.
-     */
-    bool supportsFullsceneAA() const { return fFSAASupport; }
-
-    /**
-     * Returns true if NPOT textures can be created
-     *
-     * @return    true if NPOT textures can be created
-     */
-    bool npotTextureSupport() const { return fNPOTTextureSupport; }
-
-    /**
-     * Returns true if NPOT textures can be repeat/mirror tiled.
-     *
-     * @return    true if NPOT textures can be tiled
-     */
-    bool npotTextureTileSupport() const { return fNPOTTextureTileSupport; }
-
-    /**
-     * Returns true if a NPOT texture can be a rendertarget
-     *
-     * @return    the true if NPOT texture/rendertarget can be created.
-     */
-    bool npotRenderTargetSupport() const { return fNPOTRenderTargetSupport; }
-
-    /**
-     * 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; }
-
-    virtual void clear(const GrIRect* rect, GrColor color);
-
-    /**
      * Returns an index buffer that can be used to render quads.
      * Six indices per quad: 0, 1, 2, 0, 2, 3, etc.
      * The max number of quads can be queried using GrIndexBuffer::maxQuads().
@@ -331,6 +220,7 @@
 
     // GrDrawTarget overrides
     virtual bool willUseHWAALines() const;
+    virtual void clear(const GrIRect* rect, GrColor color);
 
 protected:
     enum PrivateStateBits {
@@ -363,31 +253,6 @@
     // and the client isn't using the stencil test.
     static const GrStencilSettings gClipStencilSettings;
 
-    // defaults to false, subclass can set true to support palleted textures
-    bool f8bitPaletteSupport;
-
-    // set by subclass
-    bool fNPOTTextureSupport;
-    bool fNPOTTextureTileSupport;
-    bool fNPOTRenderTargetSupport;
-    bool fTwoSidedStencilSupport;
-    bool fStencilWrapOpsSupport;
-    bool fAALineSupport;
-    bool fShaderSupport;
-    bool fShaderDerivativeSupport;
-    bool fFSAASupport;
-    bool f4X4DownsampleFilterSupport; // supports GrSamplerState::k4x4Downsample_Filter
-    bool fDualSourceBlendingSupport;
-
-    // set by subclass to true if index and vertex buffers can be locked, false
-    // otherwise.
-    bool fBufferLockSupport;
-
-    // set by subclass
-    int fMinRenderTargetWidth;
-    int fMinRenderTargetHeight;
-    int fMaxRenderTargetSize;
-    int fMaxTextureSize;
 
     GrGpuStats fStats;
 
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 9906037..f1d6f8a 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -257,9 +257,9 @@
     return minRenderTargetWidth;
 }
 
+GrGpuGL::GrGpuGL(const GrGLInterface* gl, GrGLBinding glBinding) {
 
-GrGpuGL::GrGpuGL(const GrGLInterface* gl, GrGLBinding glBinding) 
-    : fStencilFormats(8) {
+    fPrintedCaps = false;
 
     gl->ref();
     fGL = gl;
@@ -303,241 +303,7 @@
 
     this->resetDirtyFlags();
 
-    GrGLint maxTextureUnits;
-    // check FS and fixed-function texture unit limits
-    // we only use textures in the fragment stage currently.
-    // checks are > to make sure we have a spare unit.
-    if (kES1_GrGLBinding != this->glBinding()) {
-        GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
-        GrAssert(maxTextureUnits > kNumStages);
-    }
-    if (kES2_GrGLBinding != this->glBinding()) {
-        GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
-        GrAssert(maxTextureUnits > kNumStages);
-    }
-    if (kES2_GrGLBinding == this->glBinding()) {
-        GR_GL_GetIntegerv(gl, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
-                          &fMaxFragmentUniformVectors);
-    } else if (kDesktop_GrGLBinding != this->glBinding()) {
-        GrGLint max;
-        GR_GL_GetIntegerv(gl, GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max);
-        fMaxFragmentUniformVectors = max / 4;
-    } else {
-        fMaxFragmentUniformVectors = 16;
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Check for supported features.
-
-    this->setupStencilFormats();
-
-    GrGLint numFormats;
-    GR_GL_GetIntegerv(gl, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats);
-    SkAutoSTMalloc<10, GrGLint> formats(numFormats);
-    GR_GL_GetIntegerv(gl, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats);
-    for (int i = 0; i < numFormats; ++i) {
-        if (formats[i] == GR_GL_PALETTE8_RGBA8) {
-            f8bitPaletteSupport = true;
-            break;
-        }
-    }
-
-    if (gPrintStartupSpew) {
-        GrPrintf("Palette8 support: %s\n", (f8bitPaletteSupport ? "YES" : "NO"));
-    }
-
-    GR_STATIC_ASSERT(0 == kNone_GrAALevel);
-    GR_STATIC_ASSERT(1 == kLow_GrAALevel);
-    GR_STATIC_ASSERT(2 == kMed_GrAALevel);
-    GR_STATIC_ASSERT(3 == kHigh_GrAALevel);
-
-    memset(fAASamples, 0, sizeof(fAASamples));
-    fMSFBOType = kNone_MSFBO;
-    if (kDesktop_GrGLBinding != this->glBinding()) {
-       if (this->hasExtension("GL_CHROMIUM_framebuffer_multisample")) {
-           // chrome's extension is equivalent to the EXT msaa
-           // and fbo_blit extensions.
-            fMSFBOType = kDesktopEXT_MSFBO;
-       } else if (this->hasExtension("GL_APPLE_framebuffer_multisample")) {
-            fMSFBOType = kAppleES_MSFBO;
-        }
-    } else {
-        if ((fGLVersion >= GR_GL_VER(3,0)) || this->hasExtension("GL_ARB_framebuffer_object")) {
-            fMSFBOType = kDesktopARB_MSFBO;
-        } else if (this->hasExtension("GL_EXT_framebuffer_multisample") &&
-                   this->hasExtension("GL_EXT_framebuffer_blit")) {
-            fMSFBOType = kDesktopEXT_MSFBO;
-        }
-    }
-    if (gPrintStartupSpew) {
-        switch (fMSFBOType) {
-            case kNone_MSFBO:
-                GrPrintf("MSAA Support: NONE\n");
-                break;
-            case kDesktopARB_MSFBO:
-                GrPrintf("MSAA Support: DESKTOP ARB.\n");
-                break;
-            case kDesktopEXT_MSFBO:
-                GrPrintf("MSAA Support: DESKTOP EXT.\n");
-                break;
-            case kAppleES_MSFBO:
-                GrPrintf("MSAA Support: APPLE ES.\n");
-                break;
-        }
-    }
-
-    if (kNone_MSFBO != fMSFBOType) {
-        GrGLint maxSamples;
-        GR_GL_GetIntegerv(gl, GR_GL_MAX_SAMPLES, &maxSamples);
-        if (maxSamples > 1 ) {
-            fAASamples[kNone_GrAALevel] = 0;
-            fAASamples[kLow_GrAALevel] = GrMax(2,
-                                               GrFixedFloorToInt((GR_FixedHalf) *
-                                               maxSamples));
-            fAASamples[kMed_GrAALevel] = GrMax(2,
-                                               GrFixedFloorToInt(((GR_Fixed1*3)/4) *
-                                               maxSamples));
-            fAASamples[kHigh_GrAALevel] = maxSamples;
-        }
-        if (gPrintStartupSpew) {
-            GrPrintf("\tMax Samples: %d\n", maxSamples);
-        }
-    }
-    fFSAASupport = fAASamples[kHigh_GrAALevel] > 0;
-
-    if (kDesktop_GrGLBinding == this->glBinding()) {
-        fHasStencilWrap = (fGLVersion >= GR_GL_VER(1,4)) ||
-                          this->hasExtension("GL_EXT_stencil_wrap");
-    } else {
-        fHasStencilWrap = (fGLVersion >= GR_GL_VER(2,0)) || 
-                          this->hasExtension("GL_OES_stencil_wrap");
-    }
-    if (gPrintStartupSpew) {
-        GrPrintf("Stencil Wrap: %s\n", (fHasStencilWrap ? "YES" : "NO"));
-    }
-
-    if (kDesktop_GrGLBinding == this->glBinding()) {
-        // we could also look for GL_ATI_separate_stencil extension or
-        // GL_EXT_stencil_two_side but they use different function signatures
-        // than GL2.0+ (and than each other).
-        fTwoSidedStencilSupport = (fGLVersion >= GR_GL_VER(2,0));
-        // supported on GL 1.4 and higher or by extension
-        fStencilWrapOpsSupport = (fGLVersion >= GR_GL_VER(1,4)) ||
-                                  this->hasExtension("GL_EXT_stencil_wrap");
-    } else {
-        // ES 2 has two sided stencil but 1.1 doesn't. There doesn't seem to be
-        // an ES1 extension.
-        fTwoSidedStencilSupport = (fGLVersion >= GR_GL_VER(2,0));
-        // stencil wrap support is in ES2, ES1 requires extension.
-        fStencilWrapOpsSupport = (fGLVersion >= GR_GL_VER(2,0)) ||
-                                 this->hasExtension("GL_OES_stencil_wrap");
-    }
-    if (gPrintStartupSpew) {
-        GrPrintf("Stencil Caps: TwoSide: %s, Wrap: %s\n",
-                (fTwoSidedStencilSupport ? "YES" : "NO"),
-                (fStencilWrapOpsSupport ? "YES" : "NO"));
-    }
-
-    if (kDesktop_GrGLBinding == this->glBinding()) {
-        fRGBA8Renderbuffer = true;
-    } else {
-        fRGBA8Renderbuffer = this->hasExtension("GL_OES_rgb8_rgba8");
-    }
-    if (gPrintStartupSpew) {
-        GrPrintf("RGBA Renderbuffer: %s\n", (fRGBA8Renderbuffer ? "YES" : "NO"));
-    }
-
-
-    if (kDesktop_GrGLBinding != this->glBinding()) {
-        if (GR_GL_32BPP_COLOR_FORMAT == GR_GL_BGRA) {
-            GrAssert(this->hasExtension("GL_EXT_texture_format_BGRA8888"));
-        }
-    }
-
-    if (kDesktop_GrGLBinding == this->glBinding()) {
-        fBufferLockSupport = true; // we require VBO support and the desktop VBO
-                                   // extension includes glMapBuffer.
-    } else {
-        fBufferLockSupport = this->hasExtension("GL_OES_mapbuffer");
-    }
-
-    if (gPrintStartupSpew) {
-        GrPrintf("Map Buffer: %s\n", (fBufferLockSupport ? "YES" : "NO"));
-    }
-
-    if (kDesktop_GrGLBinding == this->glBinding()) {
-        if (fGLVersion >= GR_GL_VER(2,0) || 
-            this->hasExtension("GL_ARB_texture_non_power_of_two")) {
-            fNPOTTextureTileSupport = true;
-            fNPOTTextureSupport = true;
-        } else {
-            fNPOTTextureTileSupport = false;
-            fNPOTTextureSupport = false;
-        }
-    } else {
-        if (fGLVersion >= GR_GL_VER(2,0)) {
-            fNPOTTextureSupport = true;
-            fNPOTTextureTileSupport = this->hasExtension("GL_OES_texture_npot");
-        } else {
-            fNPOTTextureSupport =
-                        this->hasExtension("GL_APPLE_texture_2D_limited_npot");
-            fNPOTTextureTileSupport = false;
-        }
-    }
-
-    fAALineSupport = (kDesktop_GrGLBinding == this->glBinding());
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Experiments to determine limitations that can't be queried.
-    // TODO: Make these a preprocess that generate some compile time constants.
-    // TODO: probe once at startup, rather than once per context creation.
-
-    int expectNPOTTargets = gl->fNPOTRenderTargetSupport;
-    if (expectNPOTTargets == kProbe_GrGLCapability) {
-        fNPOTRenderTargetSupport =
-            probe_for_npot_render_target_support(gl, fNPOTTextureSupport);
-    } else {
-        GrAssert(expectNPOTTargets == 0 || expectNPOTTargets == 1);
-        fNPOTRenderTargetSupport = static_cast<bool>(expectNPOTTargets);
-    }
-
-    if (gPrintStartupSpew) {
-        if (fNPOTTextureSupport) {
-            GrPrintf("NPOT textures supported\n");
-            if (fNPOTTextureTileSupport) {
-                GrPrintf("NPOT texture tiling supported\n");
-            } else {
-                GrPrintf("NPOT texture tiling NOT supported\n");
-            }
-            if (fNPOTRenderTargetSupport) {
-                GrPrintf("NPOT render targets supported\n");
-            } else {
-                GrPrintf("NPOT render targets NOT supported\n");
-            }
-        } else {
-            GrPrintf("NPOT textures NOT supported\n");
-        }
-    }
-
-    GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_SIZE, &fMaxTextureSize);
-    GR_GL_GetIntegerv(gl, 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);
-
-    fMinRenderTargetHeight = gl->fMinRenderTargetHeight;
-    if (fMinRenderTargetHeight == kProbe_GrGLCapability) {
-        fMinRenderTargetHeight =
-            probe_for_min_render_target_height(gl,fNPOTRenderTargetSupport,
-                                               fMaxRenderTargetSize);
-    }
-
-    fMinRenderTargetWidth = gl->fMinRenderTargetWidth;
-    if (fMinRenderTargetWidth == kProbe_GrGLCapability) {
-        fMinRenderTargetWidth =
-            probe_for_min_render_target_width(gl, fNPOTRenderTargetSupport,
-                                              fMaxRenderTargetSize);
-    }
+    this->initCaps();
 
     fLastSuccessfulStencilFmtIdx = 0;
 }
@@ -549,7 +315,255 @@
     fGL->unref();
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+static const GrGLuint kUnknownBitCount = ~0;
+
+void GrGpuGL::initCaps() {
+    GrGLint maxTextureUnits;
+    // check FS and fixed-function texture unit limits
+    // we only use textures in the fragment stage currently.
+    // checks are > to make sure we have a spare unit.
+    if (kES1_GrGLBinding != this->glBinding()) {
+        GR_GL_GetIntegerv(fGL, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+        GrAssert(maxTextureUnits > kNumStages);
+    }
+    if (kES2_GrGLBinding != this->glBinding()) {
+        GR_GL_GetIntegerv(fGL, GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
+        GrAssert(maxTextureUnits > kNumStages);
+    }
+    if (kES2_GrGLBinding == this->glBinding()) {
+        GR_GL_GetIntegerv(fGL, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
+                          &fGLCaps.fMaxFragmentUniformVectors);
+    } else if (kDesktop_GrGLBinding != this->glBinding()) {
+        GrGLint max;
+        GR_GL_GetIntegerv(fGL, GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max);
+        fGLCaps.fMaxFragmentUniformVectors = max / 4;
+    } else {
+        fGLCaps.fMaxFragmentUniformVectors = 16;
+    }
+
+    GrGLint numFormats;
+    GR_GL_GetIntegerv(fGL, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats);
+    SkAutoSTMalloc<10, GrGLint> formats(numFormats);
+    GR_GL_GetIntegerv(fGL, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats);
+    for (int i = 0; i < numFormats; ++i) {
+        if (formats[i] == GR_GL_PALETTE8_RGBA8) {
+            fCaps.f8BitPaletteSupport = true;
+            break;
+        }
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fCaps.fStencilWrapOpsSupport = (fGLVersion >= GR_GL_VER(1,4)) ||
+                                    this->hasExtension("GL_EXT_stencil_wrap");
+    } else {
+        fCaps.fStencilWrapOpsSupport = (fGLVersion >= GR_GL_VER(2,0)) ||
+                                this->hasExtension("GL_OES_stencil_wrap");
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        // we could also look for GL_ATI_separate_stencil extension or
+        // GL_EXT_stencil_two_side but they use different function signatures
+        // than GL2.0+ (and than each other).
+        fCaps.fTwoSidedStencilSupport = (fGLVersion >= GR_GL_VER(2,0));
+        // supported on GL 1.4 and higher or by extension
+        fCaps.fStencilWrapOpsSupport = (fGLVersion >= GR_GL_VER(1,4)) ||
+                                       this->hasExtension("GL_EXT_stencil_wrap");
+    } else {
+        // ES 2 has two sided stencil but 1.1 doesn't. There doesn't seem to be
+        // an ES1 extension.
+        fCaps.fTwoSidedStencilSupport = (fGLVersion >= GR_GL_VER(2,0));
+        // stencil wrap support is in ES2, ES1 requires extension.
+        fCaps.fStencilWrapOpsSupport = (fGLVersion >= GR_GL_VER(2,0)) ||
+                                       this->hasExtension("GL_OES_stencil_wrap");
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fGLCaps.fRGBA8Renderbuffer = true;
+    } else {
+        fGLCaps.fRGBA8Renderbuffer = this->hasExtension("GL_OES_rgb8_rgba8");
+    }
+
+
+    if (kDesktop_GrGLBinding != this->glBinding()) {
+        if (GR_GL_32BPP_COLOR_FORMAT == GR_GL_BGRA) {
+            GrAssert(this->hasExtension("GL_EXT_texture_format_BGRA8888"));
+        }
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fCaps.fBufferLockSupport = true; // we require VBO support and the desktop VBO
+                                         // extension includes glMapBuffer.
+    } else {
+        fCaps.fBufferLockSupport = this->hasExtension("GL_OES_mapbuffer");
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        if (fGLVersion >= GR_GL_VER(2,0) || 
+            this->hasExtension("GL_ARB_texture_non_power_of_two")) {
+            fCaps.fNPOTTextureTileSupport = true;
+            fCaps.fNPOTTextureSupport = true;
+        } else {
+            fCaps.fNPOTTextureTileSupport = false;
+            fCaps.fNPOTTextureSupport = false;
+        }
+    } else {
+        if (fGLVersion >= GR_GL_VER(2,0)) {
+            fCaps.fNPOTTextureSupport = true;
+            fCaps.fNPOTTextureTileSupport = this->hasExtension("GL_OES_texture_npot");
+        } else {
+            fCaps.fNPOTTextureSupport =
+                        this->hasExtension("GL_APPLE_texture_2D_limited_npot");
+            fCaps.fNPOTTextureTileSupport = false;
+        }
+    }
+
+    fCaps.fHWAALineSupport = (kDesktop_GrGLBinding == this->glBinding());
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Experiments to determine limitations that can't be queried.
+    // TODO: Make these a preprocess that generate some compile time constants.
+    // TODO: probe once at startup, rather than once per context creation.
+
+    int expectNPOTTargets = fGL->fNPOTRenderTargetSupport;
+    if (expectNPOTTargets == kProbe_GrGLCapability) {
+        fCaps.fNPOTRenderTargetSupport =
+            probe_for_npot_render_target_support(fGL, fCaps.fNPOTTextureSupport);
+    } else {
+        GrAssert(expectNPOTTargets == 0 || expectNPOTTargets == 1);
+        fCaps.fNPOTRenderTargetSupport = static_cast<bool>(expectNPOTTargets);
+    }
+
+    GR_GL_GetIntegerv(fGL, GR_GL_MAX_TEXTURE_SIZE, &fCaps.fMaxTextureSize);
+    GR_GL_GetIntegerv(fGL, GR_GL_MAX_RENDERBUFFER_SIZE, &fCaps.fMaxRenderTargetSize);
+    // Our render targets are always created with textures as the color
+    // attachment, hence this min:
+    fCaps.fMaxRenderTargetSize = GrMin(fCaps.fMaxTextureSize, fCaps.fMaxRenderTargetSize);
+
+    fCaps.fMinRenderTargetHeight = fGL->fMinRenderTargetHeight;
+    if (fCaps.fMinRenderTargetHeight == kProbe_GrGLCapability) {
+        fCaps.fMinRenderTargetHeight =
+            probe_for_min_render_target_height(fGL, fCaps.fNPOTRenderTargetSupport,
+                                               fCaps.fMaxRenderTargetSize);
+    }
+
+    fCaps.fMinRenderTargetWidth = fGL->fMinRenderTargetWidth;
+    if (fCaps.fMinRenderTargetWidth == kProbe_GrGLCapability) {
+        fCaps.fMinRenderTargetWidth =
+            probe_for_min_render_target_width(fGL, fCaps.fNPOTRenderTargetSupport,
+                                              fCaps.fMaxRenderTargetSize);
+    }
+
+    this->initFSAASupport();
+    this->initStencilFormats();
+}
+
+void GrGpuGL::initFSAASupport() {
+    // TODO: Get rid of GrAALevel and use # samples directly.
+    GR_STATIC_ASSERT(0 == kNone_GrAALevel);
+    GR_STATIC_ASSERT(1 == kLow_GrAALevel);
+    GR_STATIC_ASSERT(2 == kMed_GrAALevel);
+    GR_STATIC_ASSERT(3 == kHigh_GrAALevel);
+    memset(fGLCaps.fAASamples, 0, sizeof(fGLCaps.fAASamples));
+
+    fGLCaps.fMSFBOType = GLCaps::kNone_MSFBO;
+    if (kDesktop_GrGLBinding != this->glBinding()) {
+       if (this->hasExtension("GL_CHROMIUM_framebuffer_multisample")) {
+           // chrome's extension is equivalent to the EXT msaa
+           // and fbo_blit extensions.
+            fGLCaps.fMSFBOType = GLCaps::kDesktopEXT_MSFBO;
+       } else if (this->hasExtension("GL_APPLE_framebuffer_multisample")) {
+            fGLCaps.fMSFBOType = GLCaps::kAppleES_MSFBO;
+        }
+    } else {
+        if ((fGLVersion >= GR_GL_VER(3,0)) || this->hasExtension("GL_ARB_framebuffer_object")) {
+            fGLCaps.fMSFBOType = GLCaps::kDesktopARB_MSFBO;
+        } else if (this->hasExtension("GL_EXT_framebuffer_multisample") &&
+                   this->hasExtension("GL_EXT_framebuffer_blit")) {
+            fGLCaps.fMSFBOType = GLCaps::kDesktopEXT_MSFBO;
+        }
+    }
+
+    if (GLCaps::kNone_MSFBO != fGLCaps.fMSFBOType) {
+        GrGLint maxSamples;
+        GR_GL_GetIntegerv(fGL, GR_GL_MAX_SAMPLES, &maxSamples);
+        if (maxSamples > 1 ) {
+            fGLCaps.fAASamples[kNone_GrAALevel] = 0;
+            fGLCaps.fAASamples[kLow_GrAALevel] =
+                GrMax(2, GrFixedFloorToInt((GR_FixedHalf) * maxSamples));
+            fGLCaps.fAASamples[kMed_GrAALevel] =
+                GrMax(2, GrFixedFloorToInt(((GR_Fixed1*3)/4) * maxSamples));
+            fGLCaps.fAASamples[kHigh_GrAALevel] = maxSamples;
+        }
+    }
+    fCaps.fFSAASupport = fGLCaps.fAASamples[kHigh_GrAALevel] > 0;
+}
+
+void GrGpuGL::initStencilFormats() {
+
+    // Build up list of legal stencil formats (though perhaps not supported on
+    // the particular gpu/driver) from most preferred to least.
+
+    // these consts are in order of most preferred to least preferred
+    // we don't bother with GL_STENCIL_INDEX1 or GL_DEPTH32F_STENCIL8
+    static const GrGLStencilBuffer::Format
+                  // internal Format      stencil bits      total bits        packed?
+        gS8    = {GR_GL_STENCIL_INDEX8,   8,                8,                false},
+        gS16   = {GR_GL_STENCIL_INDEX16,  16,               16,               false},
+        gD24S8 = {GR_GL_DEPTH24_STENCIL8, 8,                32,               true },
+        gS4    = {GR_GL_STENCIL_INDEX4,   4,                4,                false},
+        gS     = {GR_GL_STENCIL_INDEX,    kUnknownBitCount, kUnknownBitCount, false},
+        gDS    = {GR_GL_DEPTH_STENCIL,    kUnknownBitCount, kUnknownBitCount, true };
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        bool supportsPackedDS = fGLVersion >= GR_GL_VER(3,0) || 
+                                this->hasExtension("GL_EXT_packed_depth_stencil") ||
+                                this->hasExtension("GL_ARB_framebuffer_object");
+
+        // S1 thru S16 formats are in GL 3.0+, EXT_FBO, and ARB_FBO since we
+        // require FBO support we can expect these are legal formats and don't
+        // check. These also all support the unsized GL_STENCIL_INDEX.
+        fGLCaps.fStencilFormats.push_back() = gS8;
+        fGLCaps.fStencilFormats.push_back() = gS16;
+        if (supportsPackedDS) {
+            fGLCaps.fStencilFormats.push_back() = gD24S8;
+        }
+        fGLCaps.fStencilFormats.push_back() = gS4;
+        if (supportsPackedDS) {
+            fGLCaps.fStencilFormats.push_back() = gDS;
+        }
+    } else {
+        // ES2 has STENCIL_INDEX8 without extensions.
+        // ES1 with GL_OES_framebuffer_object (which we require for ES1)
+        // introduces tokens for S1 thu S8 but there are separate extensions
+        // that make them legal (GL_OES_stencil1, ...).
+        // GL_OES_packed_depth_stencil adds DEPTH24_STENCIL8
+        // ES doesn't support using the unsized formats.
+
+        if (fGLVersion >= GR_GL_VER(2,0) ||
+            this->hasExtension("GL_OES_stencil8")) {
+            fGLCaps.fStencilFormats.push_back() = gS8;
+        }
+        //fStencilFormats.push_back() = gS16;
+        if (this->hasExtension("GL_OES_packed_depth_stencil")) {
+            fGLCaps.fStencilFormats.push_back() = gD24S8;
+        }
+        if (this->hasExtension("GL_OES_stencil4")) {
+            fGLCaps.fStencilFormats.push_back() = gS4;
+        }
+        // we require some stencil format.
+        GrAssert(fGLCaps.fStencilFormats.count() > 0);
+    }
+}
+
 void GrGpuGL::resetContext() {
+    if (gPrintStartupSpew && !fPrintedCaps) {
+        fPrintedCaps = true;
+        this->getCaps().print();
+        fGLCaps.print();
+    }
+
     // We detect cases when blending is effectively off
     fHWBlendDisabled = false;
     GL_CALL(Enable(GR_GL_BLEND));
@@ -698,66 +712,6 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-static const GrGLuint kUnknownBitCount = ~0;
-
-void GrGpuGL::setupStencilFormats() {
-
-    // Build up list of legal stencil formats (though perhaps not supported on
-    // the particular gpu/driver) from most preferred to least.
-
-    // these consts are in order of most preferred to least preferred
-    // we don't bother with GL_STENCIL_INDEX1 or GL_DEPTH32F_STENCIL8
-    static const GrGLStencilBuffer::Format
-                  // internal Format      stencil bits      total bits        packed?
-        gS8    = {GR_GL_STENCIL_INDEX8,   8,                8,                false},
-        gS16   = {GR_GL_STENCIL_INDEX16,  16,               16,               false},
-        gD24S8 = {GR_GL_DEPTH24_STENCIL8, 8,                32,               true },
-        gS4    = {GR_GL_STENCIL_INDEX4,   4,                4,                false},
-        gS     = {GR_GL_STENCIL_INDEX,    kUnknownBitCount, kUnknownBitCount, false},
-        gDS    = {GR_GL_DEPTH_STENCIL,    kUnknownBitCount, kUnknownBitCount, true };
-
-    if (kDesktop_GrGLBinding == this->glBinding()) {
-        bool supportsPackedDS = fGLVersion >= GR_GL_VER(3,0) || 
-                                this->hasExtension("GL_EXT_packed_depth_stencil") ||
-                                this->hasExtension("GL_ARB_framebuffer_object");
-
-        // S1 thru S16 formats are in GL 3.0+, EXT_FBO, and ARB_FBO since we
-        // require FBO support we can expect these are legal formats and don't
-        // check. These also all support the unsized GL_STENCIL_INDEX.
-        fStencilFormats.push_back() = gS8;
-        fStencilFormats.push_back() = gS16;
-        if (supportsPackedDS) {
-            fStencilFormats.push_back() = gD24S8;
-        }
-        fStencilFormats.push_back() = gS4;
-        if (supportsPackedDS) {
-            fStencilFormats.push_back() = gDS;
-        }
-    } else {
-        // ES2 has STENCIL_INDEX8 without extensions.
-        // ES1 with GL_OES_framebuffer_object (which we require for ES1)
-        // introduces tokens for S1 thu S8 but there are separate extensions
-        // that make them legal (GL_OES_stencil1, ...).
-        // GL_OES_packed_depth_stencil adds DEPTH24_STENCIL8
-        // ES doesn't support using the unsized formats.
-
-        if (fGLVersion >= GR_GL_VER(2,0) ||
-            this->hasExtension("GL_OES_stencil8")) {
-            fStencilFormats.push_back() = gS8;
-        }
-        //fStencilFormats.push_back() = gS16;
-        if (this->hasExtension("GL_OES_packed_depth_stencil")) {
-            fStencilFormats.push_back() = gD24S8;
-        }
-        if (this->hasExtension("GL_OES_stencil4")) {
-            fStencilFormats.push_back() = gS4;
-        }
-        // we require some stencil format.
-        GrAssert(fStencilFormats.count() > 0);
-    }
-}
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -812,7 +766,7 @@
 
     GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, desc.fUploadByteCount));
     if (kIndex_8_GrPixelConfig == desc.fFormat &&
-        supports8BitPalette()) {
+        this->getCaps().f8BitPaletteSupport) {
         // ES only supports CompressedTexImage2D, not CompressedTexSubimage2D
         GrAssert(desc.fContentWidth == desc.fAllocWidth);
         GrAssert(desc.fContentHeight == desc.fAllocHeight);
@@ -920,7 +874,7 @@
 
     // If we are using multisampling we will create two FBOS. We render
     // to one and then resolve to the texture bound to the other.
-    if (desc->fSampleCnt > 1 && kNone_MSFBO != fMSFBOType) {
+    if (desc->fSampleCnt > 1 && GLCaps::kNone_MSFBO != fGLCaps.fMSFBOType) {
         GL_CALL(GenFramebuffers(1, &desc->fRTFBOID));
         GL_CALL(GenRenderbuffers(1, &desc->fMSColorRenderbufferID));
         if (!desc->fRTFBOID ||
@@ -1032,39 +986,42 @@
         return return_null_texture();
     }
 
+    const Caps& caps = this->getCaps();
+
     // We keep GrRenderTargets in GL's normal orientation so that they
     // can be drawn to by the outside world without the client having
     // to render upside down.
     glTexDesc.fOrientation = renderTarget ? GrGLTexture::kBottomUp_Orientation :
                                             GrGLTexture::kTopDown_Orientation;
 
-    GrAssert(as_size_t(desc.fAALevel) < GR_ARRAY_COUNT(fAASamples));
-    glRTDesc.fSampleCnt = fAASamples[desc.fAALevel];
-    if (kNone_MSFBO == fMSFBOType && desc.fAALevel != kNone_GrAALevel) {
+    GrAssert(as_size_t(desc.fAALevel) < GR_ARRAY_COUNT(fGLCaps.fAASamples));
+    glRTDesc.fSampleCnt = fGLCaps.fAASamples[desc.fAALevel];
+    if (GLCaps::kNone_MSFBO == fGLCaps.fMSFBOType &&
+        desc.fAALevel != kNone_GrAALevel) {
         GrPrintf("AA RT requested but not supported on this platform.");
     }
 
     glTexDesc.fUploadByteCount = GrBytesPerPixel(desc.fFormat);
 
     if (renderTarget) {
-        if (!this->npotRenderTargetSupport()) {
+        if (!caps.fNPOTRenderTargetSupport) {
             glTexDesc.fAllocWidth  = GrNextPow2(desc.fWidth);
             glTexDesc.fAllocHeight = GrNextPow2(desc.fHeight);
         }
 
-        glTexDesc.fAllocWidth = GrMax(fMinRenderTargetWidth,
+        glTexDesc.fAllocWidth = GrMax(caps.fMinRenderTargetWidth,
                                       glTexDesc.fAllocWidth);
-        glTexDesc.fAllocHeight = GrMax(fMinRenderTargetHeight,
+        glTexDesc.fAllocHeight = GrMax(caps.fMinRenderTargetHeight,
                                        glTexDesc.fAllocHeight);
-        if (glTexDesc.fAllocWidth > fMaxRenderTargetSize ||
-            glTexDesc.fAllocHeight > fMaxRenderTargetSize) {
+        if (glTexDesc.fAllocWidth > caps.fMaxRenderTargetSize ||
+            glTexDesc.fAllocHeight > caps.fMaxRenderTargetSize) {
             return return_null_texture();
         }
-    } else if (!this->npotTextureSupport()) {
+    } else if (!caps.fNPOTTextureSupport) {
         glTexDesc.fAllocWidth  = GrNextPow2(desc.fWidth);
         glTexDesc.fAllocHeight = GrNextPow2(desc.fHeight);
-        if (glTexDesc.fAllocWidth > fMaxTextureSize ||
-            glTexDesc.fAllocHeight > fMaxTextureSize) {
+        if (glTexDesc.fAllocWidth > caps.fMaxTextureSize ||
+            glTexDesc.fAllocHeight > caps.fMaxTextureSize) {
             return return_null_texture();
         }
     }
@@ -1156,13 +1113,14 @@
 
     GrGLStencilBuffer* sb = NULL;
 
-    int stencilFmtCnt = fStencilFormats.count();
+    int stencilFmtCnt = fGLCaps.fStencilFormats.count();
     for (int i = 0; i < stencilFmtCnt; ++i) {
         GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, sbID));
         // we start with the last stencil format that succeeded in hopes
         // that we won't go through this loop more than once after the
         // first (painful) stencil creation.
         int sIdx = (i + fLastSuccessfulStencilFmtIdx) % stencilFmtCnt;
+        const GrGLStencilBuffer::Format& sFmt = fGLCaps.fStencilFormats[sIdx];
         // we do this "if" so that we don't call the multisample
         // version on a GL that doesn't have an MSAA extension.
         if (samples > 1) {
@@ -1170,21 +1128,21 @@
                                   RenderbufferStorageMultisample(
                                         GR_GL_RENDERBUFFER,
                                         samples,
-                                        fStencilFormats[sIdx].fInternalFormat,
+                                        sFmt.fInternalFormat,
                                         width,
                                         height));
         } else {
             GR_GL_CALL_NOERRCHECK(this->glInterface(),
                                   RenderbufferStorage(GR_GL_RENDERBUFFER,
-                                        fStencilFormats[sIdx].fInternalFormat,
-                                        width, height));
+                                                      sFmt.fInternalFormat,
+                                                      width, height));
         }
 
         GrGLenum err = GR_GL_GET_ERROR(this->glInterface());
         if (err == GR_GL_NO_ERROR) {
             // After sized formats we attempt an unsized format and take whatever
             // sizes GL gives us. In that case we query for the size.
-            GrGLStencilBuffer::Format format = fStencilFormats[sIdx];
+            GrGLStencilBuffer::Format format = sFmt;
             get_stencil_rb_sizes(this->glInterface(), sbID, &format);
             sb = new GrGLStencilBuffer(this, sbID, width, height, 
                                        samples, format);
@@ -1606,7 +1564,7 @@
 void GrGpuGL::resolveRenderTarget(GrGLRenderTarget* rt) {
 
     if (rt->needsResolve()) {
-        GrAssert(kNone_MSFBO != fMSFBOType);
+        GrAssert(GLCaps::kNone_MSFBO != fGLCaps.fMSFBOType);
         GrAssert(rt->textureFBOID() != rt->renderFBOID());
         GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
                                 rt->renderFBOID()));
@@ -1624,7 +1582,7 @@
         r.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop, 
                         dirtyRect.width(), dirtyRect.height());
 
-        if (kAppleES_MSFBO == fMSFBOType) {
+        if (GLCaps::kAppleES_MSFBO == fGLCaps.fMSFBOType) {
             // Apple's extension uses the scissor as the blit bounds.
             GL_CALL(Enable(GR_GL_SCISSOR_TEST));
             GL_CALL(Scissor(r.fLeft, r.fBottom,
@@ -1633,10 +1591,10 @@
             fHWBounds.fScissorRect.invalidate();
             fHWBounds.fScissorEnabled = true;
         } else {
-            if (kDesktopARB_MSFBO != fMSFBOType) {
+            if (GLCaps::kDesktopARB_MSFBO != fGLCaps.fMSFBOType) {
                 // this respects the scissor during the blit, so disable it.
-                GrAssert(kDesktopEXT_MSFBO == fMSFBOType);
-                flushScissor(NULL);
+                GrAssert(GLCaps::kDesktopEXT_MSFBO == fGLCaps.fMSFBOType);
+                this->flushScissor(NULL);
             }
             int right = r.fLeft + r.fWidth;
             int top = r.fBottom + r.fHeight;
@@ -1716,7 +1674,7 @@
         } else {
             GL_CALL(Enable(GR_GL_STENCIL_TEST));
     #if GR_DEBUG
-            if (!fStencilWrapOpsSupport) {
+            if (!this->getCaps().fStencilWrapOpsSupport) {
                 GrAssert(settings->fFrontPassOp != kIncWrap_StencilOp);
                 GrAssert(settings->fFrontPassOp != kDecWrap_StencilOp);
                 GrAssert(settings->fFrontFailOp != kIncWrap_StencilOp);
@@ -1768,7 +1726,7 @@
                      (unsigned) settings->fBackFailOp < GR_ARRAY_COUNT(grToGLStencilOp));
             GrAssert(settings->fBackPassOp >= 0 &&
                      (unsigned) settings->fBackPassOp < GR_ARRAY_COUNT(grToGLStencilOp));
-            if (fTwoSidedStencilSupport) {
+            if (this->getCaps().fTwoSidedStencilSupport) {
                 GrGLenum backFunc;
 
                 unsigned int backRef  = settings->fBackFuncRef;
@@ -2133,7 +2091,7 @@
             *type = GR_GL_UNSIGNED_SHORT_4_4_4_4;
             break;
         case kIndex_8_GrPixelConfig:
-            if (this->supports8BitPalette()) {
+            if (this->getCaps().f8BitPaletteSupport) {
                 *format = GR_GL_PALETTE8_RGBA8;
                 *internalFormat = GR_GL_PALETTE8_RGBA8;
                 *type = GR_GL_UNSIGNED_BYTE;   // unused I think
@@ -2177,7 +2135,7 @@
     switch (config) {
         case kRGBA_8888_GrPixelConfig:
         case kRGBX_8888_GrPixelConfig:
-            if (fRGBA8Renderbuffer) {
+            if (fGLCaps.fRGBA8Renderbuffer) {
                 *format = GR_GL_RGBA8;
                 return true;
             } else {
@@ -2267,6 +2225,32 @@
 int GrGpuGL::getMaxEdges() const {
     // FIXME:  This is a pessimistic estimate based on how many other things
     // want to add uniforms.  This should be centralized somewhere.
-    return GR_CT_MIN(fMaxFragmentUniformVectors - 8, kMaxEdges);
+    return GR_CT_MIN(fGLCaps.fMaxFragmentUniformVectors - 8, kMaxEdges);
 }
 
+void GrGpuGL::GLCaps::print() const {
+    for (int i = 0; i < fStencilFormats.count(); ++i) {
+        GrPrintf("Stencil Format %d, stencil bits: %02d, total bits: %02d\n",
+                 i,
+                 fStencilFormats[i].fStencilBits,
+                 fStencilFormats[i].fTotalBits);
+    }
+
+    GR_STATIC_ASSERT(0 == kNone_MSFBO);
+    GR_STATIC_ASSERT(1 == kDesktopARB_MSFBO);
+    GR_STATIC_ASSERT(2 == kDesktopEXT_MSFBO);
+    GR_STATIC_ASSERT(3 == kAppleES_MSFBO);
+    static const char* gMSFBOExtStr[] = {
+        "None",
+        "ARB",
+        "EXT",
+        "Apple",
+    };
+    GrPrintf("MSAA Type: %s\n", gMSFBOExtStr[fMSFBOType]);
+    for (int i = 0; i < GR_ARRAY_COUNT(fAASamples); ++i) {
+        GrPrintf("AA Level %d has %d samples\n", i, fAASamples[i]);
+    }
+    GrPrintf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
+    GrPrintf("Support RGBA8 Render Buffer: %s\n",
+             (fRGBA8Renderbuffer ? "YES": "NO"));
+}
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index a38d6b6..353394f 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -146,9 +146,14 @@
     static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
 
 private:
+    // Inits GrDrawTarget::Caps and GLCaps, sublcass may enable
+    // additional caps.
+    void initCaps();
+
+    void initFSAASupport();
 
     // determines valid stencil formats
-    void setupStencilFormats();
+    void initStencilFormats();
 
     // notify callbacks to update state tracking when related
     // objects are bound to GL or deleted outside of the class
@@ -194,7 +199,43 @@
     SkString fExtensionString;
     GrGLVersion fGLVersion;
 
-    SkTArray<GrGLStencilBuffer::Format, true> fStencilFormats;
+    struct GLCaps {
+        // prealloc space for 8 stencil formats
+        GLCaps() : fStencilFormats(8) {}
+        SkTArray<GrGLStencilBuffer::Format, true> fStencilFormats;
+
+        enum {
+            /**
+             * no support for MSAA FBOs
+             */
+            kNone_MSFBO = 0,  
+            /**
+             * GL3.0-style MSAA FBO (GL_ARB_framebuffer_object)
+             */
+            kDesktopARB_MSFBO,
+            /**
+             * earlier GL_EXT_framebuffer* extensions
+             */
+            kDesktopEXT_MSFBO,
+            /**
+             * GL_APPLE_framebuffer_multisample ES extension
+             */
+            kAppleES_MSFBO,
+        } fMSFBOType;
+
+        // TODO: get rid of GrAALevel and use sample cnt directly
+        GrGLuint fAASamples[4];
+
+        // The maximum number of fragment uniform vectors (GLES has min. 16).
+        int fMaxFragmentUniformVectors;
+
+        // ES requires an extension to support RGBA8 in RenderBufferStorage
+        bool fRGBA8Renderbuffer;
+
+        void print() const;
+    } fGLCaps;
+
+
     // we want to clear stencil buffers when they are created. We want to clear
     // the entire buffer even if it is larger than the color attachment. We
     // attach it to this fbo with no color attachment to do the initial clear.
@@ -202,23 +243,6 @@
 
     bool fHWBlendDisabled;
 
-    GrGLuint fAASamples[4];
-    enum {
-        kNone_MSFBO = 0,  //<! no support for MSAA FBOs
-        kDesktopARB_MSFBO,//<! GL3.0-style MSAA FBO (GL_ARB_framebuffer_object)
-        kDesktopEXT_MSFBO,//<! earlier GL_EXT_framebuffer* extensions
-        kAppleES_MSFBO,   //<! GL_APPLE_framebuffer_multisample ES extension
-    } fMSFBOType;
-
-    // Do we have stencil wrap ops.
-    bool fHasStencilWrap;
-
-    // The maximum number of fragment uniform vectors (GLES has min. 16).
-    int fMaxFragmentUniformVectors;
-
-    // ES requires an extension to support RGBA8 in RenderBufferStorage
-    bool fRGBA8Renderbuffer;
-
     int fActiveTextureUnitIdx;
 
     // we record what stencil format worked last time to hopefully exit early
@@ -228,6 +252,8 @@
     const GrGLInterface* fGL;
     GrGLBinding fGLBinding;
 
+    bool fPrintedCaps;
+
     typedef GrGpu INHERITED;
 };
 
diff --git a/gpu/src/GrGpuGLFixed.cpp b/gpu/src/GrGpuGLFixed.cpp
index 2c97e69..195ca32 100644
--- a/gpu/src/GrGpuGLFixed.cpp
+++ b/gpu/src/GrGpuGLFixed.cpp
@@ -62,9 +62,6 @@
 
 GrGpuGLFixed::GrGpuGLFixed(const GrGLInterface* gl)
     : GrGpuGL(gl, get_binding_in_use(gl)) {
-    fShaderSupport = false;
-    fShaderDerivativeSupport = false;
-    fDualSourceBlendingSupport = false;
 }
 
 GrGpuGLFixed::~GrGpuGLFixed() {
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 7d15c4f..05f6652 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -213,7 +213,7 @@
             bool vertexEdgeAA = random.nextF() > .5f;
             if (vertexEdgeAA) {
                 pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
-                if (this->supportsShaderDerivatives()) {
+                if (this->getCaps().fShaderDerivativeSupport) {
                     pdesc.fVertexEdgeType = random.nextF() > 0.5f ?
                                                         kHairQuad_EdgeType :
                                                         kHairLine_EdgeType;
@@ -229,7 +229,7 @@
             pdesc.fEdgeAANumEdges = 0;
         }
 
-        if (fDualSourceBlendingSupport) {
+        if (this->getCaps().fDualSourceBlendingSupport) {
             pdesc.fDualSrcOutput =
                (ProgramDesc::DualSrcOutput)
                (int)(random.nextF() * ProgramDesc::kDualSrcOutputCnt);
@@ -290,15 +290,15 @@
 GrGpuGLShaders::GrGpuGLShaders(const GrGLInterface* gl)
     : GrGpuGL(gl, get_binding_in_use(gl)) {
 
-    fShaderSupport = true;
+    fCaps.fShaderSupport = true;
     if (kDesktop_GrGLBinding == this->glBinding()) {
-        fDualSourceBlendingSupport =
+        fCaps.fDualSourceBlendingSupport =
                             this->glVersion() >= GR_GL_VER(3,3) ||
                             this->hasExtension("GL_ARB_blend_func_extended");
-        fShaderDerivativeSupport = true;
+        fCaps.fShaderDerivativeSupport = true;
     } else {
-        fDualSourceBlendingSupport = false;
-        fShaderDerivativeSupport =
+        fCaps.fDualSourceBlendingSupport = false;
+        fCaps.fShaderDerivativeSupport =
                             this->hasExtension("GL_OES_standard_derivatives");
     }
 
@@ -937,7 +937,7 @@
         // (e.g. solid draw, and dst coeff is kZero. It's correct to make
         // the dst coeff be kISA. Or solid draw with kSA can be tweaked to be
         // kOne).
-        if (fDualSourceBlendingSupport) {
+        if (this->getCaps().fDualSourceBlendingSupport) {
             if (kZero_BlendCoeff == fCurrDrawState.fDstBlend) {
                 // write the coverage value to second color
                 desc.fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
diff --git a/gpu/src/GrInOrderDrawBuffer.cpp b/gpu/src/GrInOrderDrawBuffer.cpp
index 85af430..9519655 100644
--- a/gpu/src/GrInOrderDrawBuffer.cpp
+++ b/gpu/src/GrInOrderDrawBuffer.cpp
@@ -18,8 +18,7 @@
 GrInOrderDrawBuffer::GrInOrderDrawBuffer(const GrGpu* gpu,
                                          GrVertexBufferAllocPool* vertexPool,
                                          GrIndexBufferAllocPool* indexPool)
-    : fGpu(gpu)
-    , fDraws(&fDrawStorage)
+    : fDraws(&fDrawStorage)
     , fStates(&fStateStorage)
     , fClears(&fClearStorage)
     , fClips(&fClipStorage)
@@ -33,12 +32,12 @@
     , fVertexPool(*vertexPool)
     , fIndexPool(*indexPool)
     , fGeoPoolStateStack(&fGeoStackStorage) {
-    
+
+    fCaps = gpu->getCaps();
+
     GrAssert(NULL != vertexPool);
     GrAssert(NULL != indexPool);
 
-    gpu->ref();
-
     GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
     poolState.fUsedPoolVertexBytes = 0;
     poolState.fUsedPoolIndexBytes = 0;
@@ -53,7 +52,6 @@
 GrInOrderDrawBuffer::~GrInOrderDrawBuffer() {
     this->reset();
     GrSafeUnref(fQuadIndexBuffer);
-    fGpu->unref();
 }
 
 void GrInOrderDrawBuffer::initializeDrawStateAndClip(const GrDrawTarget& target) {
@@ -627,7 +625,7 @@
 }
 
 bool GrInOrderDrawBuffer::willUseHWAALines() const {
-    return fGpu->supportsHWAALines() &&
+    return this->getCaps().fHWAALineSupport &&
            CanUseHWAALines(this->getGeomSrc().fVertexLayout, fCurrDrawState);
 }
 
diff --git a/gpu/src/GrPathRendererChain.cpp b/gpu/src/GrPathRendererChain.cpp
index dc09d43..a6da6d3 100644
--- a/gpu/src/GrPathRendererChain.cpp
+++ b/gpu/src/GrPathRendererChain.cpp
@@ -57,9 +57,9 @@
 void GrPathRendererChain::init() {
     GrAssert(!fInit);
     GrGpu* gpu = fOwner->getGpu();
-    this->addPathRenderer(
-                    new GrDefaultPathRenderer(gpu->supportsTwoSidedStencil(),
-                    gpu->supportsStencilWrapOps()))->unref();
+    bool twoSided = gpu->getCaps().fTwoSidedStencilSupport;
+    bool wrapOp = gpu->getCaps().fStencilWrapOpsSupport;
+    this->addPathRenderer(new GrDefaultPathRenderer(twoSided, wrapOp))->unref();
     GrPathRenderer::AddPathRenderers(fOwner, fFlags, this);
     fInit = true;
 }