Beginning of support for texture rectangles.

Adds support for importing a RECTANGLE texture into Skia via GrTexureProvider::wrapBackendTexture().

Tests read/writing pixels, copySurface, and clear.

Does not add support for texturing from a RECTANGLE texture as the coords will be incorrectly normalized.

BUG=skia:3868
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1583863002

Review URL: https://codereview.chromium.org/1583863002
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index a637e8e..b64ff64 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -39,6 +39,7 @@
       '<(skia_include_path)/gpu/GrTextureAccess.h',
       '<(skia_include_path)/gpu/GrTestUtils.h',
       '<(skia_include_path)/gpu/GrTypes.h',
+      '<(skia_include_path)/gpu/GrTypesPriv.h',
       '<(skia_include_path)/gpu/GrXferProcessor.h',
 
       '<(skia_include_path)/gpu/effects/GrConstColorProcessor.h',
diff --git a/include/gpu/GrTypesPriv.h b/include/gpu/GrTypesPriv.h
index 6a9c44f..c46e25b 100644
--- a/include/gpu/GrTypesPriv.h
+++ b/include/gpu/GrTypesPriv.h
@@ -26,8 +26,9 @@
     kMat44f_GrSLType,
     kSampler2D_GrSLType,
     kSamplerExternal_GrSLType,
+    kSampler2DRect_GrSLType,
 
-    kLast_GrSLType = kSamplerExternal_GrSLType
+    kLast_GrSLType = kSampler2DRect_GrSLType
 };
 static const int kGrSLTypeCount = kLast_GrSLType + 1;
 
@@ -64,7 +65,7 @@
  */
 static inline int GrSLTypeVectorCount(GrSLType type) {
     SkASSERT(type >= 0 && type < static_cast<GrSLType>(kGrSLTypeCount));
-    static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1, -1 };
+    static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1, -1, -1 };
     return kCounts[type];
 
     GR_STATIC_ASSERT(0 == kVoid_GrSLType);
@@ -76,6 +77,7 @@
     GR_STATIC_ASSERT(6 == kMat44f_GrSLType);
     GR_STATIC_ASSERT(7 == kSampler2D_GrSLType);
     GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType);
+    GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType);
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kCounts) == kGrSLTypeCount);
 }
 
@@ -105,7 +107,8 @@
     GR_STATIC_ASSERT(6 == kMat44f_GrSLType);
     GR_STATIC_ASSERT(7 == kSampler2D_GrSLType);
     GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType);
-    GR_STATIC_ASSERT(9 == kGrSLTypeCount);
+    GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType);
+    GR_STATIC_ASSERT(10 == kGrSLTypeCount);
 }
 
 /** Returns the size in bytes for floating point GrSLTypes. For non floating point type returns 0 */
@@ -120,7 +123,8 @@
         9 * sizeof(float),        // kMat33f_GrSLType
         16 * sizeof(float),       // kMat44f_GrSLType
         0,                        // kSampler2D_GrSLType
-        0                         // kSamplerExternal_GrSLType
+        0,                         // kSamplerExternal_GrSLType
+        0                         // kSampler2DRect_GrSLType
     };
     return kSizes[type];
 
@@ -133,7 +137,17 @@
     GR_STATIC_ASSERT(6 == kMat44f_GrSLType);
     GR_STATIC_ASSERT(7 == kSampler2D_GrSLType);
     GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType);
-    GR_STATIC_ASSERT(9 == kGrSLTypeCount);
+    GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType);
+    GR_STATIC_ASSERT(10 == kGrSLTypeCount);
+}
+
+static inline bool GrSLTypeIsSamplerType(GrSLType type) {
+    SkASSERT(type >= 0 && type < static_cast<GrSLType>(kGrSLTypeCount));
+    return type >= 7 && type <= 9;
+
+    GR_STATIC_ASSERT(7 == kSampler2D_GrSLType);
+    GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType);
+    GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType);
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/include/gpu/gl/SkGLContext.h b/include/gpu/gl/SkGLContext.h
index 7edfa73..77fd325 100644
--- a/include/gpu/gl/SkGLContext.h
+++ b/include/gpu/gl/SkGLContext.h
@@ -41,6 +41,11 @@
     virtual GrEGLImage texture2DToEGLImage(GrGLuint /*texID*/) const { return 0; }
     virtual void destroyEGLImage(GrEGLImage) const {}
 
