Use texture size to determine precision of texture coord varyings.

Review URL: https://codereview.chromium.org/778783002
diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt
index 4584cf9..b79d5d2 100644
--- a/expectations/gm/ignored-tests.txt
+++ b/expectations/gm/ignored-tests.txt
@@ -62,3 +62,26 @@
 
 # robertphillips
 imagefiltersscaled
+
+#bsalomon - changes to texture coord precision
+imageblur2
+imageblur_large
+imageblur
+downsamplebitmap_checkerboard_high_512_256
+downsamplebitmap_checkerboard_medium_512_256
+downsamplebitmap_checkerboard_low_512_256
+imagefilterscropped
+downsamplebitmap_text_medium_72.00pt
+downsamplebitmap_text_high_72.00pt
+downsamplebitmap_text_low_72.00pt
+downsamplebitmap_image_high_mandrill_132x132_12x12.astc
+downsamplebitmap_image_medium_mandrill_132x132_12x12.astc
+downsamplebitmap_image_low_mandrill_132x132_12x12.astc
+downsamplebitmap_image_high_mandrill_512.png
+downsamplebitmap_image_medium_mandrill_512.png
+downsamplebitmap_image_low_mandrill_512.png
+imagefiltersbase
+simpleblurroundrect
+blurcircles
+colortype_xfermodes
+
diff --git a/gm/tallstretchedbitmaps.cpp b/gm/tallstretchedbitmaps.cpp
new file mode 100644
index 0000000..905cee8
--- /dev/null
+++ b/gm/tallstretchedbitmaps.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014 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 "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+
+int make_bm(SkBitmap* bm, int height) {
+    static const int kRadius = 22;
+    static const int kMargin = 8;
+    static const SkScalar kStartAngle = 0;
+    static const SkScalar kDAngle = 25;
+    static const SkScalar kSweep = 320;
+    static const SkScalar kThickness = 8;
+
+    int count = (height / (2 * kRadius + kMargin));
+    height = count * (2 * kRadius + kMargin);
+
+    bm->allocN32Pixels(2 * (kRadius + kMargin), height);
+    SkRandom random;
+
+    SkCanvas wholeCanvas(*bm);
+    wholeCanvas.clear(0x00000000);
+
+    SkScalar angle = kStartAngle;
+    for (int i = 0; i < count; ++i) {
+        SkPaint paint;
+        // The sw rasterizer disables AA for large canvii. So we make a small canvas for each draw.
+        SkBitmap smallBM;
+        SkIRect subRect = SkIRect::MakeXYWH(0, i * (kMargin + 2 * kRadius),
+                                            2 * kRadius + kMargin, 2 * kRadius + kMargin);
+        bm->extractSubset(&smallBM, subRect);
+        SkCanvas canvas(smallBM);
+        canvas.translate(kMargin + kRadius, kMargin + kRadius);
+
+        paint.setAntiAlias(true);
+        paint.setColor(random.nextU() | 0xFF000000);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(kThickness);
+        paint.setStrokeCap(SkPaint::kRound_Cap);
+        SkScalar radius = kRadius - kThickness / 2;
+        SkRect bounds = SkRect::MakeLTRB(-radius, -radius, radius, radius);
+
+        canvas.drawArc(bounds, angle, kSweep, false, paint);
+        angle += kDAngle;
+    }
+    bm->setImmutable();
+    return count;
+}
+
+class TallStretchedBitmapsGM : public skiagm::GM {
+public:
+    TallStretchedBitmapsGM() {}
+
+protected:
+    SkString onShortName() SK_OVERRIDE {
+        return SkString("tall_stretched_bitmaps");
+    }
+
+    SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(750, 750);
+    }
+
+    void onOnceBeforeDraw() SK_OVERRIDE {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fTallBmps); ++i) {
+            int h = (4 + i) * 1024;
+
+            fTallBmps[i].fItemCnt = make_bm(&fTallBmps[i].fBmp, h);
+        }
+    }
+
+    void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        canvas->scale(1.3f, 1.3f);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fTallBmps); ++i) {
+            SkASSERT(fTallBmps[i].fItemCnt > 10);
+            SkBitmap bmp = fTallBmps[i].fBmp;
+            // Draw the last 10 elements of the bitmap.
+            int startItem = fTallBmps[i].fItemCnt - 10;
+            int itemHeight = bmp.height() / fTallBmps[i].fItemCnt;
+            SkIRect subRect = SkIRect::MakeLTRB(0, startItem * itemHeight,
+                                               bmp.width(), bmp.height());
+            SkRect dstRect = SkRect::MakeWH(SkIntToScalar(bmp.width()), 10.f * itemHeight);
+            SkPaint paint;
+            paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+            canvas->drawBitmapRect(bmp, &subRect, dstRect, &paint);
+            canvas->translate(SkIntToScalar(bmp.width() + 10), 0);
+        }
+    }    
+
+    uint32_t onGetFlags() const SK_OVERRIDE {
+        // This GM causes issues in replay modes.
+        return kSkipTiled_Flag | kNoBBH_Flag | kSkipPicture_Flag;
+    }
+
+private:
+    struct {
+        SkBitmap fBmp;
+        int      fItemCnt;
+    } fTallBmps[8];
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return SkNEW(TallStretchedBitmapsGM);)
+
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 35f027e..61575d3 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -186,6 +186,7 @@
         '../gm/testimagefilters.cpp',
         '../gm/texdata.cpp',
         '../gm/variedtext.cpp',