+    /** Used for testing GL_TEXTURE_RECTANGLE integration. */
+    GrGLint createTextureRectangle(int width, int height, GrGLenum internalFormat,
+                                   GrGLenum externalFormat, GrGLenum externalType,
+                                   GrGLvoid* data);
+
     /**
      * Used for testing EGLImage integration. Takes a EGLImage and wraps it in a
      * GL_TEXTURE_EXTERNAL_OES.
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 678c62a..ddc52cc 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -42,6 +42,7 @@
     fIsCoreProfile = false;
     fBindFragDataLocationSupport = false;
     fExternalTextureSupport = false;
+    fRectangleTextureSupport = false;
     fTextureSwizzleSupport = false;
     fSRGBWriteControl = false;
     fRGBA8888PixelsOpsAreSlow = false;
@@ -219,6 +220,11 @@
         }
     }
 
+    if ((kGL_GrGLStandard == standard && version >= GR_GL_VER(3, 2)) ||
+        ctxInfo.hasExtension("GL_ARB_texture_rectangle")) {
+        fRectangleTextureSupport = true;
+    }
+
     if (kGL_GrGLStandard == standard) {
         if (version >= GR_GL_VER(3,3) || ctxInfo.hasExtension("GL_ARB_texture_swizzle")) {
             fTextureSwizzleSupport = true;
@@ -885,6 +891,7 @@
     r.appendf("Partial FBO read is slow: %s\n", (fPartialFBOReadIsSlow ? "YES" : "NO"));
     r.appendf("Bind uniform location support: %s\n", (fBindUniformLocationSupport ? "YES" : "NO"));
     r.appendf("External texture support: %s\n", (fExternalTextureSupport ? "YES" : "NO"));
+    r.appendf("Rectangle texture support: %s\n", (fRectangleTextureSupport? "YES" : "NO"));
     r.appendf("Texture swizzle support: %s\n", (fTextureSwizzleSupport ? "YES" : "NO"));
 
     r.append("Configs\n-------\n");
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index c51dce1..c4f4270 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -297,6 +297,9 @@
     /// Are textures with GL_TEXTURE_EXTERNAL_OES type supported.
     bool externalTextureSupport() const { return fExternalTextureSupport; }
 
+    /// Are textures with GL_TEXTURE_RECTANGLE type supported.
+    bool rectangleTextureSupport() const { return fRectangleTextureSupport; }
+
     /// GL_ARB_texture_swizzle
     bool textureSwizzleSupport() const { return fTextureSwizzleSupport; }
 
@@ -377,6 +380,7 @@
     bool fPartialFBOReadIsSlow : 1;
     bool fBindUniformLocationSupport : 1;
     bool fExternalTextureSupport : 1;
+    bool fRectangleTextureSupport : 1;
     bool fTextureSwizzleSupport : 1;
 
     /** Number type of the components (with out considering number of bits.) */
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
index 228f258..318fcd4 100644
--- a/src/gpu/gl/GrGLDefines.h
+++ b/src/gpu/gl/GrGLDefines.h
@@ -939,6 +939,9 @@
 /* GL_OES_EGL_image_external */
 #define GR_GL_TEXTURE_EXTERNAL                              0x8D65
 
+/* GL_ARB_texture_rectangle */
+#define GR_GL_TEXTURE_RECTANGLE                             0x84F5
+
 /* EGL Defines */
 #define GR_EGL_NO_DISPLAY                                   ((GrEGLDisplay)0)
 #define GR_EGL_EXTENSIONS                                   0x3055
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 41d2da0..248a5df 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -447,6 +447,7 @@
 #else
     idDesc.fInfo = *info;
 #endif