+        '../gm/tallstretchedbitmaps.cpp',
         '../gm/textblob.cpp',
         '../gm/textblobshader.cpp',
         '../gm/texturedomaineffect.cpp',
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index dd22065..09b7dbe 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -68,6 +68,7 @@
       '<(skia_src_path)/gpu/GrClipMaskManager.h',
       '<(skia_src_path)/gpu/GrClipMaskManager.cpp',
       '<(skia_src_path)/gpu/GrContext.cpp',
+      '<(skia_src_path)/gpu/GrCoordTransform.cpp',
       '<(skia_src_path)/gpu/GrDefaultGeoProcFactory.cpp',
       '<(skia_src_path)/gpu/GrDefaultGeoProcFactory.h',
       '<(skia_src_path)/gpu/GrDefaultPathRenderer.cpp',
diff --git a/include/gpu/GrCoordTransform.h b/include/gpu/GrCoordTransform.h
index 42598dd..b00c8a4 100644
--- a/include/gpu/GrCoordTransform.h
+++ b/include/gpu/GrCoordTransform.h
@@ -12,10 +12,13 @@
 #include "SkMatrix.h"
 #include "GrTexture.h"
 #include "GrTypes.h"
+#include "GrShaderVar.h"
 
 /**
  * Coordinates available to GrProcessor subclasses for requesting transformations. Transformed
  * coordinates are made available in the the portion of fragment shader emitted by the effect.
+ *
+ * The precision of the shader var that interpolates the transformed coordinates can be specified.
  */
 enum GrCoordSet {
     /**
@@ -44,41 +47,52 @@
     GrCoordTransform() { SkDEBUGCODE(fInProcessor = false); }
 
     /**
-     * Create a transformation that maps [0, 1] to a texture's boundaries.
+     * Create a transformation that maps [0, 1] to a texture's boundaries. The precision is inferred
+     * from the texture size.
      */
     GrCoordTransform(GrCoordSet sourceCoords, const GrTexture* texture) {
+        SkASSERT(texture);
         SkDEBUGCODE(fInProcessor = false);
         this->reset(sourceCoords, texture);
     }
 
     /**
-     * Create a transformation from a matrix. The optional texture parameter is used to infer if the
+     * Create a transformation from a matrix. The texture parameter is used to infer if the
      * framework should internally do a y reversal to account for it being upside down by Skia's
-     * coord convention.
+     * coord convention and the precision of the shader variables used to implement the coord
+     * transform.
      */
-    GrCoordTransform(GrCoordSet sourceCoords, const SkMatrix& m, const GrTexture* texture = NULL) {
+    GrCoordTransform(GrCoordSet sourceCoords, const SkMatrix& m, const GrTexture* texture) {
         SkDEBUGCODE(fInProcessor = false);
+        SkASSERT(texture);
         this->reset(sourceCoords, m, texture);
     }
 
+    /**
+     * Create a transformation that applies the matrix to a coord set.
+     */
+    GrCoordTransform(GrCoordSet sourceCoords, const SkMatrix& m,
+                     GrShaderVar::Precision precision = GrShaderVar::kDefault_Precision) {
+        SkDEBUGCODE(fInProcessor = false);
+        this->reset(sourceCoords, m, precision);
+    }
+
     void reset(GrCoordSet sourceCoords, const GrTexture* texture) {
         SkASSERT(!fInProcessor);
         SkASSERT(texture);
         this->reset(sourceCoords, MakeDivByTextureWHMatrix(texture), texture);
     }
 
-    void reset(GrCoordSet sourceCoords, const SkMatrix& m, const GrTexture* texture = NULL) {
-        SkASSERT(!fInProcessor);
-        fSourceCoords = sourceCoords;
-        fMatrix = m;
-        fReverseY = texture && kBottomLeft_GrSurfaceOrigin == texture->origin();
-    }
+    void reset(GrCoordSet, const SkMatrix&, const GrTexture*);
+    void reset(GrCoordSet sourceCoords, const SkMatrix& m,
+               GrShaderVar::Precision precision = GrShaderVar::kDefault_Precision);
 
-    GrCoordTransform& operator= (const GrCoordTransform& other) {
+    GrCoordTransform& operator= (const GrCoordTransform& that) {
         SkASSERT(!fInProcessor);
-        fSourceCoords = other.fSourceCoords;
-        fMatrix = other.fMatrix;
-        fReverseY = other.fReverseY;
+        fSourceCoords = that.fSourceCoords;
+        fMatrix = that.fMatrix;
+        fReverseY = that.fReverseY;
+        fPrecision = that.fPrecision;
         return *this;
     }
 
@@ -91,17 +105,19 @@
         return &fMatrix;
     }
 
-    bool operator== (const GrCoordTransform& that) const {
+    bool operator==(const GrCoordTransform& that) const {
         return fSourceCoords == that.fSourceCoords &&
                fMatrix.cheapEqualTo(that.fMatrix) &&
-               fReverseY == that.fReverseY;
+               fReverseY == that.fReverseY &&
+               fPrecision == that.fPrecision;
     }
 
-    bool operator!= (const GrCoordTransform& that) const { return !(*this == that); }
+    bool operator!=(const GrCoordTransform& that) const { return !(*this == that); }
 
     GrCoordSet sourceCoords() const { return fSourceCoords; }
     const SkMatrix& getMatrix() const { return fMatrix; }
     bool reverseY() const { return fReverseY; }
+    GrShaderVar::Precision precision() const { return fPrecision; }
 
     /** Useful for effects that want to insert a texture matrix that is implied by the texture
         dimensions */
@@ -113,10 +129,10 @@
     }
 
 private:
-    GrCoordSet fSourceCoords;
-    SkMatrix   fMatrix;
-    bool       fReverseY;
-
+    GrCoordSet              fSourceCoords;
+    SkMatrix                fMatrix;
+    bool                    fReverseY;
+    GrShaderVar::Precision  fPrecision;
     typedef SkNoncopyable INHERITED;
 
 #ifdef SK_DEBUG
diff --git a/include/gpu/GrShaderVar.h b/include/gpu/GrShaderVar.h
index 967ae82..05ae36a 100644
--- a/include/gpu/GrShaderVar.h
+++ b/include/gpu/GrShaderVar.h
@@ -42,7 +42,10 @@
         // Default precision is medium. This is because on OpenGL ES 2 highp support is not
         // guaranteed. On (non-ES) OpenGL the specifiers have no effect on precision.
         kDefault_Precision = kMedium_Precision,
+
+        kLast_Precision = kHigh_Precision
     };
+    static const int kPrecisionCount = kLast_Precision + 1;
 
     /**
      * Defaults to a float with no precision specifier
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index 1ed7070..4e225f0 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -6,8 +6,6 @@
  * found in the LICENSE file.
  */
 
-
-
 #ifndef GrTypes_DEFINED
 #define GrTypes_DEFINED
 
diff --git a/include/gpu/GrTypesPriv.h b/include/gpu/GrTypesPriv.h
index 0ff4e57..83d8561 100644
--- a/include/gpu/GrTypesPriv.h
+++ b/include/gpu/GrTypesPriv.h
@@ -29,6 +29,15 @@
 };
 static const int kGrSLTypeCount = kLast_GrSLType + 1;
 
+enum GrShaderType {
+    kVertex_GrShaderType,
+    kGeometry_GrShaderType,
+    kFragment_GrShaderType,
+
+    kLastkFragment_GrShaderType = kFragment_GrShaderType
+};
+static const int kGrShaderTypeCount = kLastkFragment_GrShaderType + 1;
+
 /**
  * Gets the vector size of the SLType. Returns -1 for void, matrices, and samplers.
  */
diff --git a/src/gpu/GrCoordTransform.cpp b/src/gpu/GrCoordTransform.cpp
new file mode 100644
index 0000000..53285a2
--- /dev/null
+++ b/src/gpu/GrCoordTransform.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrCoordTransform.h"
+#include "GrContext.h"
+#include "GrDrawTargetCaps.h"
+#include "GrGpu.h"
+
+void GrCoordTransform::reset(GrCoordSet sourceCoords, const SkMatrix& m, const GrTexture* texture) {
+    SkASSERT(texture);
+    SkASSERT(!fInProcessor);
+
+    fSourceCoords = sourceCoords;
+    fMatrix = m;
+    fReverseY = kBottomLeft_GrSurfaceOrigin == texture->origin();
+
+    // Always start at kDefault. Then if precisions differ we see if the precision needs to be
+    // increased. Our rule is that we want at least 4 subpixel values in the representation for
+    // coords between 0 to 1. Note that this still might not be enough when drawing with repeat
+    // or mirror-repeat modes but that case can be arbitrarily bad. 
+    fPrecision = GrShaderVar::kDefault_Precision;
+    if (texture->getContext()) {
+        const GrDrawTargetCaps* caps = texture->getContext()->getGpu()->caps();
+        if (caps->floatPrecisionVaries()) {
+            int maxD = SkTMax(texture->width(), texture->height());
+            const GrDrawTargetCaps::PrecisionInfo* info;
+            info = &caps->getFloatShaderPrecisionInfo(kFragment_GrShaderType, fPrecision);
+            do {
+                SkASSERT(info->supported());
+                // Make sure there is at least 2 bits of subpixel precision in the range of
+                // texture coords from 0.5 to 1.0.
+                if ((2 << info->fBits) / maxD > 4) {
+                    break;
+                }
+                if (GrShaderVar::kHigh_Precision == fPrecision) {
+                    break;
+                }
+                GrShaderVar::Precision nextP = static_cast<GrShaderVar::Precision>(fPrecision + 1);
+                info = &caps->getFloatShaderPrecisionInfo(kFragment_GrShaderType, nextP);
+                if (!info->supported()) {
+                    break;
+                }
+                fPrecision = nextP;
+            } while (true);
+        }
+    }
+}
+
+void GrCoordTransform::reset(GrCoordSet sourceCoords,
+                             const SkMatrix& m,
+                             GrShaderVar::Precision precision) {
+    SkASSERT(!fInProcessor);
+    fSourceCoords = sourceCoords;
+    fMatrix = m;
+    fReverseY = false;
+    fPrecision = precision;
+}
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 65764b8..ed54bf9 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -1011,6 +1011,8 @@
     fMaxTextureSize = 0;
     fMaxSampleCount = 0;
 