+
     if (GR_GL_TEXTURE_EXTERNAL == idDesc.fInfo.fTarget) {
         if (renderTarget) {
             // This combination is not supported.
@@ -455,8 +456,15 @@
         if (!this->glCaps().externalTextureSupport()) {
             return nullptr;
         }
+    } else  if (GR_GL_TEXTURE_RECTANGLE == idDesc.fInfo.fTarget) {
+        if (!this->glCaps().rectangleTextureSupport()) {
+            return nullptr;
+        }
+    } else if (GR_GL_TEXTURE_2D != idDesc.fInfo.fTarget) {
+        return nullptr;
     }
-    // Sample count is interpretted to mean the number of samples that Gr code should allocate
+
+    // Sample count is interpreted to mean the number of samples that Gr code should allocate
     // for a render buffer that resolves to the texture. We don't support MSAA textures.
     if (desc.fSampleCnt && !renderTarget) {
         return nullptr;
@@ -469,7 +477,7 @@
         case kBorrow_GrWrapOwnership:
             idDesc.fLifeCycle = GrGpuResource::kBorrowed_LifeCycle;
             break;
-    }    
+    }
 
     surfDesc.fFlags = (GrSurfaceFlags) desc.fFlags;
     surfDesc.fWidth = desc.fWidth;
@@ -546,8 +554,8 @@
         ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
     } else {
         GrGLTexture* texture = static_cast<GrGLTexture*>(dstSurface->asTexture());
-        if (GR_GL_TEXTURE_2D != texture->target()) {
-             // We don't currently support writing pixels to non-TEXTURE_2D textures.
+        if (GR_GL_TEXTURE_EXTERNAL == texture->target()) {
+             // We don't currently support writing pixels to EXTERNAL textures.
              return false;
         }
     }
@@ -608,8 +616,8 @@
         return false;
     }
 
-    // Write or transfer of pixels is only implemented for TEXTURE_2D textures
-    if (GR_GL_TEXTURE_2D != glTex->target()) {
+    // Write or transfer of pixels is not implemented for TEXTURE_EXTERNAL textures
+    if (GR_GL_TEXTURE_EXTERNAL == glTex->target()) {
         return false;
     }
 
@@ -2876,10 +2884,18 @@
         fCopyPrograms[i].fProgram = 0;
     }
     const char* version = this->glCaps().glslCaps()->versionDeclString();
-    static const GrSLType kSamplerTypes[2] = { kSampler2D_GrSLType, kSamplerExternal_GrSLType };
-    SkASSERT(2 == SK_ARRAY_COUNT(fCopyPrograms));
-    int programCount = this->glCaps().externalTextureSupport() ? 2 : 1;
-    for (int i = 0; i < programCount; ++i) {
+    static const GrSLType kSamplerTypes[3] = { kSampler2D_GrSLType, kSamplerExternal_GrSLType,
+                                               kSampler2DRect_GrSLType };
+    SkASSERT(3 == SK_ARRAY_COUNT(fCopyPrograms));
+    for (int i = 0; i < 3; ++i) {
+        if (kSamplerExternal_GrSLType == kSamplerTypes[i] &&
+            !this->glCaps().externalTextureSupport()) {
+            continue;
+        }
+        if (kSampler2DRect_GrSLType == kSamplerTypes[i] &&
+            !this->glCaps().rectangleTextureSupport()) {
+            continue;
+        }
         GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier);
         GrGLSLShaderVar uTexCoordXform("u_texCoordXform", kVec4f_GrSLType,
                                      GrShaderVar::kUniform_TypeModifier);
@@ -2938,7 +2954,7 @@
             "  %s = %s(u_texture, v_texCoord);"
             "}",
             fsOutName,
-            GrGLSLTexture2DFunctionName(kVec2f_GrSLType, this->glslGeneration())
+            GrGLSLTexture2DFunctionName(kVec2f_GrSLType, kSamplerTypes[i], this->glslGeneration())
         );
     
         GL_CALL_RET(fCopyPrograms[i].fProgram, CreateProgram());
@@ -3183,16 +3199,23 @@
         dy1 = -dy1;
     }
 
-    // src rect edges in normalized texture space (0 to 1)
-    int sw = src->width();
+    GrGLfloat sx0 = (GrGLfloat)srcRect.fLeft;
+    GrGLfloat sx1 = (GrGLfloat)(srcRect.fLeft + w);
+    GrGLfloat sy0 = (GrGLfloat)srcRect.fTop;
+    GrGLfloat sy1 = (GrGLfloat)(srcRect.fTop + h);
     int sh = src->height();