+    fShaderPrecisionVaries = false;
+
     memset(fConfigRenderSupport, 0, sizeof(fConfigRenderSupport));
     memset(fConfigTextureSupport, 0, sizeof(fConfigTextureSupport));
 }
@@ -1042,6 +1044,12 @@
     memcpy(fConfigRenderSupport, other.fConfigRenderSupport, sizeof(fConfigRenderSupport));
     memcpy(fConfigTextureSupport, other.fConfigTextureSupport, sizeof(fConfigTextureSupport));
 
+    fShaderPrecisionVaries = other.fShaderPrecisionVaries;
+    for (int s = 0; s < kGrShaderTypeCount; ++s) {
+        for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) {
+            fFloatPrecisions[s][p] = other.fFloatPrecisions[s][p];
+        }
+    }
     return *this;
 }
 
@@ -1065,6 +1073,30 @@
     return str;
 }
 
+static const char* shader_type_to_string(GrShaderType type) {
+    switch (type) {
+        case kVertex_GrShaderType:
+            return "vertex";
+        case kGeometry_GrShaderType:
+            return "geometry";
+        case kFragment_GrShaderType:
+            return "fragment";
+    }
+    return "";
+}
+
+static const char* precision_to_string(GrShaderVar::Precision p) {
+    switch (p) {
+        case GrShaderVar::kLow_Precision:
+            return "low";
+        case GrShaderVar::kMedium_Precision:
+            return "medium";
+        case GrShaderVar::kHigh_Precision:
+            return "high";
+    }
+    return "";
+}
+
 SkString GrDrawTargetCaps::dump() const {
     SkString r;
     static const char* gNY[] = {"NO", "YES"};
@@ -1140,6 +1172,23 @@
                   gNY[fConfigTextureSupport[i]]);
     }
 
+    r.appendf("Shader Float Precisions (varies: %s):\n", gNY[fShaderPrecisionVaries]);
+
+    for (int s = 0; s < kGrShaderTypeCount; ++s) {
+        GrShaderType shaderType = static_cast<GrShaderType>(s);
+        r.appendf("\t%s:\n", shader_type_to_string(shaderType));
+        for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) {
+            if (fFloatPrecisions[s][p].supported()) {
+                GrShaderVar::Precision precision = static_cast<GrShaderVar::Precision>(p);
+                r.appendf("\t\t%s: log_low: %d log_high: %d bits: %d\n",
+                          precision_to_string(precision),
+                          fFloatPrecisions[s][p].fLogRangeLow,
+                          fFloatPrecisions[s][p].fLogRangeHigh,
+                          fFloatPrecisions[s][p].fBits);
+            }
+        }
+    }
+
     return r;
 }
 
diff --git a/src/gpu/GrDrawTargetCaps.h b/src/gpu/GrDrawTargetCaps.h
index f13aa53..ae6a9ed 100644
--- a/src/gpu/GrDrawTargetCaps.h
+++ b/src/gpu/GrDrawTargetCaps.h
@@ -9,6 +9,8 @@
 #define GrDrawTargetCaps_DEFINED
 
 #include "GrTypes.h"
+#include "GrTypesPriv.h"
+#include "GrShaderVar.h"
 #include "SkRefCnt.h"
 #include "SkString.h"
 
@@ -19,6 +21,41 @@
 public:
     SK_DECLARE_INST_COUNT(GrDrawTargetCaps)
 
+    /** Info about shader variable precision within a given shader stage. That is, this info
+        is relevant to a float (or vecNf) variable declared with a GrShaderVar::Precision 
+        in a given GrShaderType. The info here is hoisted from the OpenGL spec. */
+    struct PrecisionInfo {
+        PrecisionInfo() {
+            fLogRangeLow = 0;
+            fLogRangeHigh = 0;
+            fBits = 0;
+        }
+
+        /** Is this precision level allowed in the shader stage? */
+        bool supported() const { return 0 != fBits; }
+
+        bool operator==(const PrecisionInfo& that) const {
+            return fLogRangeLow == that.fLogRangeLow && fLogRangeHigh == that.fLogRangeHigh &&
+                   fBits == that.fBits;
+        }
+        bool operator!=(const PrecisionInfo& that) const { return !(*this == that); }
+
+        /** floor(log2(|min_value|)) */
+        int fLogRangeLow;
+        /** floor(log2(|max_value|)) */
+        int fLogRangeHigh;
+        /** Number of bits of precision. As defined in OpenGL (with names modified to reflect this 
+            struct) :
+            """
+                If the smallest representable value greater than 1 is 1 + e, then fBits will
+                contain floor(log2(e)), and every value in the range [2^fLogRangeLow,
+                2^fLogRangeHigh] can be represented to at least one part in 2^fBits.
+            """  
+          */
+        int fBits;
+    };
+
+
     GrDrawTargetCaps() : fUniqueID(CreateUniqueID()) {
         this->reset();
     }