-    GrGLfloat sx0 = (GrGLfloat)srcRect.fLeft / sw;
-    GrGLfloat sx1 = (GrGLfloat)(srcRect.fLeft + w) / sw;
-    GrGLfloat sy0 = (GrGLfloat)srcRect.fTop / sh;
-    GrGLfloat sy1 = (GrGLfloat)(srcRect.fTop + h) / sh;
     if (kBottomLeft_GrSurfaceOrigin == src->origin()) {
-        sy0 = 1.f - sy0;
-        sy1 = 1.f - sy1;
+        sy0 = sh - sy0;
+        sy1 = sh - sy1;
+    }
+    // src rect edges in normalized texture space (0 to 1) unless we're using a RECTANGLE texture.
+    GrGLenum srcTarget = srcTex->target();
+    if (GR_GL_TEXTURE_RECTANGLE != srcTarget) {
+        int sw = src->width();
+        sx0 /= sw;
+        sx1 /= sw;
+        sy0 /= sh;
+        sy1 /= sh;
     }
 
     GL_CALL(Uniform4f(fCopyPrograms[progIdx].fPosXformUniform, dx1 - dx0, dy1 - dy0, dx0, dy0));
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index cdd8a38..b2eec45 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -524,7 +524,7 @@
         GrGLint     fTextureUniform;
         GrGLint     fTexCoordXformUniform;
         GrGLint     fPosXformUniform;