@@ -84,6 +121,24 @@
     }
 
     /**
+     * Get the precision info for a variable of type kFloat_GrSLType, kVec2f_GrSLType, etc in a
+     * given shader type. If the shader type is not supported or the precision level is not
+     * supported in that shader type then the returned struct will report false when supported() is
+     * called.
+     */
+    const PrecisionInfo& getFloatShaderPrecisionInfo(GrShaderType shaderType,
+                                                     GrShaderVar::Precision precision) const {
+        return fFloatPrecisions[shaderType][precision];
+    };
+
+    /**
+     * Is there any difference between the float shader variable precision types? If this is true
+     * then unless the shader type is not supported, any call to getFloatShaderPrecisionInfo() would
+     * report the same info for all precisions in all shader types.
+     */
+    bool floatPrecisionVaries() const { return fShaderPrecisionVaries; }
+
+    /**
      * Gets an id that is unique for this GrDrawTargetCaps object. It is static in that it does
      * not change when the content of the GrDrawTargetCaps object changes. This will never return
      * 0.
@@ -119,6 +174,9 @@
     bool fConfigRenderSupport[kGrPixelConfigCnt][2];
     bool fConfigTextureSupport[kGrPixelConfigCnt];
 
+    bool fShaderPrecisionVaries;
+    PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][GrShaderVar::kPrecisionCount];
+
 private:
     static uint32_t CreateUniqueID();
 
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index cf442a0..a510cb4 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -386,6 +386,8 @@
     this->initConfigTexturableTable(ctxInfo, gli);
     this->initConfigRenderableTable(ctxInfo);
 
+    this->initShaderPrecisionTable(ctxInfo, gli);
+
     return true;
 }
 
@@ -774,6 +776,76 @@
     fStencilVerifiedColorConfigs.push_back_n(fStencilFormats.count());
 }
 
+static GrGLenum precision_to_gl_float_type(GrShaderVar::Precision p) {
+    switch (p) {
+        case GrShaderVar::kLow_Precision:
+            return GR_GL_LOW_FLOAT;
+        case GrShaderVar::kMedium_Precision:
+            return GR_GL_MEDIUM_FLOAT;
+        case GrShaderVar::kHigh_Precision:
+            return GR_GL_HIGH_FLOAT;
+    }
+    SkFAIL("Unknown precision.");
+    return -1;
+}
+
+static GrGLenum shader_type_to_gl_shader(GrShaderType type) {
+    switch (type) {
+        case kVertex_GrShaderType:
+            return GR_GL_VERTEX_SHADER;
+        case kGeometry_GrShaderType:
+            return GR_GL_GEOMETRY_SHADER;
+        case kFragment_GrShaderType:
+            return GR_GL_FRAGMENT_SHADER;
+    }
+    SkFAIL("Unknown shader type.");
+    return -1;
+}
+
+void GrGLCaps::initShaderPrecisionTable(const GrGLContextInfo& ctxInfo, const GrGLInterface* intf) {
+    if (kGLES_GrGLStandard == ctxInfo.standard() || ctxInfo.version() >= GR_GL_VER(4,1) ||
+        ctxInfo.hasExtension("GL_ARB_ES2_compatibility")) {
+        for (int s = 0; s < kGrShaderTypeCount; ++s) {
+            if (kGeometry_GrShaderType != s || fGeometryShaderSupport) {
+                GrShaderType shaderType = static_cast<GrShaderType>(s);
+                GrGLenum glShader = shader_type_to_gl_shader(shaderType);
+                PrecisionInfo* first = NULL;
+                fShaderPrecisionVaries = false;
+                for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) {
+                    GrShaderVar::Precision precision = static_cast<GrShaderVar::Precision>(p);
+                    GrGLenum glPrecision = precision_to_gl_float_type(precision);
+                    GrGLint range[2];
+                    GrGLint bits;
+                    GR_GL_GetShaderPrecisionFormat(intf, glShader, glPrecision, range, &bits);
+                    if (bits) {
+                        fFloatPrecisions[s][p].fLogRangeLow = range[0];
+                        fFloatPrecisions[s][p].fLogRangeHigh = range[1];
+                        fFloatPrecisions[s][p].fBits = bits;
+                        if (!first) {
+                            first = &fFloatPrecisions[s][p];
+                        } else if (!fShaderPrecisionVaries) {
+                            fShaderPrecisionVaries = (*first != fFloatPrecisions[s][p]);
+                        }
+                    }
+                }
+            }
+        }
+    } else {
+        // We're on a desktop GL that doesn't have precision info. Assume they're all 32bit float.
+        fShaderPrecisionVaries = false;
+        for (int s = 0; s < kGrShaderTypeCount; ++s) {
+            if (kGeometry_GrShaderType != s || fGeometryShaderSupport) {
+                for (int p = 0; p < GrShaderVar::kPrecisionCount; ++p) {
+                    fFloatPrecisions[s][p].fLogRangeLow = 127;
+                    fFloatPrecisions[s][p].fLogRangeHigh = 127;
+                    fFloatPrecisions[s][p].fBits = 23;
+                }
+            }
+        }
+    }
+}
+
+
 void GrGLCaps::markColorConfigAndStencilFormatAsVerified(
                                     GrPixelConfig config,
                                     const GrGLStencilBuffer::Format& format) {
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index f840193..19e9b87 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -325,9 +325,10 @@
     void initConfigRenderableTable(const GrGLContextInfo&);
     void initConfigTexturableTable(const GrGLContextInfo&, const GrGLInterface*);
 
-    bool doReadPixelsSupported(const GrGLInterface* intf,
-                                   GrGLenum format,
-                                   GrGLenum type) const;
+    // Must be called after fGeometryShaderSupport is initialized.
+    void initShaderPrecisionTable(const GrGLContextInfo&, const GrGLInterface*);
+
+    bool doReadPixelsSupported(const GrGLInterface* intf, GrGLenum format, GrGLenum type) const;
 
     // tracks configs that have been verified to pass the FBO completeness when
     // used as a color attachment
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 6867a13..16de1ea 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -14,25 +14,6 @@
 #include "gl/builders/GrGLFragmentShaderBuilder.h"
 
 /**
- * The key for an individual coord transform is made up of a matrix type and a bit that
- * indicates the source of the input coords.
- */
-enum {
-    kMatrixTypeKeyBits   = 1,
-    kMatrixTypeKeyMask   = (1 << kMatrixTypeKeyBits) - 1,
-    kPositionCoords_Flag = (1 << kMatrixTypeKeyBits),
-    kTransformKeyBits    = kMatrixTypeKeyBits + 1,
-};
-
-/**
- * We specialize the vertex code for each of these matrix types.
- */
-enum MatrixType {
-    kNoPersp_MatrixType  = 0,
-    kGeneral_MatrixType  = 1,
-};
-
-/**
  * Do we need to either map r,g,b->a or a->r. configComponentMask indicates which channels are
  * present in the texture's config. swizzleComponentMask indicates the channels present in the
  * shader swizzle.
@@ -72,8 +53,33 @@
     return key;
 }
 
-static uint32_t gen_transform_key(const GrPendingFragmentStage& stage,
-                                  bool useExplicitLocalCoords) {
+/**
+ * The key for an individual coord transform is made up of a matrix type, a precision, and a bit
+ * that indicates the source of the input coords.
+ */
+enum {
+    kMatrixTypeKeyBits   = 1,
+    kMatrixTypeKeyMask   = (1 << kMatrixTypeKeyBits) - 1,
+    
+    kPrecisionBits       = 2,
+    kPrecisionShift      = kMatrixTypeKeyBits,
+
+    kPositionCoords_Flag = (1 << (kPrecisionShift + kPrecisionBits)),
+
+    kTransformKeyBits    = kMatrixTypeKeyBits + kPrecisionBits + 1,
+};
+
+GR_STATIC_ASSERT(GrShaderVar::kHigh_Precision < (1 << kPrecisionBits));
+
+/**
+ * We specialize the vertex code for each of these matrix types.
+ */
+enum MatrixType {
+    kNoPersp_MatrixType  = 0,
+    kGeneral_MatrixType  = 1,
+};
+
+static uint32_t gen_transform_key(const GrPendingFragmentStage& stage, bool useExplicitLocalCoords) {
     uint32_t totalKey = 0;
     int numTransforms = stage.getProcessor()->numTransforms();
     for (int t = 0; t < numTransforms; ++t) {
@@ -88,7 +94,12 @@
         if (kLocal_GrCoordSet != coordTransform.sourceCoords() && useExplicitLocalCoords) {
             key |= kPositionCoords_Flag;
         }
+
+        GR_STATIC_ASSERT(GrShaderVar::kPrecisionCount <= (1 << kPrecisionBits));
+        key |= (coordTransform.precision() << kPrecisionShift);
+
         key <<= kTransformKeyBits * t;
+
         SkASSERT(0 == (totalKey & key)); // keys for each transform ought not to overlap
         totalKey |= key;
     }
diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h
index bc6fdf1..570694a 100644
--- a/src/gpu/gl/GrGLUtil.h
+++ b/src/gpu/gl/GrGLUtil.h
@@ -71,12 +71,21 @@
         *(p) = GR_GL_INIT_ZERO;                                                \
         GR_GL_CALL(gl, GetRenderbufferParameteriv(t, pname, p));               \
     } while (0)