-    }                           fCopyPrograms[2];
+    }                           fCopyPrograms[3];
     GrGLuint                    fCopyProgramArrayBuffer;
 
     struct {
@@ -535,11 +535,16 @@
     GrGLuint                    fWireRectArrayBuffer;
 
     static int TextureTargetToCopyProgramIdx(GrGLenum target) {
-        if (target == GR_GL_TEXTURE_2D) {
-            return 0;
-        } else {
-            SkASSERT(target == GR_GL_TEXTURE_EXTERNAL);
-            return 1;
+        switch (target) {
+            case GR_GL_TEXTURE_2D:
+                return 0;
+            case GR_GL_TEXTURE_EXTERNAL:
+                return 1;
+            case GR_GL_TEXTURE_RECTANGLE:
+                return 2;
+            default:
+                SkFAIL("Unexpected texture target type.");
+                return 0;
         }
     }
 
diff --git a/src/gpu/gl/GrGLProgramDataManager.cpp b/src/gpu/gl/GrGLProgramDataManager.cpp
index 54ca73a..06f568e 100644
--- a/src/gpu/gl/GrGLProgramDataManager.cpp
+++ b/src/gpu/gl/GrGLProgramDataManager.cpp
@@ -63,7 +63,8 @@
 
 void GrGLProgramDataManager::setSampler(UniformHandle u, int texUnit) const {
     const Uniform& uni = fUniforms[u.toIndex()];
-    SkASSERT(uni.fType == kSampler2D_GrSLType || uni.fType == kSamplerExternal_GrSLType);
+    SkASSERT(uni.fType == kSampler2D_GrSLType || uni.fType == kSamplerExternal_GrSLType ||
+             uni.fType == kSampler2DRect_GrSLType);
     SkASSERT(GrGLSLShaderVar::kNonArray == uni.fArrayCount);
     // FIXME: We still insert a single sampler uniform for every stage. If the shader does not
     // reference the sampler then the compiler may have optimized it out. Uncomment this assert
diff --git a/src/gpu/gl/GrGLProgramDesc.cpp b/src/gpu/gl/GrGLProgramDesc.cpp
index 4c95e2b..5edacf5 100644
--- a/src/gpu/gl/GrGLProgramDesc.cpp
+++ b/src/gpu/gl/GrGLProgramDesc.cpp
@@ -7,12 +7,19 @@
 #include "GrGLProgramDesc.h"
 
 #include "GrProcessor.h"
-#include "GrGLGpu.h"
 #include "GrPipeline.h"
 #include "SkChecksum.h"
+#include "gl/GrGLDefines.h"
+#include "gl/GrGLTexture.h"
+#include "gl/GrGLTypes.h"
 #include "glsl/GrGLSLFragmentProcessor.h"
 #include "glsl/GrGLSLFragmentShaderBuilder.h"
+#include "glsl/GrGLSLCaps.h"
 
+static uint16_t texture_target_key(GrGLenum target) {
+    SkASSERT((uint32_t)target < SK_MaxU16);
+    return target;
+}
 
 static void add_texture_key(GrProcessorKeyBuilder* b, const GrProcessor& proc,
                             const GrGLSLCaps& caps) {
@@ -25,10 +32,9 @@
     uint16_t* k16 = SkTCast<uint16_t*>(b->add32n(word32Count));
     for (int i = 0; i < numTextures; ++i) {
         const GrTextureAccess& access = proc.textureAccess(i);
-        bool isExternal = (GR_GL_TEXTURE_EXTERNAL ==
-                           static_cast<GrGLTexture*>(access.getTexture())->target());
-        k16[i] = caps.configTextureSwizzle(access.getTexture()->config()).asKey() |
-                 (isExternal ? 0xFF00 : 0x0000);
+        GrGLTexture* texture = static_cast<GrGLTexture*>(access.getTexture());
+        k16[i] = caps.configTextureSwizzle(texture->config()).asKey() |
+                 (texture_target_key(texture->target()) << 16);
     }
     // zero the last 16 bits if the number of textures is odd.
     if (numTextures & 0x1) {
diff --git a/src/gpu/gl/SkGLContext.cpp b/src/gpu/gl/SkGLContext.cpp
index ec318cb..07c61f7 100644
--- a/src/gpu/gl/SkGLContext.cpp
+++ b/src/gpu/gl/SkGLContext.cpp
@@ -152,3 +152,26 @@
     GLsync glsync = static_cast<GLsync>(fence);
     fGLDeleteSync(glsync);
 }
+
+GrGLint SkGLContext::createTextureRectangle(int width, int height, GrGLenum internalFormat,
+                                            GrGLenum externalFormat, GrGLenum externalType,
+                                            GrGLvoid* data) {
+    if (!(kGL_GrGLStandard == fGL->fStandard && GrGLGetVersion(fGL) >= GR_GL_VER(3, 2)) &&
+        !fGL->fExtensions.has("GL_ARB_texture_rectangle")) {
+        return 0;
+    }
+    GrGLuint id;
+    GR_GL_CALL(fGL, GenTextures(1, &id));
+    GR_GL_CALL(fGL, BindTexture(GR_GL_TEXTURE_RECTANGLE, id));
+    GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MAG_FILTER,
+                                  GR_GL_NEAREST));
+    GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_MIN_FILTER,
+                                  GR_GL_NEAREST));
+    GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_S,
+                                  GR_GL_CLAMP_TO_EDGE));    
+    GR_GL_CALL(fGL, TexParameteri(GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_WRAP_T,
+                                  GR_GL_CLAMP_TO_EDGE));
+    GR_GL_CALL(fGL, TexImage2D(GR_GL_TEXTURE_RECTANGLE, 0, internalFormat, width, height, 0,
+                               externalFormat, externalType, data));
+    return id;
+}
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
index 4503d1a..4a1b2e9 100644
--- a/src/gpu/gl/builders/GrGLProgramBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp
@@ -74,6 +74,8 @@
     GrGLTexture* glTexture = static_cast<GrGLTexture*>(access.getTexture());
     if (glTexture->target() == GR_GL_TEXTURE_EXTERNAL) {
         return kSamplerExternal_GrSLType;
+    } else if (glTexture->target() == GR_GL_TEXTURE_RECTANGLE) {
+        return kSampler2DRect_GrSLType;
     } else {
         SkASSERT(glTexture->target() == GR_GL_TEXTURE_2D);
         return kSampler2D_GrSLType;
diff --git a/src/gpu/glsl/GrGLSL.h b/src/gpu/glsl/GrGLSL.h
index ac38522..f2accc5 100644
--- a/src/gpu/glsl/GrGLSL.h
+++ b/src/gpu/glsl/GrGLSL.h
@@ -48,12 +48,23 @@
  * Gets the name of the function that should be used to sample a 2D texture. Coord type is used
  * to indicate whether the texture is sampled using projective textured (kVec3f) or not (kVec2f).
  */
-inline const char* GrGLSLTexture2DFunctionName(GrSLType coordType, GrGLSLGeneration glslGen) {
+inline const char* GrGLSLTexture2DFunctionName(GrSLType coordType, GrSLType samplerType,
+                                               GrGLSLGeneration glslGen) {
+    SkASSERT(GrSLTypeIsSamplerType(samplerType));
+    SkASSERT(kVec2f_GrSLType == coordType || kVec3f_GrSLType == coordType);
+    // GL_TEXTURE_RECTANGLE_ARB is written against OpenGL 2.0/GLSL 1.10. At that time there were
+    // separate texture*() functions. In OpenGL 3.0/GLSL 1.30 the different texture*() functions
+    // were deprecated in favor or the unified texture() function. RECTANGLE textures became
+    // standard in OpenGL 3.2/GLSL 1.50 and use texture(). It isn't completely clear what function
+    // should be used for RECTANGLE textures in GLSL versions >= 1.30 && < 1.50. We're going with
+    // using texture().
+    if (glslGen >= k130_GrGLSLGeneration) {
+        return (kVec2f_GrSLType == coordType) ? "texture" : "textureProj";
+    }
     if (kVec2f_GrSLType == coordType) {
-        return glslGen >= k130_GrGLSLGeneration ? "texture" : "texture2D";
+        return (samplerType == kSampler2DRect_GrSLType) ? "texture2DRect" : "texture2D";
     } else {
-        SkASSERT(kVec3f_GrSLType == coordType);
-        return glslGen >= k130_GrGLSLGeneration ? "textureProj" : "texture2DProj";
+        return (samplerType == kSampler2DRect_GrSLType) ? "texture2DRectProj" : "texture2DProj";
     }
 }
 
@@ -87,6 +98,8 @@
             return "sampler2D";
         case kSamplerExternal_GrSLType:
             return "samplerExternalOES";
+        case kSampler2DRect_GrSLType:
+            return "sampler2DRect";
         default:
             SkFAIL("Unknown shader var type.");
             return ""; // suppress warning
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.cpp b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
index 2d2ff87..f1ede1d 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.cpp
@@ -62,8 +62,9 @@
                                               GrSLType varyingType) const {
     const GrGLSLCaps* glslCaps = fProgramBuilder->glslCaps();
     GrGLSLUniformHandler* uniformHandler = fProgramBuilder->uniformHandler();
+    GrSLType samplerType = uniformHandler->getUniformVariable(sampler.fSamplerUniform).getType();
     out->appendf("%s(%s, %s)",
-                 GrGLSLTexture2DFunctionName(varyingType, glslCaps->generation()),
+                 GrGLSLTexture2DFunctionName(varyingType, samplerType, glslCaps->generation()),
                  uniformHandler->getUniformCStr(sampler.fSamplerUniform),
                  coordName);
 
diff --git a/tests/RectangleTextureTest.cpp b/tests/RectangleTextureTest.cpp
new file mode 100644
index 0000000..a98dd34
--- /dev/null
+++ b/tests/RectangleTextureTest.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrDrawContext.h"
+#include "gl/GrGLGpu.h"
+#include "gl/GrGLUtil.h"
+#include "gl/SkGLContext.h"
+
+static void test_read_pixels(skiatest::Reporter* reporter, GrContext* context,
+                             GrTexture* rectangleTexture, uint32_t expectedPixelValues[]) {
+    int pixelCnt = rectangleTexture->width() * rectangleTexture->height();
+    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
+    memset(pixels.get(), 0, sizeof(uint32_t)*pixelCnt);
+    bool read = rectangleTexture->readPixels(0, 0, rectangleTexture->width(),
+                                            rectangleTexture->height(), kRGBA_8888_GrPixelConfig,
+                                            pixels.get());
+    if (!read) {
+        ERRORF(reporter, "Error reading rectangle texture.");
+    }
+    for (int i = 0; i < pixelCnt; ++i) {
+        if (pixels.get()[i] != expectedPixelValues[i]) {
+            ERRORF(reporter, "Error, rectangle texture pixel value %d should be 0x%08x,"
+                             " got 0x%08x.", i, expectedPixelValues[i], pixels.get()[i]);
+            break;
+        }
+    }
+}
+
+static void test_write_pixels(skiatest::Reporter* reporter, GrContext* context,
+                              GrTexture* rectangleTexture) {
+    int pixelCnt = rectangleTexture->width() * rectangleTexture->height();
+    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
+    for (int y = 0; y < rectangleTexture->width(); ++y) {
+        for (int x = 0; x < rectangleTexture->height(); ++x) {
+            pixels.get()[y * rectangleTexture->width() + x] = GrColorPackRGBA(x, y, x + y, x * y);
+        }
+    }
+    bool write = rectangleTexture->writePixels(0, 0, rectangleTexture->width(),
+                                               rectangleTexture->height(), kRGBA_8888_GrPixelConfig,
+                                               pixels.get());
+    if (!write) {
+        ERRORF(reporter, "Error writing to rectangle texture.");
+    }
+    test_read_pixels(reporter, context, rectangleTexture, pixels.get());
+}
+
+static void test_copy_surface_src(skiatest::Reporter* reporter, GrContext* context,
+                                  GrTexture* rectangleTexture, uint32_t expectedPixelValues[]) {
+    GrSurfaceDesc copyDstDesc;
+    copyDstDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    copyDstDesc.fWidth = rectangleTexture->width();
+    copyDstDesc.fHeight = rectangleTexture->height();
+    copyDstDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    SkAutoTUnref<GrTexture> dst(context->textureProvider()->createTexture(copyDstDesc, true));
+    context->copySurface(dst, rectangleTexture);
+    test_read_pixels(reporter, context, dst, expectedPixelValues);
+}
+
+static void test_copy_surface_dst(skiatest::Reporter* reporter, GrContext* context,
+                                  GrTexture* rectangleTexture) {
+    int pixelCnt = rectangleTexture->width() * rectangleTexture->height();
+    SkAutoTMalloc<uint32_t> pixels(pixelCnt);
+    for (int y = 0; y < rectangleTexture->width(); ++y) {
+        for (int x = 0; x < rectangleTexture->height(); ++x) {
+            pixels.get()[y * rectangleTexture->width() + x] = GrColorPackRGBA(y, x, x * y, x *+ y);
+        }
+    }
+
+    GrSurfaceDesc copySrcDesc;
+    copySrcDesc.fConfig = kRGBA_8888_GrPixelConfig;
+    copySrcDesc.fWidth = rectangleTexture->width();
+    copySrcDesc.fHeight = rectangleTexture->height();
+    copySrcDesc.fFlags = kRenderTarget_GrSurfaceFlag;
+    SkAutoTUnref<GrTexture> src(context->textureProvider()->createTexture(copySrcDesc, true,
+                                                                          pixels.get(), 0));
+
+    context->copySurface(rectangleTexture, src);
+    test_read_pixels(reporter, context, rectangleTexture, pixels.get());
+}
+
+static void test_clear(skiatest::Reporter* reporter, GrContext* context,
+                       GrTexture* rectangleTexture) {
+    if (rectangleTexture->asRenderTarget()) {
+        SkAutoTUnref<GrDrawContext> dc(context->drawContext(rectangleTexture->asRenderTarget()));
+        if (!dc) {
+            ERRORF(reporter, "Could not get GrDrawContext for rectangle texture.");
+            return;
+        }
+
+        // Clear the whole thing.
+        GrColor color0 = GrColorPackRGBA(0xA, 0xB, 0xC, 0xD);
+        dc->clear(nullptr, color0, false);
+
+        int w = rectangleTexture->width();
+        int h = rectangleTexture->height();
+        int pixelCnt = w * h;
+        SkAutoTMalloc<uint32_t> expectedPixels(pixelCnt);
+
+        // The clear color is a GrColor, our readback is to kRGBA_8888, which may be different.
+        uint32_t expectedColor0;
+        uint8_t* expectedBytes0 = SkTCast<uint8_t*>(&expectedColor0);
+        expectedBytes0[0] = GrColorUnpackR(color0);
+        expectedBytes0[1] = GrColorUnpackG(color0);
+        expectedBytes0[2] = GrColorUnpackB(color0);
+        expectedBytes0[3] = GrColorUnpackA(color0);
+        for (int i = 0; i < rectangleTexture->width() * rectangleTexture->height(); ++i) {
+            expectedPixels.get()[i] = expectedColor0;
+        }
+
+        // Clear the the top to a different color.
+        GrColor color1 = GrColorPackRGBA(0x1, 0x2, 0x3, 0x4);
+        SkIRect rect = SkIRect::MakeWH(w, h/2);
+        dc->clear(&rect, color1, false);
+
+        uint32_t expectedColor1;
+        uint8_t* expectedBytes1 = SkTCast<uint8_t*>(&expectedColor1);
+        expectedBytes1[0] = GrColorUnpackR(color1);
+        expectedBytes1[1] = GrColorUnpackG(color1);
+        expectedBytes1[2] = GrColorUnpackB(color1);
+        expectedBytes1[3] = GrColorUnpackA(color1);
+
+        for (int y = 0; y < h/2; ++y) {
+            for (int x = 0; x < w; ++x) {
+                expectedPixels.get()[y * h + x] = expectedColor1;
+            }
+        }
+
+        test_read_pixels(reporter, context, rectangleTexture, expectedPixels.get());
+    }
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(RectangleTexture, reporter, context, glContext) {    
+    static const int kWidth = 13;
+    static const int kHeight = 13;
+
+    GrColor pixels[kWidth * kHeight];
+    for (int y = 0; y < kHeight; ++y) {
+        for (int x = 0; x < kWidth; ++x) {
+            pixels[y * kWidth + x] = y * kWidth + x;
+        }
+    }
+
+    for (int origin = 0; origin < 2; ++origin) {
+        GrGLuint rectTexID = glContext->createTextureRectangle(kWidth, kHeight, GR_GL_RGBA,
+                                                               GR_GL_RGBA, GR_GL_UNSIGNED_BYTE,
+                                                               pixels);
+
+        if (!rectTexID) {
+            return;
+        }
+
+        // Let GrContext know that we messed with the GL context directly.
+        context->resetContext();
+
+        // Wrap the rectangle texture ID in a GrTexture
+        GrGLTextureInfo rectangleInfo;
+        rectangleInfo.fID = rectTexID;
+        rectangleInfo.fTarget = GR_GL_TEXTURE_RECTANGLE;
+
+        GrBackendTextureDesc rectangleDesc;
+        rectangleDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
+        rectangleDesc.fConfig = kRGBA_8888_GrPixelConfig;
+        rectangleDesc.fWidth = kWidth;
+        rectangleDesc.fHeight = kHeight;
+        rectangleDesc.fOrigin = origin ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
+        rectangleDesc.fTextureHandle = reinterpret_cast<GrBackendObject>(&rectangleInfo);
+
+        GrColor refPixels[kWidth * kHeight];
+        bool flipRef = rectangleDesc.fOrigin == kBottomLeft_GrSurfaceOrigin;
+        for (int y = 0; y < kHeight; ++y) {
+            for (int x = 0; x < kWidth; ++x) {
+                int y0 = flipRef ? kHeight - y - 1 : y;
+                refPixels[y * kWidth + x] = pixels[y0 * kWidth + x];
+            }
+        }
+
+        SkAutoTUnref<GrTexture> rectangleTexture(
+            context->textureProvider()->wrapBackendTexture(rectangleDesc));
+        if (!rectangleTexture) {
+            ERRORF(reporter, "Error wrapping rectangle texture in GrTexture.");
+            GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID));
+            continue;
+        }
+
+        test_read_pixels(reporter, context, rectangleTexture, refPixels);
+
+        test_copy_surface_src(reporter, context, rectangleTexture, refPixels);
+
+        test_copy_surface_dst(reporter, context, rectangleTexture);
+
+        test_write_pixels(reporter, context, rectangleTexture);
+
+        test_clear(reporter, context, rectangleTexture);
+
+        GR_GL_CALL(glContext->gl(), DeleteTextures(1, &rectTexID));
+    }
+}
+
+#endif