+
 #define GR_GL_GetTexLevelParameteriv(gl, t, l, pname, p)                       \
     do {                                                                       \
         *(p) = GR_GL_INIT_ZERO;                                                \
         GR_GL_CALL(gl, GetTexLevelParameteriv(t, l, pname, p));                \
     } while (0)
 
+#define GR_GL_GetShaderPrecisionFormat(gl, st, pt, range, precision)           \
+    do {                                                                       \
+        (range)[0] = GR_GL_INIT_ZERO;                                          \
+        (range)[1] = GR_GL_INIT_ZERO;                                          \
+        (*precision) = GR_GL_INIT_ZERO;                                        \
+        GR_GL_CALL(gl, GetShaderPrecisionFormat(st, pt, range, precision));    \
+    } while (0)
+
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 4d8011c..5ed566f 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -413,7 +413,8 @@
         const char* coords = useLocalCoords ? fVS.localCoords() : fVS.positionCoords();
 
         GrGLVertToFrag v(varyingType);
-        this->addCoordVarying(varyingName, &v, uniName, coords);
+        this->addVarying(varyingName, &v, processor->coordTransform(t).precision());
+        fCoordVaryings.push_back(TransformVarying(v, uniName, coords));
 
         SkASSERT(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
         SkNEW_APPEND_TO_TARRAY(outCoords, GrGLProcessor::TransformedCoords,
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.h b/src/gpu/gl/builders/GrGLProgramBuilder.h
index 973ae5e..b7458b3 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.h
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.h
@@ -30,9 +30,9 @@
 class GrGLUniformBuilder {
 public:
     enum ShaderVisibility {
-        kVertex_Visibility   = 0x1,
-        kGeometry_Visibility = 0x2,
-        kFragment_Visibility = 0x4,
+        kVertex_Visibility   = 1 << kVertex_GrShaderType,
+        kGeometry_Visibility = 1 << kGeometry_GrShaderType,
+        kFragment_Visibility = 1 << kFragment_GrShaderType,
     };
 
     virtual ~GrGLUniformBuilder() {}
@@ -344,12 +344,6 @@
         SkString fSourceCoords;
     };
 
-    void addCoordVarying(const char* name, GrGLVarying* v, const char* uniName,
-                         const char* sourceCoords) {
-        this->addVarying(name, v);
-        fCoordVaryings.push_back(TransformVarying(*v, uniName, sourceCoords));
-    }
-
     const char* rtAdjustment() const { return "rtAdjustment"; }
 
     // number of each input/output type in a single allocation block, used by many builders