Move GL-specific source code to make room for D3D back end.



git-svn-id: http://skia.googlecode.com/svn/trunk@3165 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLContextInfo.cpp b/src/gpu/gl/GrGLContextInfo.cpp
new file mode 100644
index 0000000..e6257e7
--- /dev/null
+++ b/src/gpu/gl/GrGLContextInfo.cpp
@@ -0,0 +1,74 @@
+#include "GrGLContextInfo.h"
+
+GrGLContextInfo::~GrGLContextInfo() {
+    GrSafeUnref(fInterface);
+}
+
+GrGLContextInfo::GrGLContextInfo() {
+    this->reset();
+}
+
+GrGLContextInfo::GrGLContextInfo(const GrGLInterface* interface) {
+    fInterface = NULL;
+    this->initialize(interface);
+}
+
+GrGLContextInfo::GrGLContextInfo(const GrGLContextInfo& ctx) {
+    fInterface = NULL;
+    *this = ctx;
+}
+
+GrGLContextInfo& GrGLContextInfo::operator = (const GrGLContextInfo& ctx) {
+    GrSafeAssign(fInterface, ctx.fInterface);
+    fBindingInUse = ctx.fBindingInUse;
+    fGLVersion = ctx.fGLVersion;
+    fGLSLGeneration = ctx.fGLSLGeneration;
+    fExtensionString = ctx.fExtensionString;
+    return *this;
+}
+
+void GrGLContextInfo::reset() {
+    GrSafeSetNull(fInterface);
+    fBindingInUse = kNone_GrGLBinding;
+    fGLVersion = GR_GL_VER(0, 0);
+    fGLSLGeneration = static_cast<GrGLSLGeneration>(0);
+    fExtensionString = "";
+}
+
+bool GrGLContextInfo::initialize(const GrGLInterface* interface) {
+    this->reset();
+    // We haven't validated the GrGLInterface yet, so check for GetString
+    // function pointer
+    if (NULL != interface->fGetString) {
+
+        const GrGLubyte* verUByte;
+        GR_GL_CALL_RET(interface, verUByte, GetString(GR_GL_VERSION));
+        const char* ver = reinterpret_cast<const char*>(verUByte);
+        GrGLBinding binding = GrGLGetBindingInUseFromString(ver);
+
+        if (!interface->validate(fBindingInUse)) {
+
+            fInterface = interface;
+            interface->ref();
+
+            fBindingInUse = binding;
+
+            fGLVersion = GrGLGetVersionFromString(ver);
+
+            fGLSLGeneration = GrGetGLSLGeneration(fBindingInUse,
+                                                  this->interface());
+
+            const GrGLubyte* ext;
+            GR_GL_CALL_RET(interface, ext, GetString(GR_GL_EXTENSIONS));
+            fExtensionString = reinterpret_cast<const char*>(ext);
+
+            return true;
+        }
+    }
+    return false;
+}
+
+bool GrGLContextInfo::isInitialized() const {
+    return kNone_GrGLBinding != fBindingInUse;
+}
+
diff --git a/src/gpu/gl/GrGLContextInfo.h b/src/gpu/gl/GrGLContextInfo.h
new file mode 100644
index 0000000..cac8959
--- /dev/null
+++ b/src/gpu/gl/GrGLContextInfo.h
@@ -0,0 +1,68 @@
+#include "GrGLInterface.h"
+#include "GrGLSL.h"
+
+#include "SkString.h"
+
+/**
+ * Encapsulates information about an OpenGL context including the GrGLInterface
+ * used to make GL calls, the OpenGL version, the GrGLBinding type of the
+ * context, and GLSL version.
+ */
+class GrGLContextInfo {
+public:
+
+    /**
+     * Default constructor, creates an uninitialized GrGLContextInfo
+     */
+    GrGLContextInfo();
+
+    /**
+     * Creates a GrGLContextInfo from a GrGLInterface and the currently
+     * bound OpenGL context accesible by the GrGLInterface.
+     */
+    GrGLContextInfo(const GrGLInterface* interface);
+
+    /**
+     * Copies a GrGLContextInfo
+     */
+    GrGLContextInfo(const GrGLContextInfo& ctx);
+
+    ~GrGLContextInfo();
+
+    /**
+     * Copies a GrGLContextInfo
+     */
+    GrGLContextInfo& operator = (const GrGLContextInfo& ctx);
+
+    /**
+     * Initializes a GrGLContextInfo from a GrGLInterface and the currently
+     * bound OpenGL context accessible by the GrGLInterface.
+     */
+    bool initialize(const GrGLInterface* interface);
+    bool isInitialized() const;
+
+    const GrGLInterface* interface() const { return fInterface; }
+    GrGLBinding binding() const { return fBindingInUse; }
+    GrGLVersion version() const { return fGLVersion; }
+    GrGLSLGeneration glslGeneration() const { return fGLSLGeneration; }
+
+    /**
+     * Checks for extension support using a cached copy of the GL_EXTENSIONS
+     * string.
+     */
+    bool hasExtension(const char* ext) const {
+        if (!this->isInitialized()) {
+            return false;
+        }
+        return GrGLHasExtensionFromString(ext, fExtensionString.c_str());
+    }
+
+private:
+    void reset();
+
+    const GrGLInterface* fInterface;
+    GrGLBinding          fBindingInUse;
+    GrGLVersion          fGLVersion;
+    GrGLSLGeneration     fGLSLGeneration;
+    SkString             fExtensionString;
+};
diff --git a/src/gpu/gl/GrGLCreateNativeInterface_none.cpp b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
new file mode 100644
index 0000000..7de5912
--- /dev/null
+++ b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLInterface.h"
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+    return NULL;
+}
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
new file mode 100644
index 0000000..cdac75f
--- /dev/null
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -0,0 +1,506 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLInterface.h"
+#include "../GrTDArray.h"
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLActiveTexture(GrGLenum texture) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLAttachShader(GrGLuint program, GrGLuint shader) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBeginQuery(GrGLenum target, GrGLuint id) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindAttribLocation(GrGLuint program, GrGLuint index, const char* name) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindTexture(GrGLenum target, GrGLuint texture) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlendColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFragDataLocation(GrGLuint program, GrGLuint colorNumber, const GrGLchar* name) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlendFunc(GrGLenum sfactor, GrGLenum dfactor) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferData(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBufferSubData(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid* data) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClear(GrGLbitfield mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClearColor(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLClearStencil(GrGLint s) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLColorMask(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCompileShader(GrGLuint shader) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCompressedTexImage2D(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLCullFace(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDepthMask(GrGLboolean flag) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDisable(GrGLenum cap) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDisableVertexAttribArray(GrGLuint index) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawArrays(GrGLenum mode, GrGLint first, GrGLsizei count) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawBuffer(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawBuffers(GrGLsizei n, const GrGLenum* bufs) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDrawElements(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEnable(GrGLenum cap) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEnableVertexAttribArray(GrGLuint index) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLEndQuery(GrGLenum target) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFinish() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFlush() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFrontFace(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLLineWidth(GrGLfloat width) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLLinkProgram(GrGLuint program) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLPixelStorei(GrGLenum pname, GrGLint param) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLQueryCounter(GrGLuint id, GrGLenum target) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadBuffer(GrGLenum src) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLScissor(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLShaderSource(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilFunc(GrGLenum func, GrGLint ref, GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilFuncSeparate(GrGLenum face, GrGLenum func, GrGLint ref, GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilMask(GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilMaskSeparate(GrGLenum face, GrGLuint mask) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilOp(GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilOpSeparate(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexImage2D(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexParameteri(GrGLenum target, GrGLenum pname, GrGLint param) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexStorage2D(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1f(GrGLint location, GrGLfloat v0) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1i(GrGLint location, GrGLint v0) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2f(GrGLint location, GrGLfloat v0, GrGLfloat v1) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2i(GrGLint location, GrGLint v0, GrGLint v1) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform2iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform3iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4f(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4i(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4fv(GrGLint location, GrGLsizei count, const GrGLfloat* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform4iv(GrGLint location, GrGLsizei count, const GrGLint* v) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix2fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix3fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniformMatrix4fv(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLUseProgram(GrGLuint program) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLVertexAttrib4fv(GrGLuint indx, const GrGLfloat* values) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLVertexAttribPointer(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLViewport(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFramebuffer(GrGLenum target, GrGLuint framebuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindRenderbuffer(GrGLenum target, GrGLuint renderbuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteFramebuffers(GrGLsizei n, const GrGLuint *framebuffers) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteRenderbuffers(GrGLsizei n, const GrGLuint *renderbuffers) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferRenderbuffer(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFramebufferTexture2D(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetFramebufferAttachmentParameteriv(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetRenderbufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLRenderbufferStorage(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLRenderbufferStorageMultisample(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBlitFramebuffer(GrGLint srcX0, GrGLint srcY0, GrGLint srcX1, GrGLint srcY1, GrGLint dstX0, GrGLint dstY0, GrGLint dstX1, GrGLint dstY1, GrGLbitfield mask, GrGLenum filter) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLResolveMultisampleFramebuffer() {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindFragDataLocationIndexed(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name) {}
+
+GrGLenum GR_GL_FUNCTION_TYPE nullGLCheckFramebufferStatus(GrGLenum target) {
+    return GR_GL_FRAMEBUFFER_COMPLETE;
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateProgram() {
+    static int gCurrID = 0;
+    return ++gCurrID;
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE nullGLCreateShader(GrGLenum type) {
+    static int gCurrID = 0;
+    return ++gCurrID;
+}
+
+// same delete used for shaders and programs
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDelete(GrGLuint program) {
+}
+
+// same function used for all glGen*(GLsize i, GLuint*) functions
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGenIds(GrGLsizei n, GrGLuint* ids) {
+    static int gCurrID = 0;
+    for (int i = 0; i < n; ++i) {
+        ids[i] = ++gCurrID;
+    }
+}
+// same delete function for all glDelete*(GLsize i, const GLuint*) except buffers
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteIds(GrGLsizei n, const GrGLuint* ids) {}
+
+// In debug builds we do asserts that ensure we agree with GL about when a buffer
+// is mapped.
+static GrTDArray<GrGLuint> gMappedBuffers;
+static GrGLuint gCurrArrayBuffer;
+static GrGLuint gCurrElementArrayBuffer;
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLBindBuffer(GrGLenum target, GrGLuint buffer) {
+    switch (target) {
+    case GR_GL_ARRAY_BUFFER:
+        gCurrArrayBuffer = buffer;
+        break;
+    case GR_GL_ELEMENT_ARRAY_BUFFER:
+        gCurrElementArrayBuffer = buffer;
+        break;
+    }
+}
+
+// deleting a bound buffer has the side effect of binding 0
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLDeleteBuffers(GrGLsizei n, const GrGLuint* ids) {
+    for (int i = 0; i < n; ++i) {
+        if (ids[i] == gCurrArrayBuffer) {
+            gCurrArrayBuffer = 0;
+        }
+        if (ids[i] == gCurrElementArrayBuffer) {
+            gCurrElementArrayBuffer = 0;
+        }
+        for (int j = 0; j < gMappedBuffers.count(); ++j) {
+            if (gMappedBuffers[j] == ids[i]) {
+                gMappedBuffers.remove(j);
+                // don't break b/c we didn't check for dupes on insert
+                --j;
+            }
+        }
+    }
+}
+
+GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
+    // We just reserve 32MB of RAM for all locks and hope its big enough
+    static SkAutoMalloc gBufferData(32 * (1 << 20));
+    GrGLuint buf = 0;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buf = gCurrArrayBuffer;
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buf = gCurrElementArrayBuffer;
+            break;
+    }
+    if (buf) {
+        *gMappedBuffers.append() = buf;
+    }
+    return gBufferData.get();
+}
+
+GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
+    GrGLuint buf = 0;
+    switch (target) {
+    case GR_GL_ARRAY_BUFFER:
+        buf = gCurrArrayBuffer;
+        break;
+    case GR_GL_ELEMENT_ARRAY_BUFFER:
+        buf = gCurrElementArrayBuffer;
+        break;
+    }
+    if (buf) {
+        for (int i = 0; i < gMappedBuffers.count(); ++i) {
+            if (gMappedBuffers[i] == buf) {
+                gMappedBuffers.remove(i);
+                // don't break b/c we didn't check for dupes on insert
+                --i;
+            }
+        }
+    }
+    return GR_GL_TRUE;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetBufferParameteriv(GrGLenum target, GrGLenum pname, GrGLint* params) {
+    switch (pname) {
+        case GR_GL_BUFFER_MAPPED: {
+            *params = GR_GL_FALSE;
+            GrGLuint buf = 0;
+            switch (target) {
+                case GR_GL_ARRAY_BUFFER:
+                    buf = gCurrArrayBuffer;
+                    break;
+                case GR_GL_ELEMENT_ARRAY_BUFFER:
+                    buf = gCurrElementArrayBuffer;
+                    break;  
+            }
+            if (buf) {
+                for (int i = 0; i < gMappedBuffers.count(); ++i) {
+                    if (gMappedBuffers[i] == buf) {
+                        *params = GR_GL_TRUE;
+                        break;
+                    }
+                }
+            }
+            break; }
+        default:
+            GrCrash("Unexpected pname to GetBufferParamateriv");
+            break;
+    }
+};
+
+GrGLenum GR_GL_FUNCTION_TYPE nullGLGetError() {
+    return GR_GL_NO_ERROR;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetIntegerv(GrGLenum pname, GrGLint* params) {
+    switch (pname) {
+        case GR_GL_STENCIL_BITS:
+            *params = 8;
+            break;
+        case GR_GL_SAMPLES:
+            *params = 1;
+            break;
+        case GR_GL_FRAMEBUFFER_BINDING:
+            *params = 0;
+            break;
+        case GR_GL_VIEWPORT:
+            params[0] = 0;
+            params[1] = 0;
+            params[2] = 800;
+            params[3] = 600;
+            break;
+        case GR_GL_MAX_TEXTURE_IMAGE_UNITS:
+            *params = 8;
+            break;
+        case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
+            *params = 16;
+            break;
+        case GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS:
+            *params = 16 * 4;
+            break;
+        case GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
+            *params = 0;
+            break;
+        case GR_GL_COMPRESSED_TEXTURE_FORMATS:
+            break;
+        case GR_GL_MAX_TEXTURE_SIZE:
+            *params = 8192;
+            break;
+        case GR_GL_MAX_RENDERBUFFER_SIZE:
+            *params = 8192;
+            break;
+        case GR_GL_MAX_SAMPLES:
+            *params = 32;
+            break;
+        case GR_GL_MAX_VERTEX_ATTRIBS:
+            *params = 16;
+            break;
+        case GR_GL_MAX_TEXTURE_UNITS:
+            *params = 8;
+            break;
+        default:
+            GrCrash("Unexpected pname to GetIntegerv");
+    }
+}
+// used for both the program and shader info logs
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetInfoLog(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog) {
+    if (length) {
+        *length = 0;
+    }
+    if (bufsize > 0) {
+        *infolog = 0;
+    }
+}
+
+// used for both the program and shader params
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetShaderOrProgramiv(GrGLuint program, GrGLenum pname, GrGLint* params) {
+    switch (pname) {
+        case GR_GL_LINK_STATUS:  // fallthru
+        case GR_GL_COMPILE_STATUS:
+            *params = GR_GL_TRUE;
+            break;
+        case GR_GL_INFO_LOG_LENGTH:
+            *params = 0;
+            break;
+        // we don't expect any other pnames
+        default:
+            GrCrash("Unexpected pname to GetProgramiv");
+            break;
+    }
+}
+
+namespace {
+template <typename T>
+void query_result(GrGLenum GLtarget, GrGLenum pname, T *params) {
+    switch (pname) {
+        case GR_GL_QUERY_RESULT_AVAILABLE:
+            *params = GR_GL_TRUE;
+            break;
+        case GR_GL_QUERY_RESULT:
+            *params = 0;
+            break;
+        default:
+            GrCrash("Unexpected pname passed to GetQueryObject.");
+            break;
+    }
+}
+}
+
+// Queries on the null GL just don't do anything at all. We could potentially make
+// the timers work.
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryiv(GrGLenum GLtarget, GrGLenum pname, GrGLint *params) {
+    switch (pname) {
+        case GR_GL_CURRENT_QUERY:
+            *params = 0;
+            break;
+        case GR_GL_QUERY_COUNTER_BITS:
+            *params = 32;
+            break;
+        default:
+            GrCrash("Unexpected pname passed GetQueryiv.");
+    }
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjecti64v(GrGLuint id, GrGLenum pname, GrGLint64 *params) {
+    query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectiv(GrGLuint id, GrGLenum pname, GrGLint *params) {
+    query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectui64v(GrGLuint id, GrGLenum pname, GrGLuint64 *params) {
+    query_result(id, pname, params);
+}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetQueryObjectuiv(GrGLuint id, GrGLenum pname, GrGLuint *params) {
+    query_result(id, pname, params);
+}
+
+const GrGLubyte* GR_GL_FUNCTION_TYPE nullGLGetString(GrGLenum name) {
+    switch (name) {
+        case GR_GL_EXTENSIONS:
+            return (const GrGLubyte*)"GL_ARB_framebuffer_object GL_ARB_blend_func_extended GL_ARB_timer_query GL_ARB_draw_buffers GL_ARB_occlusion_query GL_EXT_blend_color GL_EXT_stencil_wrap";
+        case GR_GL_VERSION:
+            return (const GrGLubyte*)"4.0 Null GL";
+        case GR_GL_SHADING_LANGUAGE_VERSION:
+            return (const GrGLubyte*)"4.20.8 Null GLSL";
+        default:
+            GrCrash("Unexpected name to GetString");
+            return NULL;
+    }
+}
+
+// we used to use this to query stuff about externally created textures, now we just
+// require clients to tell us everything about the texture.
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLGetTexLevelParameteriv(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint* params) {
+    GrCrash("Should never query texture parameters.");
+}
+
+GrGLint GR_GL_FUNCTION_TYPE nullGLGetUniformLocation(GrGLuint program, const char* name) {
+    static int gUniLocation = 0;
+    return ++gUniLocation;
+}
+
+const GrGLInterface* GrGLCreateNullInterface() {
+    // The gl functions are not context-specific so we create one global 
+    // interface
+    static SkAutoTUnref<GrGLInterface> glInterface;
+    if (!glInterface.get()) {
+        GrGLInterface* interface = new GrGLInterface;
+        glInterface.reset(interface);
+        interface->fBindingsExported = kDesktop_GrGLBinding;
+        interface->fActiveTexture = nullGLActiveTexture;
+        interface->fAttachShader = nullGLAttachShader;
+        interface->fBeginQuery = nullGLBeginQuery;
+        interface->fBindAttribLocation = nullGLBindAttribLocation;
+        interface->fBindBuffer = nullGLBindBuffer;
+        interface->fBindFragDataLocation = nullGLBindFragDataLocation;
+        interface->fBindTexture = nullGLBindTexture;
+        interface->fBlendColor = nullGLBlendColor;
+        interface->fBlendFunc = nullGLBlendFunc;
+        interface->fBufferData = nullGLBufferData;
+        interface->fBufferSubData = nullGLBufferSubData;
+        interface->fClear = nullGLClear;
+        interface->fClearColor = nullGLClearColor;
+        interface->fClearStencil = nullGLClearStencil;
+        interface->fColorMask = nullGLColorMask;
+        interface->fCompileShader = nullGLCompileShader;
+        interface->fCompressedTexImage2D = nullGLCompressedTexImage2D;
+        interface->fCreateProgram = nullGLCreateProgram;
+        interface->fCreateShader = nullGLCreateShader;
+        interface->fCullFace = nullGLCullFace;
+        interface->fDeleteBuffers = nullGLDeleteBuffers;
+        interface->fDeleteProgram = nullGLDelete;
+        interface->fDeleteQueries = nullGLDeleteIds;
+        interface->fDeleteShader = nullGLDelete;
+        interface->fDeleteTextures = nullGLDeleteIds;
+        interface->fDepthMask = nullGLDepthMask;
+        interface->fDisable = nullGLDisable;
+        interface->fDisableVertexAttribArray = nullGLDisableVertexAttribArray;
+        interface->fDrawArrays = nullGLDrawArrays;
+        interface->fDrawBuffer = nullGLDrawBuffer;
+        interface->fDrawBuffers = nullGLDrawBuffers;
+        interface->fDrawElements = nullGLDrawElements;
+        interface->fEnable = nullGLEnable;
+        interface->fEnableVertexAttribArray = nullGLEnableVertexAttribArray;
+        interface->fEndQuery = nullGLEndQuery;
+        interface->fFinish = nullGLFinish;
+        interface->fFlush = nullGLFlush;
+        interface->fFrontFace = nullGLFrontFace;
+        interface->fGenBuffers = nullGLGenIds;
+        interface->fGenQueries = nullGLGenIds;
+        interface->fGenTextures = nullGLGenIds;
+        interface->fGetBufferParameteriv = nullGLGetBufferParameteriv;
+        interface->fGetError = nullGLGetError;
+        interface->fGetIntegerv = nullGLGetIntegerv;
+        interface->fGetQueryObjecti64v = nullGLGetQueryObjecti64v;
+        interface->fGetQueryObjectiv = nullGLGetQueryObjectiv;
+        interface->fGetQueryObjectui64v = nullGLGetQueryObjectui64v;
+        interface->fGetQueryObjectuiv = nullGLGetQueryObjectuiv;
+        interface->fGetQueryiv = nullGLGetQueryiv;
+        interface->fGetProgramInfoLog = nullGLGetInfoLog;
+        interface->fGetProgramiv = nullGLGetShaderOrProgramiv;
+        interface->fGetShaderInfoLog = nullGLGetInfoLog;
+        interface->fGetShaderiv = nullGLGetShaderOrProgramiv;
+        interface->fGetString = nullGLGetString;
+        interface->fGetTexLevelParameteriv = nullGLGetTexLevelParameteriv;
+        interface->fGetUniformLocation = nullGLGetUniformLocation;
+        interface->fLineWidth = nullGLLineWidth;
+        interface->fLinkProgram = nullGLLinkProgram;
+        interface->fPixelStorei = nullGLPixelStorei;
+        interface->fQueryCounter = nullGLQueryCounter;
+        interface->fReadBuffer = nullGLReadBuffer;
+        interface->fReadPixels = nullGLReadPixels;
+        interface->fScissor = nullGLScissor;
+        interface->fShaderSource = nullGLShaderSource;
+        interface->fStencilFunc = nullGLStencilFunc;
+        interface->fStencilFuncSeparate = nullGLStencilFuncSeparate;
+        interface->fStencilMask = nullGLStencilMask;
+        interface->fStencilMaskSeparate = nullGLStencilMaskSeparate;
+        interface->fStencilOp = nullGLStencilOp;
+        interface->fStencilOpSeparate = nullGLStencilOpSeparate;
+        interface->fTexImage2D = nullGLTexImage2D;
+        interface->fTexParameteri = nullGLTexParameteri;
+        interface->fTexSubImage2D = nullGLTexSubImage2D;
+        interface->fTexStorage2D = nullGLTexStorage2D;
+        interface->fUniform1f = nullGLUniform1f;
+        interface->fUniform1i = nullGLUniform1i;
+        interface->fUniform1fv = nullGLUniform1fv;
+        interface->fUniform1iv = nullGLUniform1iv;
+        interface->fUniform2f = nullGLUniform2f;
+        interface->fUniform2i = nullGLUniform2i;
+        interface->fUniform2fv = nullGLUniform2fv;
+        interface->fUniform2iv = nullGLUniform2iv;
+        interface->fUniform3f = nullGLUniform3f;
+        interface->fUniform3i = nullGLUniform3i;
+        interface->fUniform3fv = nullGLUniform3fv;
+        interface->fUniform3iv = nullGLUniform3iv;
+        interface->fUniform4f = nullGLUniform4f;
+        interface->fUniform4i = nullGLUniform4i;
+        interface->fUniform4fv = nullGLUniform4fv;
+        interface->fUniform4iv = nullGLUniform4iv;
+        interface->fUniformMatrix2fv = nullGLUniformMatrix2fv;
+        interface->fUniformMatrix3fv = nullGLUniformMatrix3fv;
+        interface->fUniformMatrix4fv = nullGLUniformMatrix4fv;
+        interface->fUseProgram = nullGLUseProgram;
+        interface->fVertexAttrib4fv = nullGLVertexAttrib4fv;
+        interface->fVertexAttribPointer = nullGLVertexAttribPointer;
+        interface->fViewport = nullGLViewport;
+        interface->fBindFramebuffer = nullGLBindFramebuffer;
+        interface->fBindRenderbuffer = nullGLBindRenderbuffer;
+        interface->fCheckFramebufferStatus = nullGLCheckFramebufferStatus;
+        interface->fDeleteFramebuffers = nullGLDeleteFramebuffers;
+        interface->fDeleteRenderbuffers = nullGLDeleteRenderbuffers;
+        interface->fFramebufferRenderbuffer = nullGLFramebufferRenderbuffer;
+        interface->fFramebufferTexture2D = nullGLFramebufferTexture2D;
+        interface->fGenFramebuffers = nullGLGenIds;
+        interface->fGenRenderbuffers = nullGLGenIds;
+        interface->fGetFramebufferAttachmentParameteriv = nullGLGetFramebufferAttachmentParameteriv;
+        interface->fGetRenderbufferParameteriv = nullGLGetRenderbufferParameteriv;
+        interface->fRenderbufferStorage = nullGLRenderbufferStorage;
+        interface->fRenderbufferStorageMultisample = nullGLRenderbufferStorageMultisample;
+        interface->fBlitFramebuffer = nullGLBlitFramebuffer;
+        interface->fResolveMultisampleFramebuffer = nullGLResolveMultisampleFramebuffer;
+        interface->fMapBuffer = nullGLMapBuffer;
+        interface->fUnmapBuffer = nullGLUnmapBuffer;
+        interface->fBindFragDataLocationIndexed = nullGLBindFragDataLocationIndexed;
+    }
+    glInterface.get()->ref();
+    return glInterface.get();
+}
diff --git a/src/gpu/gl/GrGLDefaultInterface_native.cpp b/src/gpu/gl/GrGLDefaultInterface_native.cpp
new file mode 100644
index 0000000..7b8b84a
--- /dev/null
+++ b/src/gpu/gl/GrGLDefaultInterface_native.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLInterface.h"
+
+const GrGLInterface* GrGLDefaultInterface() {
+    return GrGLCreateNativeInterface();
+}
diff --git a/src/gpu/gl/GrGLDefaultInterface_none.cpp b/src/gpu/gl/GrGLDefaultInterface_none.cpp
new file mode 100644
index 0000000..2cca135
--- /dev/null
+++ b/src/gpu/gl/GrGLDefaultInterface_none.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLInterface.h"
+
+const GrGLInterface* GrGLDefaultInterface() {
+    return NULL;
+}
diff --git a/src/gpu/gl/GrGLIRect.h b/src/gpu/gl/GrGLIRect.h
new file mode 100644
index 0000000..e94fa21
--- /dev/null
+++ b/src/gpu/gl/GrGLIRect.h
@@ -0,0 +1,74 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGLIRect_DEFINED
+#define GrGLIRect_DEFINED
+
+#include "GrGLInterface.h"
+
+/**
+ * Helper struct for dealing with the fact that Ganesh and GL use different
+ * window coordinate systems (top-down vs bottom-up)
+ */
+struct GrGLIRect {
+    GrGLint   fLeft;
+    GrGLint   fBottom;
+    GrGLsizei fWidth;
+    GrGLsizei fHeight;
+
+    void pushToGLViewport(const GrGLInterface* gl) const {
+        GR_GL_CALL(gl, Viewport(fLeft, fBottom, fWidth, fHeight));
+    }
+
+    void pushToGLScissor(const GrGLInterface* gl) const {
+        GR_GL_CALL(gl, Scissor(fLeft, fBottom, fWidth, fHeight));
+    }
+
+    void setFromGLViewport(const GrGLInterface* gl) {
+        GR_STATIC_ASSERT(sizeof(GrGLIRect) == 4*sizeof(GrGLint));
+        GR_GL_GetIntegerv(gl, GR_GL_VIEWPORT, (GrGLint*) this);
+    }
+
+    // sometimes we have a GrIRect from the client that we
+    // want to simultaneously make relative to GL's viewport
+    // and convert from top-down to bottom-up.
+    void setRelativeTo(const GrGLIRect& glRect,
+                       int leftOffset,
+                       int topOffset,
+                       int width,
+                       int height) {
+        fLeft = glRect.fLeft + leftOffset;
+        fWidth = width;
+        fBottom = glRect.fBottom + (glRect.fHeight - topOffset - height);
+        fHeight = height;
+
+        GrAssert(fLeft >= 0);
+        GrAssert(fWidth >= 0);
+        GrAssert(fBottom >= 0);
+        GrAssert(fHeight >= 0);
+    }
+
+    bool contains(const GrGLIRect& glRect) const {
+        return fLeft <= glRect.fLeft &&
+               fBottom <= glRect.fBottom &&
+               fLeft + fWidth >=  glRect.fLeft + glRect.fWidth &&
+               fBottom + fHeight >=  glRect.fBottom + glRect.fHeight;
+    }
+
+    void invalidate() {fLeft = fWidth = fBottom = fHeight = -1;}
+
+    bool operator ==(const GrGLIRect& glRect) const {
+        return 0 == memcmp(this, &glRect, sizeof(GrGLIRect));
+    }
+
+    bool operator !=(const GrGLIRect& glRect) const {return !(*this == glRect);}
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLIndexBuffer.cpp b/src/gpu/gl/GrGLIndexBuffer.cpp
new file mode 100644
index 0000000..b64668e
--- /dev/null
+++ b/src/gpu/gl/GrGLIndexBuffer.cpp
@@ -0,0 +1,131 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#include "GrGLIndexBuffer.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+GrGLIndexBuffer::GrGLIndexBuffer(GrGpuGL* gpu,
+                                 GrGLuint id,
+                                 size_t sizeInBytes,
+                                 bool dynamic)
+    : INHERITED(gpu, sizeInBytes, dynamic)
+    , fBufferID(id)
+    , fLockPtr(NULL) {
+
+}
+
+void GrGLIndexBuffer::onRelease() {
+    // make sure we've not been abandoned
+    if (fBufferID) {
+        GPUGL->notifyIndexBufferDelete(this);
+        GL_CALL(DeleteBuffers(1, &fBufferID));
+        fBufferID = 0;
+    }
+}
+
+void GrGLIndexBuffer::onAbandon() {
+    fBufferID = 0;
+    fLockPtr = NULL;
+}
+
+void GrGLIndexBuffer::bind() const {
+    GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, fBufferID));
+    GPUGL->notifyIndexBufferBind(this);
+}
+
+GrGLuint GrGLIndexBuffer::bufferID() const {
+    return fBufferID;
+}
+
+void* GrGLIndexBuffer::lock() {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (this->getGpu()->getCaps().fBufferLockSupport) {
+        this->bind();
+        // Let driver know it can discard the old data
+        GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                           this->sizeInBytes(),
+                           NULL,
+                           this->dynamic() ? GR_GL_DYNAMIC_DRAW :
+                                             GR_GL_STATIC_DRAW));
+        GR_GL_CALL_RET(GPUGL->glInterface(),
+                       fLockPtr,
+                       MapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER,
+                                 GR_GL_WRITE_ONLY));
+
+        return fLockPtr;
+    }
+    return NULL;
+}
+
+void* GrGLIndexBuffer::lockPtr() const {
+    return fLockPtr;
+}
+
+void GrGLIndexBuffer::unlock() {
+    GrAssert(fBufferID);
+    GrAssert(isLocked());
+    GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
+
+    this->bind();
+    GL_CALL(UnmapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER));
+    fLockPtr = NULL;
+}
+
+bool GrGLIndexBuffer::isLocked() const {
+#if GR_DEBUG
+    if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
+        this->bind();
+        GrGLint mapped;
+        GL_CALL(GetBufferParameteriv(GR_GL_ELEMENT_ARRAY_BUFFER,
+                                     GR_GL_BUFFER_MAPPED, &mapped));
+        GrAssert(!!mapped == !!fLockPtr);
+    }
+#endif
+    return NULL != fLockPtr;
+}
+
+bool GrGLIndexBuffer::updateData(const void* src, size_t srcSizeInBytes) {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (srcSizeInBytes > this->sizeInBytes()) {
+        return false;
+    }
+    this->bind();
+    GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW;
+#if !GR_GL_USE_BUFFER_DATA_NULL_HINT
+    // Note that we're cheating on the size here. Currently no methods
+    // allow a partial update that preserves contents of non-updated
+    // portions of the buffer (and lock() does a glBufferData(..size, NULL..))
+    GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+#else
+    if (this->sizeInBytes() == srcSizeInBytes) {
+        GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                            srcSizeInBytes, src, usage));
+    } else {
+        // Before we call glBufferSubData we give the driver a hint using
+        // glBufferData with NULL. This makes the old buffer contents
+        // inaccessible to future draws. The GPU may still be processing draws
+        // that reference the old contents. With this hint it can assign a
+        // different allocation for the new contents to avoid flushing the gpu
+        // past draws consuming the old contents.
+        GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                           this->sizeInBytes(), NULL, usage));
+        GL_CALL(BufferSubData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                              0, srcSizeInBytes, src));
+    }
+#endif
+    return true;
+}
+
diff --git a/src/gpu/gl/GrGLIndexBuffer.h b/src/gpu/gl/GrGLIndexBuffer.h
new file mode 100644
index 0000000..9f32890
--- /dev/null
+++ b/src/gpu/gl/GrGLIndexBuffer.h
@@ -0,0 +1,55 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGLIndexBuffer_DEFINED
+#define GrGLIndexBuffer_DEFINED
+
+#include "../GrIndexBuffer.h"
+#include "GrGLInterface.h"
+
+class GrGpuGL;
+
+class GrGLIndexBuffer : public GrIndexBuffer {
+
+public:
+
+    virtual ~GrGLIndexBuffer() { this->release(); }
+
+    GrGLuint bufferID() const;
+
+    // overrides of GrIndexBuffer
+    virtual void* lock();
+    virtual void* lockPtr() const;
+    virtual void unlock();
+    virtual bool isLocked() const;
+    virtual bool updateData(const void* src, size_t srcSizeInBytes);
+
+protected:
+    GrGLIndexBuffer(GrGpuGL* gpu,
+                    GrGLuint id,
+                    size_t sizeInBytes,
+                    bool dynamic);
+
+    // overrides of GrResource
+    virtual void onAbandon();
+    virtual void onRelease();
+
+private:
+    void bind() const;
+
+    GrGLuint     fBufferID;
+    void*        fLockPtr;
+
+    friend class GrGpuGL;
+
+    typedef GrIndexBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
new file mode 100644
index 0000000..c1ac088
--- /dev/null
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -0,0 +1,527 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrTypes.h"
+#include "GrGLInterface.h"
+#include "GrGLDefines.h"
+
+#include <stdio.h>
+
+#if GR_GL_PER_GL_FUNC_CALLBACK
+namespace {
+void GrGLDefaultInterfaceCallback(const GrGLInterface*) {}
+}
+#endif
+
+GrGLBinding GrGLGetBindingInUseFromString(const char* versionString) {
+    if (NULL == versionString) {
+        GrAssert(!"NULL GL version string.");
+        return kNone_GrGLBinding;
+    }
+
+    int major, minor;
+
+    // check for desktop
+    int n = sscanf(versionString, "%d.%d", &major, &minor);
+    if (2 == n) {
+        return kDesktop_GrGLBinding;
+    }
+
+    // check for ES 1
+    char profile[2];
+    n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
+               &major, &minor);
+    if (4 == n) {
+        // we no longer support ES1.
+        return kNone_GrGLBinding;
+    }
+
+    // check for ES2
+    n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
+    if (2 == n) {
+        return kES2_GrGLBinding;
+    }
+    return kNone_GrGLBinding;
+}
+
+GrGLVersion GrGLGetVersionFromString(const char* versionString) {
+    if (NULL == versionString) {
+        GrAssert(!"NULL GL version string.");
+        return 0;
+    }
+
+    int major, minor;
+
+    int n = sscanf(versionString, "%d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GL_VER(major, minor);
+    }
+
+    char profile[2];
+    n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
+               &major, &minor);
+    if (4 == n) {
+        return GR_GL_VER(major, minor);
+    }
+    
+    n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GL_VER(major, minor);
+    }
+
+    return 0;
+}
+
+GrGLSLVersion GrGLGetGLSLVersionFromString(const char* versionString) {
+    if (NULL == versionString) {
+        GrAssert(!"NULL GLSL version string.");
+        return 0;
+    }
+
+    int major, minor;
+
+    int n = sscanf(versionString, "%d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GLSL_VER(major, minor);
+    }
+    
+    n = sscanf(versionString, "OpenGL ES GLSL ES %d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GLSL_VER(major, minor);
+    }
+    return 0;
+}
+
+bool GrGLHasExtensionFromString(const char* ext, const char* extensionString) {
+    int extLength = strlen(ext);
+
+    while (true) {
+        int n = strcspn(extensionString, " ");
+        if (n == extLength && 0 == strncmp(ext, extensionString, n)) {
+            return true;
+        }
+        if (0 == extensionString[n]) {
+            return false;
+        }
+        extensionString += n+1;
+    }
+
+    return false;
+}
+
+bool GrGLHasExtension(const GrGLInterface* gl, const char* ext) {
+    const GrGLubyte* glstr;
+    GR_GL_CALL_RET(gl, glstr, GetString(GR_GL_EXTENSIONS));
+    return GrGLHasExtensionFromString(ext, (const char*) glstr);
+}
+
+GrGLBinding GrGLGetBindingInUse(const GrGLInterface* gl) {
+    const GrGLubyte* v;
+    GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
+    return GrGLGetBindingInUseFromString((const char*) v);
+}
+
+GrGLVersion GrGLGetVersion(const GrGLInterface* gl) {
+    const GrGLubyte* v;
+    GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
+    return GrGLGetVersionFromString((const char*) v);
+}
+
+GrGLSLVersion GrGLGetGLSLVersion(const GrGLInterface* gl) {
+    const GrGLubyte* v;
+    GR_GL_CALL_RET(gl, v, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
+    return GrGLGetGLSLVersionFromString((const char*) v);
+}
+
+GrGLInterface::GrGLInterface() {
+    fBindingsExported = kNone_GrGLBinding;
+
+    fActiveTexture = NULL;
+    fAttachShader = NULL;
+    fBeginQuery = NULL;
+    fBindAttribLocation = NULL;
+    fBindBuffer = NULL;
+    fBindFragDataLocation = NULL;
+    fBindTexture = NULL;
+    fBlendColor = NULL;
+    fBlendFunc = NULL;
+    fBufferData = NULL;
+    fBufferSubData = NULL;
+    fClear = NULL;
+    fClearColor = NULL;
+    fClearStencil = NULL;
+    fColorMask = NULL;
+    fColorPointer = NULL;
+    fCompileShader = NULL;
+    fCompressedTexImage2D = NULL;
+    fCreateProgram = NULL;
+    fCreateShader = NULL;
+    fCullFace = NULL;
+    fDeleteBuffers = NULL;
+    fDeleteProgram = NULL;
+    fDeleteQueries = NULL;
+    fDeleteShader = NULL;
+    fDeleteTextures = NULL;
+    fDepthMask = NULL;
+    fDisable = NULL;
+    fDisableVertexAttribArray = NULL;
+    fDrawArrays = NULL;
+    fDrawBuffer = NULL;
+    fDrawBuffers = NULL;
+    fDrawElements = NULL;
+    fEndQuery = NULL;
+    fFinish = NULL;
+    fFlush = NULL;
+    fEnable = NULL;
+    fEnableVertexAttribArray = NULL;
+    fFrontFace = NULL;
+    fGenBuffers = NULL;
+    fGenQueries = NULL;
+    fGenTextures = NULL;
+    fGetBufferParameteriv = NULL;
+    fGetError = NULL;
+    fGetIntegerv = NULL;
+    fGetQueryiv = NULL;
+    fGetQueryObjecti64v = NULL;
+    fGetQueryObjectiv = NULL;
+    fGetQueryObjectui64v = NULL;
+    fGetQueryObjectuiv = NULL;
+    fGetProgramInfoLog = NULL;
+    fGetProgramiv = NULL;
+    fGetShaderInfoLog = NULL;
+    fGetShaderiv = NULL;
+    fGetString = NULL;
+    fGetTexLevelParameteriv = NULL;
+    fGetUniformLocation = NULL;
+    fLineWidth = NULL;
+    fLinkProgram = NULL;
+    fPixelStorei = NULL;
+    fQueryCounter = NULL;
+    fReadBuffer = NULL;
+    fReadPixels = NULL;
+    fScissor = NULL;
+    fShaderSource = NULL;
+    fStencilFunc = NULL;
+    fStencilFuncSeparate = NULL;
+    fStencilMask = NULL;
+    fStencilMaskSeparate = NULL;
+    fStencilOp = NULL;
+    fStencilOpSeparate = NULL;
+    fTexImage2D = NULL;
+    fTexParameteri = NULL;
+    fTexStorage2D = NULL;
+    fTexSubImage2D = NULL;
+    fUniform1f = NULL;
+    fUniform1i = NULL;
+    fUniform1fv = NULL;
+    fUniform1iv = NULL;
+    fUniform2f = NULL;
+    fUniform2i = NULL;
+    fUniform2fv = NULL;
+    fUniform2iv = NULL;
+    fUniform3f = NULL;
+    fUniform3i = NULL;
+    fUniform3fv = NULL;
+    fUniform3iv = NULL;
+    fUniform4f = NULL;
+    fUniform4i = NULL;
+    fUniform4fv = NULL;
+    fUniform4iv = NULL;
+    fUniformMatrix2fv = NULL;
+    fUniformMatrix3fv = NULL;
+    fUniformMatrix4fv = NULL;
+    fUseProgram = NULL;
+    fVertexAttrib4fv = NULL;
+    fVertexAttribPointer = NULL;
+    fViewport = NULL;
+    fBindFramebuffer = NULL;
+    fBindRenderbuffer = NULL;
+    fCheckFramebufferStatus = NULL;
+    fDeleteFramebuffers = NULL;
+    fDeleteRenderbuffers = NULL;
+    fFramebufferRenderbuffer = NULL;
+    fFramebufferTexture2D = NULL;
+    fGenFramebuffers = NULL;
+    fGenRenderbuffers = NULL;
+    fGetFramebufferAttachmentParameteriv = NULL;
+    fGetRenderbufferParameteriv = NULL;
+    fRenderbufferStorage = NULL;
+    fRenderbufferStorageMultisample = NULL;
+    fBlitFramebuffer = NULL;
+    fResolveMultisampleFramebuffer = NULL;
+    fMapBuffer = NULL;
+    fUnmapBuffer = NULL;
+    fBindFragDataLocationIndexed = NULL;
+
+#if GR_GL_PER_GL_FUNC_CALLBACK
+    fCallback = GrGLDefaultInterfaceCallback;
+    fCallbackData = 0;
+#endif
+}
+
+bool GrGLInterface::validate(GrGLBinding binding) const {
+
+    // kNone must be 0 so that the check we're about to do can never succeed if
+    // binding == kNone.
+    GR_STATIC_ASSERT(kNone_GrGLBinding == 0);
+
+    if (0 == (binding & fBindingsExported)) {
+        return false;
+    }
+
+    // functions that are always required
+    if (NULL == fActiveTexture ||
+        NULL == fAttachShader ||
+        NULL == fBindAttribLocation ||
+        NULL == fBindBuffer ||
+        NULL == fBindTexture ||
+        NULL == fBlendFunc ||
+        NULL == fBufferData ||
+        NULL == fBufferSubData ||
+        NULL == fClear ||
+        NULL == fClearColor ||
+        NULL == fClearStencil ||
+        NULL == fColorMask ||
+        NULL == fCompileShader ||
+        NULL == fCreateProgram ||
+        NULL == fCreateShader ||
+        NULL == fCullFace ||
+        NULL == fDeleteBuffers ||
+        NULL == fDeleteProgram ||
+        NULL == fDeleteShader ||
+        NULL == fDeleteTextures ||
+        NULL == fDepthMask ||
+        NULL == fDisable ||
+        NULL == fDisableVertexAttribArray ||
+        NULL == fDrawArrays ||
+        NULL == fDrawElements ||
+        NULL == fEnable ||
+        NULL == fEnableVertexAttribArray ||
+        NULL == fFrontFace ||
+        NULL == fGenBuffers ||
+        NULL == fGenTextures ||
+        NULL == fGetBufferParameteriv ||
+        NULL == fGetError ||
+        NULL == fGetIntegerv ||
+        NULL == fGetProgramInfoLog ||
+        NULL == fGetProgramiv ||
+        NULL == fGetShaderInfoLog ||
+        NULL == fGetShaderiv ||
+        NULL == fGetString ||
+        NULL == fGetUniformLocation ||
+        NULL == fLinkProgram ||
+        NULL == fPixelStorei ||
+        NULL == fReadPixels ||
+        NULL == fScissor ||
+        NULL == fShaderSource ||
+        NULL == fStencilFunc ||
+        NULL == fStencilMask ||
+        NULL == fStencilOp ||
+        NULL == fTexImage2D ||
+        NULL == fTexParameteri ||
+        NULL == fTexSubImage2D ||
+        NULL == fUniform1f ||
+        NULL == fUniform1i ||
+        NULL == fUniform1fv ||
+        NULL == fUniform1iv ||
+        NULL == fUniform2f ||
+        NULL == fUniform2i ||
+        NULL == fUniform2fv ||
+        NULL == fUniform2iv ||
+        NULL == fUniform3f ||
+        NULL == fUniform3i ||
+        NULL == fUniform3fv ||
+        NULL == fUniform3iv ||
+        NULL == fUniform4f ||
+        NULL == fUniform4i ||
+        NULL == fUniform4fv ||
+        NULL == fUniform4iv ||
+        NULL == fUniformMatrix2fv ||
+        NULL == fUniformMatrix3fv ||
+        NULL == fUniformMatrix4fv ||
+        NULL == fUseProgram ||
+        NULL == fVertexAttrib4fv ||
+        NULL == fVertexAttribPointer ||
+        NULL == fViewport ||
+        NULL == fBindFramebuffer ||
+        NULL == fBindRenderbuffer ||
+        NULL == fCheckFramebufferStatus ||
+        NULL == fDeleteFramebuffers ||
+        NULL == fDeleteRenderbuffers ||
+        NULL == fFinish ||
+        NULL == fFlush ||
+        NULL == fFramebufferRenderbuffer ||
+        NULL == fFramebufferTexture2D ||
+        NULL == fGetFramebufferAttachmentParameteriv ||
+        NULL == fGetRenderbufferParameteriv ||
+        NULL == fGenFramebuffers ||
+        NULL == fGenRenderbuffers ||
+        NULL == fRenderbufferStorage) {
+        return false;
+    }
+
+    const char* ext;
+    GrGLVersion glVer = GrGLGetVersion(this);
+    ext = (const char*)fGetString(GR_GL_EXTENSIONS);
+
+    // Now check that baseline ES/Desktop fns not covered above are present
+    // and that we have fn pointers for any advertised extensions that we will
+    // try to use.
+
+    // these functions are part of ES2, we assume they are available
+    // On the desktop we assume they are available if the extension
+    // is present or GL version is high enough.
+    if (kES2_GrGLBinding == binding) {
+        if (NULL == fBlendColor ||
+            NULL == fStencilFuncSeparate ||
+            NULL == fStencilMaskSeparate ||
+            NULL == fStencilOpSeparate) {
+            return false;
+        }
+    } else if (kDesktop_GrGLBinding == binding) {
+        if (glVer >= GR_GL_VER(2,0)) {
+            if (NULL == fStencilFuncSeparate ||
+                NULL == fStencilMaskSeparate ||
+                NULL == fStencilOpSeparate) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(3,0) && NULL == fBindFragDataLocation) {
+            return false;
+        }
+        if (glVer >= GR_GL_VER(2,0) ||
+            GrGLHasExtensionFromString("GL_ARB_draw_buffers", ext)) {
+            if (NULL == fDrawBuffers) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(1,4) ||
+            GrGLHasExtensionFromString("GL_EXT_blend_color", ext)) {
+            if (NULL == fBlendColor) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(1,5) ||
+            GrGLHasExtensionFromString("GL_ARB_occlusion_query", ext)) {
+            if (NULL == fGenQueries ||
+                NULL == fDeleteQueries ||
+                NULL == fBeginQuery ||
+                NULL == fEndQuery ||
+                NULL == fGetQueryiv ||
+                NULL == fGetQueryObjectiv ||
+                NULL == fGetQueryObjectuiv) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(3,3) ||
+            GrGLHasExtensionFromString("GL_ARB_timer_query", ext) ||
+            GrGLHasExtensionFromString("GL_EXT_timer_query", ext)) {
+            if (NULL == fGetQueryObjecti64v ||
+                NULL == fGetQueryObjectui64v) {
+                return false;
+            }
+        }
+        if (glVer >= GR_GL_VER(3,3) ||
+            GrGLHasExtensionFromString("GL_ARB_timer_query", ext)) {
+            if (NULL == fQueryCounter) {
+                return false;
+            }
+        }
+    }
+
+    // optional function on desktop before 1.3
+    if (kDesktop_GrGLBinding != binding ||
+        (glVer >= GR_GL_VER(1,3) ||
+        GrGLHasExtensionFromString("GL_ARB_texture_compression", ext))) {
+        if (NULL == fCompressedTexImage2D) {
+            return false;
+        }
+    }
+
+    // part of desktop GL, but not ES
+    if (kDesktop_GrGLBinding == binding &&
+        (NULL == fLineWidth ||
+         NULL == fGetTexLevelParameteriv ||
+         NULL == fDrawBuffer ||
+         NULL == fReadBuffer)) {
+        return false;
+    }
+
+    // GL_EXT_texture_storage is part of desktop 4.2
+    // There is a desktop ARB extension and an ES+desktop EXT extension
+    if (kDesktop_GrGLBinding == binding) {
+        if (glVer >= GR_GL_VER(4,2) ||
+            GrGLHasExtensionFromString("GL_ARB_texture_storage", ext) ||
+            GrGLHasExtensionFromString("GL_EXT_texture_storage", ext)) {
+            if (NULL == fTexStorage2D) {
+                return false;
+            }
+        }
+    } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", ext)) {
+        if (NULL == fTexStorage2D) {
+            return false;
+        }
+    }
+
+    // FBO MSAA
+    if (kDesktop_GrGLBinding == binding) {
+        // GL 3.0 and the ARB extension have multisample + blit
+        if (glVer >= GR_GL_VER(3,0) || GrGLHasExtensionFromString("GL_ARB_framebuffer_object", ext)) {
+            if (NULL == fRenderbufferStorageMultisample ||
+                NULL == fBlitFramebuffer) {
+                return false;
+            }
+        } else {
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit", ext) &&
+                NULL == fBlitFramebuffer) {
+                return false;
+            }
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample", ext) &&
+                NULL == fRenderbufferStorageMultisample) {
+                return false;
+            }
+        }
+    } else {
+        if (GrGLHasExtensionFromString("GL_CHROMIUM_framebuffer_multisample", ext)) {
+            if (NULL == fRenderbufferStorageMultisample ||
+                NULL == fBlitFramebuffer) {
+                return false;
+            }
+        }
+        if (GrGLHasExtensionFromString("GL_APPLE_framebuffer_multisample", ext)) {
+            if (NULL == fRenderbufferStorageMultisample ||
+                NULL == fResolveMultisampleFramebuffer) {
+                return false;
+            }
+        }
+    }
+
+    // On ES buffer mapping is an extension. On Desktop
+    // buffer mapping was part of original VBO extension
+    // which we require.
+    if (kDesktop_GrGLBinding == binding || 
+        GrGLHasExtensionFromString("GL_OES_mapbuffer", ext)) {
+        if (NULL == fMapBuffer ||
+            NULL == fUnmapBuffer) {
+            return false;
+        }
+    }
+
+    // Dual source blending
+    if (kDesktop_GrGLBinding == binding &&
+        (glVer >= GR_GL_VER(3,3) || 
+         GrGLHasExtensionFromString("GL_ARB_blend_func_extended", ext))) {
+        if (NULL == fBindFragDataLocationIndexed) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
new file mode 100644
index 0000000..fb9debf
--- /dev/null
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -0,0 +1,1889 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLProgram.h"
+
+#include "../GrAllocator.h"
+#include "GrGLShaderVar.h"
+#include "SkTrace.h"
+#include "SkXfermode.h"
+
+namespace {
+
+enum {
+    /// Used to mark a StageUniLocation field that should be bound
+    /// to a uniform during getUniformLocationsAndInitCache().
+    kUseUniform = 2000
+};
+
+}  // namespace
+
+#define PRINT_SHADERS 0
+
+typedef GrTAllocator<GrGLShaderVar> VarArray;
+
+// number of each input/output type in a single allocation block
+static const int gVarsPerBlock = 8;
+// except FS outputs where we expect 2 at most.
+static const int gMaxFSOutputs = 2;
+
+struct ShaderCodeSegments {
+    ShaderCodeSegments() 
+    : fVSUnis(gVarsPerBlock)
+    , fVSAttrs(gVarsPerBlock)
+    , fVSOutputs(gVarsPerBlock)
+    , fGSInputs(gVarsPerBlock)
+    , fGSOutputs(gVarsPerBlock)
+    , fFSInputs(gVarsPerBlock)
+    , fFSUnis(gVarsPerBlock)
+    , fFSOutputs(gMaxFSOutputs)
+    , fUsesGS(false) {}
+    GrStringBuilder fHeader; // VS+FS, GLSL version, etc
+    VarArray        fVSUnis;
+    VarArray        fVSAttrs;
+    VarArray        fVSOutputs;
+    VarArray        fGSInputs;
+    VarArray        fGSOutputs;
+    VarArray        fFSInputs;
+    GrStringBuilder fGSHeader; // layout qualifiers specific to GS
+    VarArray        fFSUnis;
+    VarArray        fFSOutputs;
+    GrStringBuilder fFSFunctions;
+    GrStringBuilder fVSCode;
+    GrStringBuilder fGSCode;
+    GrStringBuilder fFSCode;
+
+    bool            fUsesGS;
+};
+
+typedef GrGLProgram::ProgramDesc::StageDesc StageDesc;
+
+#if GR_GL_ATTRIBUTE_MATRICES
+    #define VIEW_MATRIX_NAME "aViewM"
+#else
+    #define VIEW_MATRIX_NAME "uViewM"
+#endif
+
+#define POS_ATTR_NAME "aPosition"
+#define COL_ATTR_NAME "aColor"
+#define COV_ATTR_NAME "aCoverage"
+#define EDGE_ATTR_NAME "aEdge"
+#define COL_UNI_NAME "uColor"
+#define COV_UNI_NAME "uCoverage"
+#define EDGES_UNI_NAME "uEdges"
+#define COL_FILTER_UNI_NAME "uColorFilter"
+#define COL_MATRIX_UNI_NAME "uColorMatrix"
+#define COL_MATRIX_VEC_UNI_NAME "uColorMatrixVec"
+
+namespace {
+inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
+    *s = "aTexCoord";
+    s->appendS32(coordIdx);
+}
+
+inline GrGLShaderVar::Type float_vector_type(int count) {
+    GR_STATIC_ASSERT(GrGLShaderVar::kFloat_Type == 0);
+    GR_STATIC_ASSERT(GrGLShaderVar::kVec2f_Type == 1);
+    GR_STATIC_ASSERT(GrGLShaderVar::kVec3f_Type == 2);
+    GR_STATIC_ASSERT(GrGLShaderVar::kVec4f_Type == 3);
+    GrAssert(count > 0 && count <= 4);
+    return (GrGLShaderVar::Type)(count - 1);
+}
+
+inline const char* float_vector_type_str(int count) {
+    return GrGLShaderVar::TypeString(float_vector_type(count));
+}
+
+inline const char* vector_homog_coord(int count) {
+    static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
+    return HOMOGS[count];
+}
+
+inline const char* vector_nonhomog_coords(int count) {
+    static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS));
+    return NONHOMOGS[count];
+}
+
+inline const char* vector_all_coords(int count) {
+    static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
+    return ALL[count];
+}
+
+inline const char* all_ones_vec(int count) {
+    static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
+                                    "vec3(1,1,1)", "vec4(1,1,1,1)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
+    return ONESVEC[count];
+}
+
+inline const char* all_zeros_vec(int count) {
+    static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
+                                    "vec3(0,0,0)", "vec4(0,0,0,0)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
+    return ZEROSVEC[count];
+}
+
+inline const char* declared_color_output_name() { return "fsColorOut"; }
+inline const char* dual_source_output_name() { return "dualSourceOut"; }
+
+inline void tex_matrix_name(int stage, GrStringBuilder* s) {
+#if GR_GL_ATTRIBUTE_MATRICES
+    *s = "aTexM";
+#else
+    *s = "uTexM";
+#endif
+    s->appendS32(stage);
+}
+
+inline void normalized_texel_size_name(int stage, GrStringBuilder* s) {
+    *s = "uTexelSize";
+    s->appendS32(stage);
+}
+
+inline void sampler_name(int stage, GrStringBuilder* s) {
+    *s = "uSampler";
+    s->appendS32(stage);
+}
+
+inline void radial2_param_name(int stage, GrStringBuilder* s) {
+    *s = "uRadial2Params";
+    s->appendS32(stage);
+}
+
+inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) {
+    *k = "uKernel";
+    k->appendS32(stage);
+    *i = "uImageIncrement";
+    i->appendS32(stage);
+}
+
+inline void tex_domain_name(int stage, GrStringBuilder* s) {
+    *s = "uTexDom";
+    s->appendS32(stage);
+}
+}
+
+GrGLProgram::GrGLProgram() {
+}
+
+GrGLProgram::~GrGLProgram() {
+}
+
+void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
+                                GrBlendCoeff* dstCoeff) const {
+    switch (fProgramDesc.fDualSrcOutput) {
+        case ProgramDesc::kNone_DualSrcOutput:
+            break;
+        // the prog will write a coverage value to the secondary
+        // output and the dst is blended by one minus that value.
+        case ProgramDesc::kCoverage_DualSrcOutput:
+        case ProgramDesc::kCoverageISA_DualSrcOutput:
+        case ProgramDesc::kCoverageISC_DualSrcOutput:
+        *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
+        break;
+        default:
+            GrCrash("Unexpected dual source blend output");
+            break;
+    }
+}
+
+// assigns modulation of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all ones
+static inline void modulate_helper(const char* outputVar,
+                                   const char* var0,
+                                   const char* var1,
+                                   GrStringBuilder* code) {
+    GrAssert(NULL != outputVar);
+    GrAssert(NULL != var0);
+    GrAssert(NULL != var1);
+    GrAssert(NULL != code);
+
+    bool has0 = '\0' != *var0;
+    bool has1 = '\0' != *var1;
+
+    if (!has0 && !has1) {
+        code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
+    } else if (!has0) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+    } else if (!has1) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+    } else {
+        code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
+    }
+}
+
+// assigns addition of two vars to an output var
+// vars can be vec4s or floats (or one of each)
+// result is always vec4
+// if either var is "" then assign to the other var
+// if both are "" then assign all zeros
+static inline void add_helper(const char* outputVar,
+                              const char* var0,
+                              const char* var1,
+                              GrStringBuilder* code) {
+    GrAssert(NULL != outputVar);
+    GrAssert(NULL != var0);
+    GrAssert(NULL != var1);
+    GrAssert(NULL != code);
+
+    bool has0 = '\0' != *var0;
+    bool has1 = '\0' != *var1;
+
+    if (!has0 && !has1) {
+        code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
+    } else if (!has0) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
+    } else if (!has1) {
+        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
+    } else {
+        code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
+    }
+}
+
+// given two blend coeffecients determine whether the src
+// and/or dst computation can be omitted.
+static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
+                                   SkXfermode::Coeff dstCoeff,
+                                   bool* needSrcValue,
+                                   bool* needDstValue) {
+    if (SkXfermode::kZero_Coeff == srcCoeff) {
+        switch (dstCoeff) {
+            // these all read the src
+            case SkXfermode::kSC_Coeff:
+            case SkXfermode::kISC_Coeff:
+            case SkXfermode::kSA_Coeff:
+            case SkXfermode::kISA_Coeff:
+                *needSrcValue = true;
+                break;
+            default:
+                *needSrcValue = false;
+                break;
+        }
+    } else {
+        *needSrcValue = true;
+    }
+    if (SkXfermode::kZero_Coeff == dstCoeff) {
+        switch (srcCoeff) {
+            // these all read the dst
+            case SkXfermode::kDC_Coeff:
+            case SkXfermode::kIDC_Coeff:
+            case SkXfermode::kDA_Coeff:
+            case SkXfermode::kIDA_Coeff:
+                *needDstValue = true;
+                break;
+            default:
+                *needDstValue = false;
+                break;
+        }
+    } else {
+        *needDstValue = true;
+    }
+}
+
+/**
+ * Create a blend_coeff * value string to be used in shader code. Sets empty
+ * string if result is trivially zero.
+ */
+static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff,
+                             const char* src, const char* dst,
+                             const char* value) {
+    switch (coeff) {
+    case SkXfermode::kZero_Coeff:    /** 0 */
+        *str = "";
+        break;
+    case SkXfermode::kOne_Coeff:     /** 1 */
+        *str = value;
+        break;
+    case SkXfermode::kSC_Coeff:
+        str->printf("(%s * %s)", src, value);
+        break;
+    case SkXfermode::kISC_Coeff:
+        str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value);
+        break;
+    case SkXfermode::kDC_Coeff:
+        str->printf("(%s * %s)", dst, value);
+        break;
+    case SkXfermode::kIDC_Coeff:
+        str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value);
+        break;
+    case SkXfermode::kSA_Coeff:      /** src alpha */
+        str->printf("(%s.a * %s)", src, value);
+        break;
+    case SkXfermode::kISA_Coeff:     /** inverse src alpha (i.e. 1 - sa) */
+        str->printf("((1.0 - %s.a) * %s)", src, value);
+        break;
+    case SkXfermode::kDA_Coeff:      /** dst alpha */
+        str->printf("(%s.a * %s)", dst, value);
+        break;
+    case SkXfermode::kIDA_Coeff:     /** inverse dst alpha (i.e. 1 - da) */
+        str->printf("((1.0 - %s.a) * %s)", dst, value);
+        break;
+    default:
+        GrCrash("Unexpected xfer coeff.");
+        break;
+    }
+}
+/**
+ * Adds a line to the fragment shader code which modifies the color by
+ * the specified color filter.
+ */
+static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
+                           SkXfermode::Coeff uniformCoeff,
+                           SkXfermode::Coeff colorCoeff,
+                           const char* inColor) {
+    GrStringBuilder colorStr, constStr;
+    blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME,
+                    inColor, inColor);
+    blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME,
+                    inColor, COL_FILTER_UNI_NAME);
+
+    add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
+}
+/**
+ * Adds code to the fragment shader code which modifies the color by
+ * the specified color matrix.
+ */
+static void addColorMatrix(GrStringBuilder* fsCode, const char * outputVar,
+                           const char* inColor) {
+    fsCode->appendf("\t%s = %s * vec4(%s.rgb / %s.a, %s.a) + %s;\n", outputVar, COL_MATRIX_UNI_NAME, inColor, inColor, inColor, COL_MATRIX_VEC_UNI_NAME);
+    fsCode->appendf("\t%s.rgb *= %s.a;\n", outputVar, outputVar);
+}
+
+namespace {
+
+// Adds a var that is computed in the VS and read in FS.
+// If there is a GS it will just pass it through.
+void append_varying(GrGLShaderVar::Type type,
+                    const char* name,
+                    ShaderCodeSegments* segments,
+                    const char** vsOutName = NULL,
+                    const char** fsInName = NULL) {
+    segments->fVSOutputs.push_back();
+    segments->fVSOutputs.back().setType(type);
+    segments->fVSOutputs.back().setTypeModifier(
+        GrGLShaderVar::kOut_TypeModifier);
+    segments->fVSOutputs.back().accessName()->printf("v%s", name);
+    if (vsOutName) {
+        *vsOutName = segments->fVSOutputs.back().getName().c_str();
+    }
+    // input to FS comes either from VS or GS
+    const GrStringBuilder* fsName;
+    if (segments->fUsesGS) {
+        // if we have a GS take each varying in as an array
+        // and output as non-array.
+        segments->fGSInputs.push_back();
+        segments->fGSInputs.back().setType(type);
+        segments->fGSInputs.back().setTypeModifier(
+            GrGLShaderVar::kIn_TypeModifier);
+        segments->fGSInputs.back().setUnsizedArray();
+        *segments->fGSInputs.back().accessName() =
+            segments->fVSOutputs.back().getName();
+        segments->fGSOutputs.push_back();
+        segments->fGSOutputs.back().setType(type);
+        segments->fGSOutputs.back().setTypeModifier(
+            GrGLShaderVar::kOut_TypeModifier);
+        segments->fGSOutputs.back().accessName()->printf("g%s", name);
+        fsName = segments->fGSOutputs.back().accessName();
+    } else {
+        fsName = segments->fVSOutputs.back().accessName();
+    }
+    segments->fFSInputs.push_back();
+    segments->fFSInputs.back().setType(type);
+    segments->fFSInputs.back().setTypeModifier(
+        GrGLShaderVar::kIn_TypeModifier);
+    segments->fFSInputs.back().setName(*fsName);
+    if (fsInName) {
+        *fsInName = fsName->c_str();
+    }
+}
+
+// version of above that adds a stage number to the
+// the var name (for uniqueness)
+void append_varying(GrGLShaderVar::Type type,
+                    const char* name,
+                    int stageNum,
+                    ShaderCodeSegments* segments,
+                    const char** vsOutName = NULL,
+                    const char** fsInName = NULL) {
+    GrStringBuilder nameWithStage(name);
+    nameWithStage.appendS32(stageNum);
+    append_varying(type, nameWithStage.c_str(), segments, vsOutName, fsInName);
+}
+}
+
+void GrGLProgram::genEdgeCoverage(const GrGLInterface* gl,
+                                  GrVertexLayout layout,
+                                  CachedData* programData,
+                                  GrStringBuilder* coverageVar,
+                                  ShaderCodeSegments* segments) const {
+    if (fProgramDesc.fEdgeAANumEdges > 0) {
+        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec3f_Type,
+                                          GrGLShaderVar::kUniform_TypeModifier,
+                                          EDGES_UNI_NAME,
+                                          fProgramDesc.fEdgeAANumEdges);
+        programData->fUniLocations.fEdgesUni = kUseUniform;
+        int count = fProgramDesc.fEdgeAANumEdges;
+        segments->fFSCode.append(
+            "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n");
+        for (int i = 0; i < count; i++) {
+            segments->fFSCode.append("\tfloat a");
+            segments->fFSCode.appendS32(i);
+            segments->fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "[");
+            segments->fFSCode.appendS32(i);
+            segments->fFSCode.append("], pos), 0.0, 1.0);\n");
+        }
+        if (fProgramDesc.fEdgeAAConcave && (count & 0x01) == 0) {
+            // For concave polys, we consider the edges in pairs.
+            segments->fFSFunctions.append("float cross2(vec2 a, vec2 b) {\n");
+            segments->fFSFunctions.append("\treturn dot(a, vec2(b.y, -b.x));\n");
+            segments->fFSFunctions.append("}\n");
+            for (int i = 0; i < count; i += 2) {
+                segments->fFSCode.appendf("\tfloat eb%d;\n", i / 2);
+                segments->fFSCode.appendf("\tif (cross2(" EDGES_UNI_NAME "[%d].xy, " EDGES_UNI_NAME "[%d].xy) < 0.0) {\n", i, i + 1);
+                segments->fFSCode.appendf("\t\teb%d = a%d * a%d;\n", i / 2, i, i + 1);
+                segments->fFSCode.append("\t} else {\n");
+                segments->fFSCode.appendf("\t\teb%d = a%d + a%d - a%d * a%d;\n", i / 2, i, i + 1, i, i + 1);
+                segments->fFSCode.append("\t}\n");
+            }
+            segments->fFSCode.append("\tfloat edgeAlpha = ");
+            for (int i = 0; i < count / 2 - 1; i++) {
+                segments->fFSCode.appendf("min(eb%d, ", i);
+            }
+            segments->fFSCode.appendf("eb%d", count / 2 - 1);
+            for (int i = 0; i < count / 2 - 1; i++) {
+                segments->fFSCode.append(")");
+            }
+            segments->fFSCode.append(";\n");
+        } else {
+            segments->fFSCode.append("\tfloat edgeAlpha = ");
+            for (int i = 0; i < count - 1; i++) {
+                segments->fFSCode.appendf("min(a%d * a%d, ", i, i + 1);
+            }
+            segments->fFSCode.appendf("a%d * a0", count - 1);
+            for (int i = 0; i < count - 1; i++) {
+                segments->fFSCode.append(")");
+            }
+            segments->fFSCode.append(";\n");
+        }
+        *coverageVar = "edgeAlpha";
+    } else  if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
+        const char *vsName, *fsName;
+        append_varying(GrGLShaderVar::kVec4f_Type, "Edge", segments,
+            &vsName, &fsName);
+        segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+            GrGLShaderVar::kAttribute_TypeModifier, EDGE_ATTR_NAME);
+        segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
+        if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
+            segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
+            segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+        } else if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
+            segments->fFSCode.appendf("\tfloat edgeAlpha;\n");
+            // keep the derivative instructions outside the conditional 
+            segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+            segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+            segments->fFSCode.appendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
+            // today we know z and w are in device space. We could use derivatives
+            segments->fFSCode.appendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, fsName);
+            segments->fFSCode.append ("\t} else {\n");
+            segments->fFSCode.appendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+                                      "\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
+                                      fsName, fsName);
+            segments->fFSCode.appendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
+            segments->fFSCode.appendf("\t\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n"
+                                      "\t}\n");
+            if (gl->supportsES2()) {
+                segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+            }
+        } else {
+            GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType);
+            segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+            segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+            segments->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+                                      "\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
+                                      fsName, fsName);
+            segments->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
+            segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
+            segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+            if (gl->supportsES2()) {
+                segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+            }
+        }
+        *coverageVar = "edgeAlpha";
+    } else {
+        coverageVar->reset();
+    }
+}
+
+namespace {
+
+void genInputColor(GrGLProgram::ProgramDesc::ColorInput colorInput,
+                   GrGLProgram::CachedData* programData,
+                   ShaderCodeSegments* segments,
+                   GrStringBuilder* inColor) {
+    switch (colorInput) {
+        case GrGLProgram::ProgramDesc::kAttribute_ColorInput: {
+            segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+                GrGLShaderVar::kAttribute_TypeModifier,
+                COL_ATTR_NAME);
+            const char *vsName, *fsName;
+            append_varying(GrGLShaderVar::kVec4f_Type, "Color", segments, &vsName, &fsName);
+            segments->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
+            *inColor = fsName;
+            } break;
+        case GrGLProgram::ProgramDesc::kUniform_ColorInput:
+            segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+                GrGLShaderVar::kUniform_TypeModifier,
+                COL_UNI_NAME);
+            programData->fUniLocations.fColorUni = kUseUniform;
+            *inColor = COL_UNI_NAME;
+            break;
+        case GrGLProgram::ProgramDesc::kTransBlack_ColorInput:
+            GrAssert(!"needComputedColor should be false.");
+            break;
+        case GrGLProgram::ProgramDesc::kSolidWhite_ColorInput:
+            break;
+        default:
+            GrCrash("Unknown color type.");
+            break;
+    }
+}
+
+void genAttributeCoverage(ShaderCodeSegments* segments,
+                          GrStringBuilder* inOutCoverage) {
+    segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                       GrGLShaderVar::kAttribute_TypeModifier,
+                                       COV_ATTR_NAME);
+    const char *vsName, *fsName;
+    append_varying(GrGLShaderVar::kVec4f_Type, "Coverage", 
+                   segments, &vsName, &fsName);
+    segments->fVSCode.appendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
+    if (inOutCoverage->size()) {
+        segments->fFSCode.appendf("\tvec4 attrCoverage = %s * %s;\n",
+                                  fsName, inOutCoverage->c_str());
+        *inOutCoverage = "attrCoverage";
+    } else {
+        *inOutCoverage = fsName;
+    }
+}
+    
+void genUniformCoverage(ShaderCodeSegments* segments,
+                        GrGLProgram::CachedData* programData,
+                        GrStringBuilder* inOutCoverage) {
+    segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                      GrGLShaderVar::kUniform_TypeModifier,
+                                      COV_UNI_NAME);
+    programData->fUniLocations.fCoverageUni = kUseUniform;
+    if (inOutCoverage->size()) {
+        segments->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
+                                  COV_UNI_NAME, inOutCoverage->c_str());
+        *inOutCoverage = "uniCoverage";
+    } else {
+        *inOutCoverage = COV_UNI_NAME;
+    }
+}
+
+}
+
+void GrGLProgram::genGeometryShader(const GrGLInterface* gl,
+                                    GrGLSLGeneration glslGeneration,
+                                    ShaderCodeSegments* segments) const {
+#if GR_GL_EXPERIMENTAL_GS
+    if (fProgramDesc.fExperimentalGS) {
+        GrAssert(glslGeneration >= k150_GrGLSLGeneration);
+        segments->fGSHeader.append("layout(triangles) in;\n"
+                                   "layout(triangle_strip, max_vertices = 6) out;\n");
+        segments->fGSCode.append("void main() {\n"
+                                 "\tfor (int i = 0; i < 3; ++i) {\n"
+                                  "\t\tgl_Position = gl_in[i].gl_Position;\n");
+        if (this->fProgramDesc.fEmitsPointSize) {
+            segments->fGSCode.append("\t\tgl_PointSize = 1.0;\n");
+        }
+        GrAssert(segments->fGSInputs.count() == segments->fGSOutputs.count());
+        int count = segments->fGSInputs.count();
+        for (int i = 0; i < count; ++i) {
+            segments->fGSCode.appendf("\t\t%s = %s[i];\n",
+                                      segments->fGSOutputs[i].getName().c_str(),
+                                      segments->fGSInputs[i].getName().c_str());
+        }
+        segments->fGSCode.append("\t\tEmitVertex();\n"
+                                 "\t}\n"
+                                 "\tEndPrimitive();\n"
+                                 "}\n");
+    }
+#endif
+}
+
+const char* GrGLProgram::adjustInColor(const GrStringBuilder& inColor) const {
+    if (inColor.size()) {
+          return inColor.c_str();
+    } else {
+        if (ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput) {
+            return all_ones_vec(4);
+        } else {
+            return all_zeros_vec(4);
+        }
+    }
+}
+
+
+bool GrGLProgram::genProgram(const GrGLInterface* gl,
+                             GrGLSLGeneration glslGeneration,
+                             GrGLProgram::CachedData* programData) const {
+
+    ShaderCodeSegments segments;
+    const uint32_t& layout = fProgramDesc.fVertexLayout;
+
+    programData->fUniLocations.reset();
+
+#if GR_GL_EXPERIMENTAL_GS
+    segments.fUsesGS = fProgramDesc.fExperimentalGS;
+#endif
+
+    SkXfermode::Coeff colorCoeff, uniformCoeff;
+    bool applyColorMatrix = SkToBool(fProgramDesc.fColorMatrixEnabled);
+    // The rest of transfer mode color filters have not been implemented
+    if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+        GR_DEBUGCODE(bool success =)
+            SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
+                                    (fProgramDesc.fColorFilterXfermode),
+                                    &uniformCoeff, &colorCoeff);
+        GR_DEBUGASSERT(success);
+    } else {
+        colorCoeff = SkXfermode::kOne_Coeff;
+        uniformCoeff = SkXfermode::kZero_Coeff;
+    }
+
+    // no need to do the color filter / matrix at all if coverage is 0. The
+    // output color is scaled by the coverage. All the dual source outputs are
+    // scaled by the coverage as well.
+    if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fCoverageInput) {
+        colorCoeff = SkXfermode::kZero_Coeff;
+        uniformCoeff = SkXfermode::kZero_Coeff;
+        applyColorMatrix = false;
+    }
+
+    // If we know the final color is going to be all zeros then we can
+    // simplify the color filter coeffecients. needComputedColor will then
+    // come out false below.
+    if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fColorInput) {
+        colorCoeff = SkXfermode::kZero_Coeff;
+        if (SkXfermode::kDC_Coeff == uniformCoeff ||
+            SkXfermode::kDA_Coeff == uniformCoeff) {
+            uniformCoeff = SkXfermode::kZero_Coeff;
+        } else if (SkXfermode::kIDC_Coeff == uniformCoeff ||
+                   SkXfermode::kIDA_Coeff == uniformCoeff) {
+            uniformCoeff = SkXfermode::kOne_Coeff;
+        }
+    }
+
+    bool needColorFilterUniform;
+    bool needComputedColor;
+    needBlendInputs(uniformCoeff, colorCoeff,
+                    &needColorFilterUniform, &needComputedColor);
+
+    // the dual source output has no canonical var name, have to
+    // declare an output, which is incompatible with gl_FragColor/gl_FragData.
+    bool dualSourceOutputWritten = false;
+    segments.fHeader.printf(GrGetGLSLVersionDecl(gl->fBindingsExported,
+                                                 glslGeneration));
+
+    GrGLShaderVar colorOutput;
+    bool isColorDeclared = GrGLSLSetupFSColorOuput(glslGeneration,
+                                                   declared_color_output_name(),
+                                                   &colorOutput);
+    if (isColorDeclared) {
+        segments.fFSOutputs.push_back(colorOutput);
+    }
+
+#if GR_GL_ATTRIBUTE_MATRICES
+    segments.fVSAttrs.push_back().set(GrGLShaderVar::kMat33f_Type,
+        GrGLShaderVar::kAttribute_TypeModifier, VIEW_MATRIX_NAME);
+    programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
+#else
+    segments.fVSUnis.push_back().set(GrGLShaderVar::kMat33f_Type,
+        GrGLShaderVar::kUniform_TypeModifier, VIEW_MATRIX_NAME);
+    programData->fUniLocations.fViewMatrixUni = kUseUniform;
+#endif
+    segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+        GrGLShaderVar::kAttribute_TypeModifier, POS_ATTR_NAME);
+
+    segments.fVSCode.append(
+        "void main() {\n"
+            "\tvec3 pos3 = " VIEW_MATRIX_NAME " * vec3("POS_ATTR_NAME", 1);\n"
+            "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n");
+
+    // incoming color to current stage being processed.
+    GrStringBuilder inColor;
+
+    if (needComputedColor) {
+        genInputColor((ProgramDesc::ColorInput) fProgramDesc.fColorInput,
+                      programData, &segments, &inColor);
+    }
+
+    // we output point size in the GS if present
+    if (fProgramDesc.fEmitsPointSize && !segments.fUsesGS){
+        segments.fVSCode.append("\tgl_PointSize = 1.0;\n");
+    }
+
+    segments.fFSCode.append("void main() {\n");
+
+    // add texture coordinates that are used to the list of vertex attr decls
+    GrStringBuilder texCoordAttrs[GrDrawState::kMaxTexCoords];
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
+            tex_attr_name(t, texCoordAttrs + t);
+            segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+                GrGLShaderVar::kAttribute_TypeModifier,
+                texCoordAttrs[t].c_str());
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the final color
+
+    // if we have color stages string them together, feeding the output color
+    // of each to the next and generating code for each stage.
+    if (needComputedColor) {
+        GrStringBuilder outColor;
+        for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
+            if (fProgramDesc.fStages[s].isEnabled()) {
+                // create var to hold stage result
+                outColor = "color";
+                outColor.appendS32(s);
+                segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
+
+                const char* inCoords;
+                // figure out what our input coords are
+                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
+                    layout) {
+                    inCoords = POS_ATTR_NAME;
+                } else {
+                    int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                     // we better have input tex coordinates if stage is enabled.
+                    GrAssert(tcIdx >= 0);
+                    GrAssert(texCoordAttrs[tcIdx].size());
+                    inCoords = texCoordAttrs[tcIdx].c_str();
+                }
+
+                this->genStageCode(gl,
+                                   s,
+                                   fProgramDesc.fStages[s],
+                                   inColor.size() ? inColor.c_str() : NULL,
+                                   outColor.c_str(),
+                                   inCoords,
+                                   &segments,
+                                   &programData->fUniLocations.fStages[s]);
+                inColor = outColor;
+            }
+        }
+    }
+
+    // if have all ones or zeros for the "dst" input to the color filter then we
+    // may be able to make additional optimizations.
+    if (needColorFilterUniform && needComputedColor && !inColor.size()) {
+        GrAssert(ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput);
+        bool uniformCoeffIsZero = SkXfermode::kIDC_Coeff == uniformCoeff ||
+                                  SkXfermode::kIDA_Coeff == uniformCoeff;
+        if (uniformCoeffIsZero) {
+            uniformCoeff = SkXfermode::kZero_Coeff;
+            bool bogus;
+            needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
+                            &needColorFilterUniform, &bogus);
+        }
+    }
+    if (needColorFilterUniform) {
+        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                         GrGLShaderVar::kUniform_TypeModifier,
+                                         COL_FILTER_UNI_NAME);
+        programData->fUniLocations.fColorFilterUni = kUseUniform;
+    }
+    bool wroteFragColorZero = false;
+    if (SkXfermode::kZero_Coeff == uniformCoeff &&
+        SkXfermode::kZero_Coeff == colorCoeff &&
+        !applyColorMatrix) {
+        segments.fFSCode.appendf("\t%s = %s;\n",
+                                 colorOutput.getName().c_str(),
+                                 all_zeros_vec(4));
+        wroteFragColorZero = true;
+    } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
+        segments.fFSCode.appendf("\tvec4 filteredColor;\n");
+        const char* color = adjustInColor(inColor);
+        addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
+                       colorCoeff, color);
+        inColor = "filteredColor";
+    }
+    if (applyColorMatrix) {
+        segments.fFSUnis.push_back().set(GrGLShaderVar::kMat44f_Type,
+                                         GrGLShaderVar::kUniform_TypeModifier,
+                                         COL_MATRIX_UNI_NAME);
+        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+                                         GrGLShaderVar::kUniform_TypeModifier,
+                                         COL_MATRIX_VEC_UNI_NAME);
+        programData->fUniLocations.fColorMatrixUni = kUseUniform;
+        programData->fUniLocations.fColorMatrixVecUni = kUseUniform;
+        segments.fFSCode.appendf("\tvec4 matrixedColor;\n");
+        const char* color = adjustInColor(inColor);
+        addColorMatrix(&segments.fFSCode, "matrixedColor", color);
+        inColor = "matrixedColor";
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compute the partial coverage (coverage stages and edge aa)
+
+    GrStringBuilder inCoverage;
+    bool coverageIsZero = ProgramDesc::kTransBlack_ColorInput ==
+                          fProgramDesc.fCoverageInput;
+    // we don't need to compute coverage at all if we know the final shader
+    // output will be zero and we don't have a dual src blend output.
+    if (!wroteFragColorZero ||
+        ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+
+        if (!coverageIsZero) {
+            this->genEdgeCoverage(gl,
+                                  layout,
+                                  programData,
+                                  &inCoverage,
+                                  &segments);
+
+            switch (fProgramDesc.fCoverageInput) {
+                case ProgramDesc::kSolidWhite_ColorInput:
+                    // empty string implies solid white
+                    break;
+                case ProgramDesc::kAttribute_ColorInput:
+                    genAttributeCoverage(&segments, &inCoverage);
+                    break;
+                case ProgramDesc::kUniform_ColorInput:
+                    genUniformCoverage(&segments, programData, &inCoverage);
+                    break;
+                default:
+                    GrCrash("Unexpected input coverage.");
+            }
+
+            GrStringBuilder outCoverage;
+            const int& startStage = fProgramDesc.fFirstCoverageStage;
+            for (int s = startStage; s < GrDrawState::kNumStages; ++s) {
+                if (fProgramDesc.fStages[s].isEnabled()) {
+                    // create var to hold stage output
+                    outCoverage = "coverage";
+                    outCoverage.appendS32(s);
+                    segments.fFSCode.appendf("\tvec4 %s;\n",
+                                             outCoverage.c_str());
+
+                    const char* inCoords;
+                    // figure out what our input coords are
+                    if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
+                        layout) {
+                        inCoords = POS_ATTR_NAME;
+                    } else {
+                        int tcIdx =
+                            GrDrawTarget::VertexTexCoordsForStage(s, layout);
+                        // we better have input tex coordinates if stage is
+                        // enabled.
+                        GrAssert(tcIdx >= 0);
+                        GrAssert(texCoordAttrs[tcIdx].size());
+                        inCoords = texCoordAttrs[tcIdx].c_str();
+                    }
+
+                    genStageCode(gl, s,
+                                 fProgramDesc.fStages[s],
+                                 inCoverage.size() ? inCoverage.c_str() : NULL,
+                                 outCoverage.c_str(),
+                                 inCoords,
+                                 &segments,
+                                 &programData->fUniLocations.fStages[s]);
+                    inCoverage = outCoverage;
+                }
+            }
+        }
+        if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+            segments.fFSOutputs.push_back().set(GrGLShaderVar::kVec4f_Type,
+                GrGLShaderVar::kOut_TypeModifier,
+                dual_source_output_name());
+            bool outputIsZero = coverageIsZero;
+            GrStringBuilder coeff;
+            if (!outputIsZero &&
+                ProgramDesc::kCoverage_DualSrcOutput !=
+                fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
+                if (!inColor.size()) {
+                    outputIsZero = true;
+                } else {
+                    if (fProgramDesc.fDualSrcOutput ==
+                        ProgramDesc::kCoverageISA_DualSrcOutput) {
+                        coeff.printf("(1 - %s.a)", inColor.c_str());
+                    } else {
+                        coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
+                    }
+                }
+            }
+            if (outputIsZero) {
+                segments.fFSCode.appendf("\t%s = %s;\n",
+                                         dual_source_output_name(),
+                                         all_zeros_vec(4));
+            } else {
+                modulate_helper(dual_source_output_name(),
+                                coeff.c_str(),
+                                inCoverage.c_str(),
+                                &segments.fFSCode);
+            }
+            dualSourceOutputWritten = true;
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    // combine color and coverage as frag color
+
+    if (!wroteFragColorZero) {
+        if (coverageIsZero) {
+            segments.fFSCode.appendf("\t%s = %s;\n",
+                                     colorOutput.getName().c_str(),
+                                     all_zeros_vec(4));
+        } else {
+            modulate_helper(colorOutput.getName().c_str(),
+                            inColor.c_str(),
+                            inCoverage.c_str(),
+                            &segments.fFSCode);
+        }
+        if (ProgramDesc::kNo_OutputPM == fProgramDesc.fOutputPM) {
+            segments.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(%s.rgb / %s.a, %s.a);\n",
+                                     colorOutput.getName().c_str(),
+                                     colorOutput.getName().c_str(),
+                                     colorOutput.getName().c_str(),
+                                     colorOutput.getName().c_str(),
+                                     colorOutput.getName().c_str());
+        }
+    }
+
+    segments.fVSCode.append("}\n");
+    segments.fFSCode.append("}\n");
+
+    ///////////////////////////////////////////////////////////////////////////
+    // insert GS
+#if GR_DEBUG
+    this->genGeometryShader(gl, glslGeneration, &segments);
+#endif
+
+    ///////////////////////////////////////////////////////////////////////////
+    // compile and setup attribs and unis
+
+    if (!CompileShaders(gl, glslGeneration, segments, programData)) {
+        return false;
+    }
+
+    if (!this->bindOutputsAttribsAndLinkProgram(gl, texCoordAttrs,
+                                                isColorDeclared,
+                                                dualSourceOutputWritten,
+                                                programData)) {
+        return false;
+    }
+
+    this->getUniformLocationsAndInitCache(gl, programData);
+
+    return true;
+}
+
+namespace {
+
+inline void expand_decls(const VarArray& vars,
+                         const GrGLInterface* gl,
+                         GrStringBuilder* string,
+                         GrGLSLGeneration gen) {
+    const int count = vars.count();
+    for (int i = 0; i < count; ++i) {
+        vars[i].appendDecl(gl, string, gen);
+    }
+}
+
+inline void print_shader(int stringCnt,
+                         const char** strings,
+                         int* stringLengths) {
+    for (int i = 0; i < stringCnt; ++i) {
+        if (NULL == stringLengths || stringLengths[i] < 0) {
+            GrPrintf(strings[i]);
+        } else {
+            GrPrintf("%.*s", stringLengths[i], strings[i]);
+        }
+    }
+}
+
+typedef SkTArray<const char*, true>         StrArray;
+#define PREALLOC_STR_ARRAY(N) SkSTArray<(N), const char*, true>
+
+typedef SkTArray<int, true>                 LengthArray;
+#define PREALLOC_LENGTH_ARRAY(N) SkSTArray<(N), int, true>
+
+// these shouldn't relocate
+typedef GrTAllocator<GrStringBuilder>       TempArray;
+#define PREALLOC_TEMP_ARRAY(N) GrSTAllocator<(N), GrStringBuilder>
+
+inline void append_string(const GrStringBuilder& str,
+                          StrArray* strings,
+                          LengthArray* lengths) {
+    int length = (int) str.size();
+    if (length) {
+        strings->push_back(str.c_str());
+        lengths->push_back(length);
+    }
+    GrAssert(strings->count() == lengths->count());
+}
+
+inline void append_decls(const VarArray& vars,
+                         const GrGLInterface* gl,
+                         StrArray* strings,
+                         LengthArray* lengths,
+                         TempArray* temp,
+                         GrGLSLGeneration gen) {
+    expand_decls(vars, gl, &temp->push_back(), gen);
+    append_string(temp->back(), strings, lengths);
+}
+
+}
+
+bool GrGLProgram::CompileShaders(const GrGLInterface* gl,
+                                 GrGLSLGeneration glslGeneration,
+                                 const ShaderCodeSegments& segments,
+                                 CachedData* programData) {
+    enum { kPreAllocStringCnt = 8 };
+
+    PREALLOC_STR_ARRAY(kPreAllocStringCnt)    strs;
+    PREALLOC_LENGTH_ARRAY(kPreAllocStringCnt) lengths;
+    PREALLOC_TEMP_ARRAY(kPreAllocStringCnt)   temps;
+
+    GrStringBuilder unis;
+    GrStringBuilder inputs;
+    GrStringBuilder outputs;
+
+    append_string(segments.fHeader, &strs, &lengths);
+    append_decls(segments.fVSUnis, gl, &strs, &lengths, &temps, glslGeneration);
+    append_decls(segments.fVSAttrs, gl, &strs, &lengths,
+                 &temps, glslGeneration);
+    append_decls(segments.fVSOutputs, gl, &strs, &lengths,
+                 &temps, glslGeneration);
+    append_string(segments.fVSCode, &strs, &lengths);
+
+#if PRINT_SHADERS
+    print_shader(strs.count(), &strs[0], &lengths[0]);
+    GrPrintf("\n");
+#endif
+
+    programData->fVShaderID =
+        CompileShader(gl, GR_GL_VERTEX_SHADER, strs.count(),
+                      &strs[0], &lengths[0]);
+
+    if (!programData->fVShaderID) {
+        return false;
+    }
+    if (segments.fUsesGS) {
+        strs.reset();
+        lengths.reset();
+        temps.reset();
+        append_string(segments.fHeader, &strs, &lengths);
+        append_string(segments.fGSHeader, &strs, &lengths);
+        append_decls(segments.fGSInputs, gl, &strs, &lengths,
+                     &temps, glslGeneration);
+        append_decls(segments.fGSOutputs, gl, &strs, &lengths,
+                     &temps, glslGeneration);
+        append_string(segments.fGSCode, &strs, &lengths);
+#if PRINT_SHADERS
+        print_shader(strs.count(), &strs[0], &lengths[0]);
+        GrPrintf("\n");
+#endif
+        programData->fGShaderID =
+            CompileShader(gl, GR_GL_GEOMETRY_SHADER, strs.count(),
+                          &strs[0], &lengths[0]);
+    } else {
+        programData->fGShaderID = 0;
+    }
+
+    strs.reset();
+    lengths.reset();
+    temps.reset();
+
+    append_string(segments.fHeader, &strs, &lengths);
+    GrStringBuilder precisionStr(GrGetGLSLShaderPrecisionDecl(gl->fBindingsExported));
+    append_string(precisionStr, &strs, &lengths);
+    append_decls(segments.fFSUnis, gl, &strs, &lengths, &temps, glslGeneration);
+    append_decls(segments.fFSInputs, gl, &strs, &lengths,
+                 &temps, glslGeneration);
+    // We shouldn't have declared outputs on 1.10
+    GrAssert(k110_GrGLSLGeneration != glslGeneration ||
+             segments.fFSOutputs.empty());
+    append_decls(segments.fFSOutputs, gl, &strs, &lengths,
+                 &temps, glslGeneration);
+    append_string(segments.fFSFunctions, &strs, &lengths);
+    append_string(segments.fFSCode, &strs, &lengths);
+
+#if PRINT_SHADERS
+    print_shader(strs.count(), &strs[0], &lengths[0]);
+    GrPrintf("\n");
+#endif
+
+    programData->fFShaderID =
+        CompileShader(gl, GR_GL_FRAGMENT_SHADER, strs.count(),
+                      &strs[0], &lengths[0]);
+
+    if (!programData->fFShaderID) {
+        return false;
+    }
+
+    return true;
+}
+
+GrGLuint GrGLProgram::CompileShader(const GrGLInterface* gl,
+                                    GrGLenum type,
+                                    int stringCnt,
+                                    const char** strings,
+                                    int* stringLengths) {
+    SK_TRACE_EVENT1("GrGLProgram::CompileShader",
+                    "stringCount", SkStringPrintf("%i", stringCnt).c_str());
+
+    GrGLuint shader;
+    GR_GL_CALL_RET(gl, shader, CreateShader(type));
+    if (0 == shader) {
+        return 0;
+    }
+
+    GrGLint compiled = GR_GL_INIT_ZERO;
+    GR_GL_CALL(gl, ShaderSource(shader, stringCnt, strings, stringLengths));
+    GR_GL_CALL(gl, CompileShader(shader));
+    GR_GL_CALL(gl, GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
+
+    if (!compiled) {
+        GrGLint infoLen = GR_GL_INIT_ZERO;
+        GR_GL_CALL(gl, GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
+        SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
+        if (infoLen > 0) {
+            // retrieve length even though we don't need it to workaround
+            // bug in chrome cmd buffer param validation.
+            GrGLsizei length = GR_GL_INIT_ZERO;
+            GR_GL_CALL(gl, GetShaderInfoLog(shader, infoLen+1, 
+                                            &length, (char*)log.get()));
+            print_shader(stringCnt, strings, stringLengths);
+            GrPrintf("\n%s", log.get());
+        }
+        GrAssert(!"Shader compilation failed!");
+        GR_GL_CALL(gl, DeleteShader(shader));
+        return 0;
+    }
+    return shader;
+}
+
+bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
+                                        const GrGLInterface* gl,
+                                        GrStringBuilder texCoordAttrNames[],
+                                        bool bindColorOut,
+                                        bool bindDualSrcOut,
+                                        CachedData* programData) const {
+    GR_GL_CALL_RET(gl, programData->fProgramID, CreateProgram());
+    if (!programData->fProgramID) {
+        return false;
+    }
+    const GrGLint& progID = programData->fProgramID;
+
+    GR_GL_CALL(gl, AttachShader(progID, programData->fVShaderID));
+    if (programData->fGShaderID) {
+        GR_GL_CALL(gl, AttachShader(progID, programData->fGShaderID));
+    }
+    GR_GL_CALL(gl, AttachShader(progID, programData->fFShaderID));
+
+    if (bindColorOut) {
+        GR_GL_CALL(gl, BindFragDataLocation(programData->fProgramID,
+                                          0, declared_color_output_name()));
+    }
+    if (bindDualSrcOut) {
+        GR_GL_CALL(gl, BindFragDataLocationIndexed(programData->fProgramID,
+                                          0, 1, dual_source_output_name()));
+    }
+
+    // Bind the attrib locations to same values for all shaders
+    GR_GL_CALL(gl, BindAttribLocation(progID, PositionAttributeIdx(),
+                                      POS_ATTR_NAME));
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (texCoordAttrNames[t].size()) {
+            GR_GL_CALL(gl, BindAttribLocation(progID,
+                                              TexCoordAttributeIdx(t),
+                                              texCoordAttrNames[t].c_str()));
+        }
+    }
+
+    if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
+        GR_GL_CALL(gl, BindAttribLocation(progID,
+                                          ViewMatrixAttributeIdx(),
+                                          VIEW_MATRIX_NAME));
+    }
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        const StageUniLocations& unis = programData->fUniLocations.fStages[s];
+        if (kSetAsAttribute == unis.fTextureMatrixUni) {
+            GrStringBuilder matName;
+            tex_matrix_name(s, &matName);
+            GR_GL_CALL(gl, BindAttribLocation(progID,
+                                              TextureMatrixAttributeIdx(s),
+                                              matName.c_str()));
+        }
+    }
+
+    GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(),
+                                      COL_ATTR_NAME));
+    GR_GL_CALL(gl, BindAttribLocation(progID, CoverageAttributeIdx(),
+                                      COV_ATTR_NAME));
+    GR_GL_CALL(gl, BindAttribLocation(progID, EdgeAttributeIdx(),
+                                      EDGE_ATTR_NAME));
+
+    GR_GL_CALL(gl, LinkProgram(progID));
+
+    GrGLint linked = GR_GL_INIT_ZERO;
+    GR_GL_CALL(gl, GetProgramiv(progID, GR_GL_LINK_STATUS, &linked));
+    if (!linked) {
+        GrGLint infoLen = GR_GL_INIT_ZERO;
+        GR_GL_CALL(gl, GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen));
+        SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
+        if (infoLen > 0) {
+            // retrieve length even though we don't need it to workaround
+            // bug in chrome cmd buffer param validation.
+            GrGLsizei length = GR_GL_INIT_ZERO;
+            GR_GL_CALL(gl, GetProgramInfoLog(progID, infoLen+1,
+                                             &length, (char*)log.get()));
+            GrPrintf((char*)log.get());
+        }
+        GrAssert(!"Error linking program");
+        GR_GL_CALL(gl, DeleteProgram(progID));
+        programData->fProgramID = 0;
+        return false;
+    }
+    return true;
+}
+
+void GrGLProgram::getUniformLocationsAndInitCache(const GrGLInterface* gl, 
+                                                  CachedData* programData) const {
+    const GrGLint& progID = programData->fProgramID;
+
+    if (kUseUniform == programData->fUniLocations.fViewMatrixUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fViewMatrixUni,
+                       GetUniformLocation(progID, VIEW_MATRIX_NAME));
+        GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
+    }
+    if (kUseUniform == programData->fUniLocations.fColorUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fColorUni,
+                       GetUniformLocation(progID, COL_UNI_NAME));
+        GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
+    }
+    if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fColorFilterUni, 
+                       GetUniformLocation(progID, COL_FILTER_UNI_NAME));
+        GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
+    }
+
+    if (kUseUniform == programData->fUniLocations.fColorMatrixUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fColorMatrixUni,
+                       GetUniformLocation(progID, COL_MATRIX_UNI_NAME));
+    }
+
+    if (kUseUniform == programData->fUniLocations.fColorMatrixVecUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fColorMatrixVecUni,
+                       GetUniformLocation(progID, COL_MATRIX_VEC_UNI_NAME));
+    }
+    if (kUseUniform == programData->fUniLocations.fCoverageUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fCoverageUni,
+                       GetUniformLocation(progID, COV_UNI_NAME));
+        GrAssert(kUnusedUniform != programData->fUniLocations.fCoverageUni);
+    }
+
+    if (kUseUniform == programData->fUniLocations.fEdgesUni) {
+        GR_GL_CALL_RET(gl, programData->fUniLocations.fEdgesUni,
+                       GetUniformLocation(progID, EDGES_UNI_NAME));
+        GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
+    } else {
+        programData->fUniLocations.fEdgesUni = kUnusedUniform;
+    }
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        StageUniLocations& locations = programData->fUniLocations.fStages[s];
+        if (fProgramDesc.fStages[s].isEnabled()) {
+            if (kUseUniform == locations.fTextureMatrixUni) {
+                GrStringBuilder texMName;
+                tex_matrix_name(s, &texMName);
+                GR_GL_CALL_RET(gl, locations.fTextureMatrixUni,
+                               GetUniformLocation(progID, texMName.c_str()));
+                GrAssert(kUnusedUniform != locations.fTextureMatrixUni);
+            }
+
+            if (kUseUniform == locations.fSamplerUni) {
+                GrStringBuilder samplerName;
+                sampler_name(s, &samplerName);
+                GR_GL_CALL_RET(gl, locations.fSamplerUni,
+                               GetUniformLocation(progID,samplerName.c_str()));
+                GrAssert(kUnusedUniform != locations.fSamplerUni);
+            }
+
+            if (kUseUniform == locations.fNormalizedTexelSizeUni) {
+                GrStringBuilder texelSizeName;
+                normalized_texel_size_name(s, &texelSizeName);
+                GR_GL_CALL_RET(gl, locations.fNormalizedTexelSizeUni,
+                               GetUniformLocation(progID, texelSizeName.c_str()));
+                GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni);
+            }
+
+            if (kUseUniform == locations.fRadial2Uni) {
+                GrStringBuilder radial2ParamName;
+                radial2_param_name(s, &radial2ParamName);
+                GR_GL_CALL_RET(gl, locations.fRadial2Uni,
+                               GetUniformLocation(progID, radial2ParamName.c_str()));
+                GrAssert(kUnusedUniform != locations.fRadial2Uni);
+            }
+
+            if (kUseUniform == locations.fTexDomUni) {
+                GrStringBuilder texDomName;
+                tex_domain_name(s, &texDomName);
+                GR_GL_CALL_RET(gl, locations.fTexDomUni,
+                               GetUniformLocation(progID, texDomName.c_str()));
+                GrAssert(kUnusedUniform != locations.fTexDomUni);
+            }
+
+            GrStringBuilder kernelName, imageIncrementName;
+            convolve_param_names(s, &kernelName, &imageIncrementName);
+            if (kUseUniform == locations.fKernelUni) {
+                GR_GL_CALL_RET(gl, locations.fKernelUni, 
+                               GetUniformLocation(progID, kernelName.c_str()));
+                GrAssert(kUnusedUniform != locations.fKernelUni);
+            }
+
+            if (kUseUniform == locations.fImageIncrementUni) {
+                GR_GL_CALL_RET(gl, locations.fImageIncrementUni, 
+                               GetUniformLocation(progID, 
+                                                  imageIncrementName.c_str()));
+                GrAssert(kUnusedUniform != locations.fImageIncrementUni);
+            }
+        }
+    }
+    GR_GL_CALL(gl, UseProgram(progID));
+
+    // init sampler unis and set bogus values for state tracking
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if (kUnusedUniform != programData->fUniLocations.fStages[s].fSamplerUni) {
+            GR_GL_CALL(gl, Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s));
+        }
+        programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix();
+        programData->fRadial2CenterX1[s] = GR_ScalarMax;
+        programData->fRadial2Radius0[s] = -GR_ScalarMax;
+        programData->fTextureWidth[s] = -1;
+        programData->fTextureHeight[s] = -1;
+    }
+    programData->fViewMatrix = GrMatrix::InvalidMatrix();
+    programData->fColor = GrColor_ILLEGAL;
+    programData->fColorFilterColor = GrColor_ILLEGAL;
+}
+
+//============================================================================
+// Stage code generation
+//============================================================================
+
+namespace {
+
+bool isRadialMapping(GrGLProgram::StageDesc::CoordMapping mapping) {
+    return
+       (GrGLProgram::StageDesc::kRadial2Gradient_CoordMapping == mapping ||
+        GrGLProgram::StageDesc::kRadial2GradientDegenerate_CoordMapping == mapping);
+}
+
+GrGLShaderVar* genRadialVS(int stageNum,
+                        ShaderCodeSegments* segments,
+                        GrGLProgram::StageUniLocations* locations,
+                        const char** radial2VaryingVSName,
+                        const char** radial2VaryingFSName,
+                        const char* varyingVSName,
+                        int varyingDims, int coordDims) {
+
+    GrGLShaderVar* radial2FSParams = &segments->fFSUnis.push_back();
+    radial2FSParams->setType(GrGLShaderVar::kFloat_Type);
+    radial2FSParams->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+    radial2FSParams->setArrayCount(6);
+    radial2_param_name(stageNum, radial2FSParams->accessName());
+    segments->fVSUnis.push_back(*radial2FSParams).setEmitPrecision(true);
+
+    locations->fRadial2Uni = kUseUniform;
+
+    // for radial grads without perspective we can pass the linear
+    // part of the quadratic as a varying.
+    if (varyingDims == coordDims) {
+        GrAssert(2 == coordDims);
+        append_varying(GrGLShaderVar::kFloat_Type,
+                       "Radial2BCoeff",
+                       stageNum,
+                       segments,
+                       radial2VaryingVSName,
+                       radial2VaryingFSName);
+
+        GrStringBuilder radial2p2;
+        GrStringBuilder radial2p3;
+        radial2FSParams->appendArrayAccess(2, &radial2p2);
+        radial2FSParams->appendArrayAccess(3, &radial2p3);
+
+        // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
+        const char* r2ParamName = radial2FSParams->getName().c_str();
+        segments->fVSCode.appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
+                                  *radial2VaryingVSName, radial2p2.c_str(),
+                                  varyingVSName, radial2p3.c_str());
+    }
+
+    return radial2FSParams;
+}
+
+bool genRadial2GradientCoordMapping(int stageNum,
+                                    ShaderCodeSegments* segments,
+                                    const char* radial2VaryingFSName,
+                                    GrGLShaderVar* radial2Params,
+                                    GrStringBuilder& sampleCoords,
+                                    GrStringBuilder& fsCoordName,
+                                    int varyingDims,
+                                    int coordDims) {
+    GrStringBuilder cName("c");
+    GrStringBuilder ac4Name("ac4");
+    GrStringBuilder rootName("root");
+
+    cName.appendS32(stageNum);
+    ac4Name.appendS32(stageNum);
+    rootName.appendS32(stageNum);
+
+    GrStringBuilder radial2p0;
+    GrStringBuilder radial2p1;
+    GrStringBuilder radial2p2;
+    GrStringBuilder radial2p3;
+    GrStringBuilder radial2p4;
+    GrStringBuilder radial2p5;
+    radial2Params->appendArrayAccess(0, &radial2p0);
+    radial2Params->appendArrayAccess(1, &radial2p1);
+    radial2Params->appendArrayAccess(2, &radial2p2);
+    radial2Params->appendArrayAccess(3, &radial2p3);
+    radial2Params->appendArrayAccess(4, &radial2p4);
+    radial2Params->appendArrayAccess(5, &radial2p5);
+
+    // if we were able to interpolate the linear component bVar is the varying
+    // otherwise compute it
+    GrStringBuilder bVar;
+    if (coordDims == varyingDims) {
+        bVar = radial2VaryingFSName;
+        GrAssert(2 == varyingDims);
+    } else {
+        GrAssert(3 == varyingDims);
+        bVar = "b";
+        bVar.appendS32(stageNum);
+        segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
+                                    bVar.c_str(), radial2p2.c_str(),
+                                    fsCoordName.c_str(), radial2p3.c_str());
+    }
+
+    // c = (x^2)+(y^2) - params[4]
+    segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s;\n",
+                              cName.c_str(), fsCoordName.c_str(),
+                              fsCoordName.c_str(),
+                              radial2p4.c_str());
+    // ac4 = 4.0 * params[0] * c
+    segments->fFSCode.appendf("\tfloat %s = %s * 4.0 * %s;\n",
+                              ac4Name.c_str(), radial2p0.c_str(),
+                              cName.c_str());
+
+    // root = sqrt(b^2-4ac)
+    // (abs to avoid exception due to fp precision)
+    segments->fFSCode.appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
+                              rootName.c_str(), bVar.c_str(), bVar.c_str(),
+                              ac4Name.c_str());
+
+    // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
+    // y coord is 0.5 (texture is effectively 1D)
+    sampleCoords.printf("vec2((-%s + %s * %s) * %s, 0.5)",
+                        bVar.c_str(), radial2p5.c_str(),
+                        rootName.c_str(), radial2p1.c_str());
+    return true;
+}
+
+bool genRadial2GradientDegenerateCoordMapping(int stageNum,
+                                              ShaderCodeSegments* segments,
+                                              const char* radial2VaryingFSName,
+                                              GrGLShaderVar* radial2Params,
+                                              GrStringBuilder& sampleCoords,
+                                              GrStringBuilder& fsCoordName,
+                                              int varyingDims,
+                                              int coordDims) {
+    GrStringBuilder cName("c");
+
+    cName.appendS32(stageNum);
+
+    GrStringBuilder radial2p2;
+    GrStringBuilder radial2p3;
+    GrStringBuilder radial2p4;
+    radial2Params->appendArrayAccess(2, &radial2p2);
+    radial2Params->appendArrayAccess(3, &radial2p3);
+    radial2Params->appendArrayAccess(4, &radial2p4);
+
+    // if we were able to interpolate the linear component bVar is the varying
+    // otherwise compute it
+    GrStringBuilder bVar;
+    if (coordDims == varyingDims) {
+        bVar = radial2VaryingFSName;
+        GrAssert(2 == varyingDims);
+    } else {
+        GrAssert(3 == varyingDims);
+        bVar = "b";
+        bVar.appendS32(stageNum);
+        segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
+                                    bVar.c_str(), radial2p2.c_str(),
+                                    fsCoordName.c_str(), radial2p3.c_str());
+    }
+
+    // c = (x^2)+(y^2) - params[4]
+    segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s;\n",
+                              cName.c_str(), fsCoordName.c_str(),
+                              fsCoordName.c_str(),
+                              radial2p4.c_str());
+
+    // x coord is: -c/b
+    // y coord is 0.5 (texture is effectively 1D)
+    sampleCoords.printf("vec2((-%s / %s), 0.5)", cName.c_str(), bVar.c_str());
+    return true;
+}
+
+void gen2x2FS(int stageNum,
+              ShaderCodeSegments* segments,
+              GrGLProgram::StageUniLocations* locations,
+              GrStringBuilder* sampleCoords,
+              const char* samplerName,
+              const char* texelSizeName,
+              const char* swizzle,
+              const char* fsOutColor,
+              GrStringBuilder& texFunc,
+              GrStringBuilder& modulate,
+              bool complexCoord,
+              int coordDims) {
+    locations->fNormalizedTexelSizeUni = kUseUniform;
+    if (complexCoord) {
+        // assign the coord to a var rather than compute 4x.
+        GrStringBuilder coordVar("tCoord");
+        coordVar.appendS32(stageNum);
+        segments->fFSCode.appendf("\t%s %s = %s;\n",
+                            float_vector_type_str(coordDims),
+                            coordVar.c_str(), sampleCoords->c_str());
+        *sampleCoords = coordVar;
+    }
+    GrAssert(2 == coordDims);
+    GrStringBuilder accumVar("accum");
+    accumVar.appendS32(stageNum);
+    segments->fFSCode.appendf("\tvec4 %s  = %s(%s, %s + vec2(-%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+    segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+    segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(-%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+    segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
+    segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
+
+}
+
+void genConvolutionVS(int stageNum,
+                      const StageDesc& desc,
+                      ShaderCodeSegments* segments,
+                      GrGLProgram::StageUniLocations* locations,
+                      GrGLShaderVar** kernel,
+                      const char** imageIncrementName,
+                      const char* varyingVSName) {
+    //GrGLShaderVar* kernel = &segments->fFSUnis.push_back();
+    *kernel = &segments->fFSUnis.push_back();
+    (*kernel)->setType(GrGLShaderVar::kFloat_Type);
+    (*kernel)->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+    (*kernel)->setArrayCount(desc.fKernelWidth);
+    GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
+    imgInc->setType(GrGLShaderVar::kVec2f_Type);
+    imgInc->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+
+    convolve_param_names(stageNum,
+                         (*kernel)->accessName(),
+                         imgInc->accessName());
+    *imageIncrementName = imgInc->getName().c_str();
+
+    // need image increment in both VS and FS
+    segments->fVSUnis.push_back(*imgInc).setEmitPrecision(true);
+
+    locations->fKernelUni = kUseUniform;
+    locations->fImageIncrementUni = kUseUniform;
+    float scale = (desc.fKernelWidth - 1) * 0.5f;
+    segments->fVSCode.appendf("\t%s -= vec2(%g, %g) * %s;\n",
+                                  varyingVSName, scale, scale,
+                                  *imageIncrementName);
+}
+
+void genConvolutionFS(int stageNum,
+                      const StageDesc& desc,
+                      ShaderCodeSegments* segments,
+                      const char* samplerName,
+                      GrGLShaderVar* kernel,
+                      const char* swizzle,
+                      const char* imageIncrementName,
+                      const char* fsOutColor,
+                      GrStringBuilder& sampleCoords,
+                      GrStringBuilder& texFunc,
+                      GrStringBuilder& modulate) {
+    GrStringBuilder sumVar("sum");
+    sumVar.appendS32(stageNum);
+    GrStringBuilder coordVar("coord");
+    coordVar.appendS32(stageNum);
+
+    GrStringBuilder kernelIndex;
+    kernel->appendArrayAccess("i", &kernelIndex);
+
+    segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n",
+                              sumVar.c_str());
+    segments->fFSCode.appendf("\tvec2 %s = %s;\n", 
+                              coordVar.c_str(),
+                              sampleCoords.c_str());
+    segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n",
+                              desc.fKernelWidth);
+    segments->fFSCode.appendf("\t\t%s += %s(%s, %s)%s * %s;\n",
+                              sumVar.c_str(), texFunc.c_str(),
+                              samplerName, coordVar.c_str(), swizzle,
+                              kernelIndex.c_str());
+    segments->fFSCode.appendf("\t\t%s += %s;\n",
+                              coordVar.c_str(),
+                              imageIncrementName);
+    segments->fFSCode.appendf("\t}\n");
+    segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
+                              sumVar.c_str(), modulate.c_str());
+}
+
+}
+
+void GrGLProgram::genStageCode(const GrGLInterface* gl,
+                               int stageNum,
+                               const GrGLProgram::StageDesc& desc,
+                               const char* fsInColor, // NULL means no incoming color
+                               const char* fsOutColor,
+                               const char* vsInCoord,
+                               ShaderCodeSegments* segments,
+                               StageUniLocations* locations) const {
+
+    GrAssert(stageNum >= 0 && stageNum <= GrDrawState::kNumStages);
+    GrAssert((desc.fInConfigFlags & StageDesc::kInConfigBitMask) ==
+             desc.fInConfigFlags);
+
+    // First decide how many coords are needed to access the texture
+    // Right now it's always 2 but we could start using 1D textures for
+    // gradients.
+    static const int coordDims = 2;
+    int varyingDims;
+    /// Vertex Shader Stuff
+
+    // decide whether we need a matrix to transform texture coords
+    // and whether the varying needs a perspective coord.
+    const char* matName = NULL;
+    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
+        varyingDims = coordDims;
+    } else {
+        GrGLShaderVar* mat;
+    #if GR_GL_ATTRIBUTE_MATRICES
+        mat = &segments->fVSAttrs.push_back();
+        mat->setTypeModifier(GrGLShaderVar::kAttribute_TypeModifier);
+        locations->fTextureMatrixUni = kSetAsAttribute;
+    #else
+        mat = &segments->fVSUnis.push_back();
+        mat->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+        locations->fTextureMatrixUni = kUseUniform;
+    #endif
+        tex_matrix_name(stageNum, mat->accessName());
+        mat->setType(GrGLShaderVar::kMat33f_Type);
+        matName = mat->getName().c_str();
+
+        if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
+            varyingDims = coordDims;
+        } else {
+            varyingDims = coordDims + 1;
+        }
+    }
+
+    segments->fFSUnis.push_back().set(GrGLShaderVar::kSampler2D_Type,
+        GrGLShaderVar::kUniform_TypeModifier, "");
+    sampler_name(stageNum, segments->fFSUnis.back().accessName());
+    locations->fSamplerUni = kUseUniform;
+    const char* samplerName = segments->fFSUnis.back().getName().c_str();
+
+    const char* texelSizeName = NULL;
+    if (StageDesc::k2x2_FetchMode == desc.fFetchMode) {
+        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec2f_Type,
+            GrGLShaderVar::kUniform_TypeModifier, "");
+        normalized_texel_size_name(stageNum, segments->fFSUnis.back().accessName());
+        texelSizeName = segments->fFSUnis.back().getName().c_str();
+    }
+
+    const char *varyingVSName, *varyingFSName;
+    append_varying(float_vector_type(varyingDims),
+                    "Stage",
+                   stageNum,
+                   segments,
+                   &varyingVSName,
+                   &varyingFSName);
+
+    if (!matName) {
+        GrAssert(varyingDims == coordDims);
+        segments->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
+    } else {
+        // varying = texMatrix * texCoord
+        segments->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
+                                  varyingVSName, matName, vsInCoord,
+                                  vector_all_coords(varyingDims));
+    }
+
+    GrGLShaderVar* radial2Params = NULL;
+    const char* radial2VaryingVSName = NULL;
+    const char* radial2VaryingFSName = NULL;
+
+    if (isRadialMapping((StageDesc::CoordMapping) desc.fCoordMapping)) {
+        radial2Params = genRadialVS(stageNum, segments,
+                                    locations,
+                                    &radial2VaryingVSName,
+                                    &radial2VaryingFSName,
+                                    varyingVSName,
+                                    varyingDims, coordDims);
+    }
+
+    GrGLShaderVar* kernel = NULL;
+    const char* imageIncrementName = NULL;
+    if (StageDesc::kConvolution_FetchMode == desc.fFetchMode) {
+        genConvolutionVS(stageNum, desc, segments, locations,
+                         &kernel, &imageIncrementName, varyingVSName);
+    }
+
+    /// Fragment Shader Stuff
+    GrStringBuilder fsCoordName;
+    // function used to access the shader, may be made projective
+    GrStringBuilder texFunc("texture2D");
+    if (desc.fOptFlags & (StageDesc::kIdentityMatrix_OptFlagBit |
+                          StageDesc::kNoPerspective_OptFlagBit)) {
+        GrAssert(varyingDims == coordDims);
+        fsCoordName = varyingFSName;
+    } else {
+        // if we have to do some special op on the varyings to get
+        // our final tex coords then when in perspective we have to
+        // do an explicit divide. Otherwise, we can use a Proj func.
+        if  (StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
+             StageDesc::kSingle_FetchMode == desc.fFetchMode) {
+            texFunc.append("Proj");
+            fsCoordName = varyingFSName;
+        } else {
+            fsCoordName = "inCoord";
+            fsCoordName.appendS32(stageNum);
+            segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
+                                GrGLShaderVar::TypeString(float_vector_type(coordDims)),
+                                fsCoordName.c_str(),
+                                varyingFSName,
+                                vector_nonhomog_coords(varyingDims),
+                                varyingFSName,
+                                vector_homog_coord(varyingDims));
+        }
+    }
+
+    GrStringBuilder sampleCoords;
+    bool complexCoord = false;
+    switch (desc.fCoordMapping) {
+    case StageDesc::kIdentity_CoordMapping:
+        sampleCoords = fsCoordName;
+        break;
+    case StageDesc::kSweepGradient_CoordMapping:
+        sampleCoords.printf("vec2(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5, 0.5)", fsCoordName.c_str(), fsCoordName.c_str());
+        complexCoord = true;
+        break;
+    case StageDesc::kRadialGradient_CoordMapping:
+        sampleCoords.printf("vec2(length(%s.xy), 0.5)", fsCoordName.c_str());
+        complexCoord = true;
+        break;
+    case StageDesc::kRadial2Gradient_CoordMapping:
+        complexCoord = genRadial2GradientCoordMapping(
+                           stageNum, segments,
+                           radial2VaryingFSName, radial2Params,
+                           sampleCoords, fsCoordName,
+                           varyingDims, coordDims);
+
+        break;
+    case StageDesc::kRadial2GradientDegenerate_CoordMapping:
+        complexCoord = genRadial2GradientDegenerateCoordMapping(
+                           stageNum, segments,
+                           radial2VaryingFSName, radial2Params,
+                           sampleCoords, fsCoordName,
+                           varyingDims, coordDims);
+        break;
+
+    };
+
+    const char* swizzle = "";
+    if (desc.fInConfigFlags & StageDesc::kSwapRAndB_InConfigFlag) {
+        GrAssert(!(desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag));
+        swizzle = ".bgra";
+    } else if (desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag) {
+        GrAssert(!(desc.fInConfigFlags &
+                   StageDesc::kMulRGBByAlpha_InConfigFlag));
+        swizzle = ".aaaa";
+    } 
+
+    GrStringBuilder modulate;
+    if (NULL != fsInColor) {
+        modulate.printf(" * %s", fsInColor);
+    }
+
+    if (desc.fOptFlags &
+        StageDesc::kCustomTextureDomain_OptFlagBit) {
+        GrStringBuilder texDomainName;
+        tex_domain_name(stageNum, &texDomainName);
+        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
+            GrGLShaderVar::kUniform_TypeModifier, texDomainName);
+        GrStringBuilder coordVar("clampCoord");
+        segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
+                                  float_vector_type_str(coordDims),
+                                  coordVar.c_str(),
+                                  sampleCoords.c_str(),
+                                  texDomainName.c_str(),
+                                  texDomainName.c_str());
+        sampleCoords = coordVar;
+        locations->fTexDomUni = kUseUniform;
+    }
+
+    switch (desc.fFetchMode) {
+    case StageDesc::k2x2_FetchMode:
+        GrAssert(!(desc.fInConfigFlags &
+                   StageDesc::kMulRGBByAlpha_InConfigFlag));
+        gen2x2FS(stageNum, segments, locations, &sampleCoords,
+            samplerName, texelSizeName, swizzle, fsOutColor,
+            texFunc, modulate, complexCoord, coordDims);
+        break;
+    case StageDesc::kConvolution_FetchMode:
+        GrAssert(!(desc.fInConfigFlags &
+                   StageDesc::kMulRGBByAlpha_InConfigFlag));
+        genConvolutionFS(stageNum, desc, segments,
+            samplerName, kernel, swizzle, imageIncrementName, fsOutColor,
+            sampleCoords, texFunc, modulate);
+        break;
+    default:
+        if (desc.fInConfigFlags & StageDesc::kMulRGBByAlpha_InConfigFlag) {
+            GrAssert(!(desc.fInConfigFlags & 
+                       StageDesc::kSmearAlpha_InConfigFlag));
+            segments->fFSCode.appendf("\t%s = %s(%s, %s)%s;\n",
+                                      fsOutColor, texFunc.c_str(), 
+                                      samplerName, sampleCoords.c_str(),
+                                      swizzle);
+            segments->fFSCode.appendf("\t%s = vec4(%s.rgb*%s.a,%s.a)%s;\n",
+                                      fsOutColor, fsOutColor, fsOutColor,
+                                      fsOutColor, modulate.c_str());
+        } else {
+            segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n",
+                                      fsOutColor, texFunc.c_str(), 
+                                      samplerName, sampleCoords.c_str(),
+                                      swizzle, modulate.c_str());
+        }
+    }
+}
+
+
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
new file mode 100644
index 0000000..dc6093e
--- /dev/null
+++ b/src/gpu/gl/GrGLProgram.h
@@ -0,0 +1,390 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLProgram_DEFINED
+#define GrGLProgram_DEFINED
+
+#include "../GrDrawState.h"
+#include "GrGLInterface.h"
+#include "GrGLSL.h"
+#include "../GrStringBuilder.h"
+#include "../GrGpu.h"
+
+#include "SkXfermode.h"
+
+class GrBinHashKeyBuilder;
+
+struct ShaderCodeSegments;
+
+// optionally compile the experimental GS code. Set to GR_DEBUG
+// so that debug build bots will execute the code.
+#define GR_GL_EXPERIMENTAL_GS GR_DEBUG
+
+/**
+ * This class manages a GPU program and records per-program information.
+ * We can specify the attribute locations so that they are constant
+ * across our shaders. But the driver determines the uniform locations
+ * at link time. We don't need to remember the sampler uniform location
+ * because we will bind a texture slot to it and never change it
+ * Uniforms are program-local so we can't rely on fHWState to hold the
+ * previous uniform state after a program change.
+ */
+class GrGLProgram {
+public:
+
+    class CachedData;
+
+    GrGLProgram();
+    ~GrGLProgram();
+
+    /**
+     *  This is the heavy initilization routine for building a GLProgram.
+     *  The result of heavy init is not stored in datamembers of GrGLProgam,
+     *  but in a separate cacheable container.
+     */
+    bool genProgram(const GrGLInterface* gl,
+                    GrGLSLGeneration glslVersion,
+                    CachedData* programData) const;
+
+     /**
+      * The shader may modify the blend coeffecients. Params are in/out
+      */
+     void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
+
+    /**
+     * Attribute indices. These should not overlap. Matrices consume 3 slots.
+     */
+    static int PositionAttributeIdx() { return 0; }
+    static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
+    static int ColorAttributeIdx() { return 1 + GrDrawState::kMaxTexCoords; }
+    static int CoverageAttributeIdx() {
+        return 2 + GrDrawState::kMaxTexCoords;
+    }
+    static int EdgeAttributeIdx() { return 3 + GrDrawState::kMaxTexCoords; }
+
+    static int ViewMatrixAttributeIdx() {
+        return 4 + GrDrawState::kMaxTexCoords;
+    }
+    static int TextureMatrixAttributeIdx(int stage) {
+        return 7 + GrDrawState::kMaxTexCoords + 3 * stage;
+    }
+
+public:
+
+    // Parameters that affect code generation
+    // These structs should be kept compact; they are the input to an
+    // expensive hash key generator.
+    struct ProgramDesc {
+        ProgramDesc() {
+            // since we use this as part of a key we can't have any unitialized
+            // padding
+            memset(this, 0, sizeof(ProgramDesc));
+        }
+
+        enum OutputPM {
+            // PM-color OR color with no alpha channel
+            kYes_OutputPM,
+            // nonPM-color with alpha channel
+            kNo_OutputPM,
+
+            kOutputPMCnt
+        };
+
+        struct StageDesc {
+            enum OptFlagBits {
+                kNoPerspective_OptFlagBit       = 1 << 0,
+                kIdentityMatrix_OptFlagBit      = 1 << 1,
+                kCustomTextureDomain_OptFlagBit = 1 << 2,
+                kIsEnabled_OptFlagBit           = 1 << 7
+            };
+            enum FetchMode {
+                kSingle_FetchMode,
+                k2x2_FetchMode,
+                kConvolution_FetchMode,
+
+                kFetchModeCnt,
+            };
+            /**
+              Flags set based on a src texture's pixel config. The operations
+              described are performed after reading a texel.
+             */
+            enum InConfigFlags {
+                kNone_InConfigFlag              = 0x0,
+
+                /**
+                  Swap the R and B channels. This is incompatible with
+                  kSmearAlpha. It is prefereable to perform the swizzle outside
+                  the shader using GL_ARB_texture_swizzle if possible rather
+                  than setting this flag.
+                 */
+                kSwapRAndB_InConfigFlag         = 0x1,
+
+                /**
+                 Smear alpha across all four channels. This is incompatible with
+                 kSwapRAndB and kPremul.  It is prefereable to perform the
+                 smear outside the shader using GL_ARB_texture_swizzle if
+                 possible rather than setting this flag.
+                */
+                kSmearAlpha_InConfigFlag        = 0x2,
+
+                /**
+                 Multiply r,g,b by a after texture reads. This flag incompatible
+                 with kSmearAlpha and may only be used with FetchMode kSingle.
+                 */
+                kMulRGBByAlpha_InConfigFlag     =  0x4,
+
+                kDummyInConfigFlag,
+                kInConfigBitMask = (kDummyInConfigFlag-1) |
+                                   (kDummyInConfigFlag-2)
+            };
+            enum CoordMapping {
+                kIdentity_CoordMapping,
+                kRadialGradient_CoordMapping,
+                kSweepGradient_CoordMapping,
+                kRadial2Gradient_CoordMapping,
+                // need different shader computation when quadratic
+                // eq describing the gradient degenerates to a linear eq.
+                kRadial2GradientDegenerate_CoordMapping,
+                kCoordMappingCnt
+            };
+
+            uint8_t fOptFlags;
+            uint8_t fInConfigFlags; // bitfield of InConfigFlags values
+            uint8_t fFetchMode;     // casts to enum FetchMode
+            uint8_t fCoordMapping;  // casts to enum CoordMapping
+            uint8_t fKernelWidth;
+
+            GR_STATIC_ASSERT((InConfigFlags)(uint8_t)kInConfigBitMask ==
+                             kInConfigBitMask);
+
+            inline bool isEnabled() const {
+                return SkToBool(fOptFlags & kIsEnabled_OptFlagBit);
+            }
+            inline void setEnabled(bool newValue) {
+                if (newValue) {
+                    fOptFlags |= kIsEnabled_OptFlagBit;
+                } else {
+                    fOptFlags &= ~kIsEnabled_OptFlagBit;
+                }
+            }
+        };
+
+        // Specifies where the intitial color comes from before the stages are
+        // applied.
+        enum ColorInput {
+            kSolidWhite_ColorInput,
+            kTransBlack_ColorInput,
+            kAttribute_ColorInput,
+            kUniform_ColorInput,
+
+            kColorInputCnt
+        };
+        // Dual-src blending makes use of a secondary output color that can be
+        // used as a per-pixel blend coeffecient. This controls whether a
+        // secondary source is output and what value it holds.
+        enum DualSrcOutput {
+            kNone_DualSrcOutput,
+            kCoverage_DualSrcOutput,
+            kCoverageISA_DualSrcOutput,
+            kCoverageISC_DualSrcOutput,
+
+            kDualSrcOutputCnt
+        };
+
+        GrDrawState::VertexEdgeType fVertexEdgeType;
+
+        // stripped of bits that don't affect prog generation
+        GrVertexLayout fVertexLayout;
+
+        StageDesc fStages[GrDrawState::kNumStages];
+
+        // To enable experimental geometry shader code (not for use in
+        // production)
+#if GR_GL_EXPERIMENTAL_GS
+        bool fExperimentalGS;
+#endif
+
+        uint8_t fColorInput;        // casts to enum ColorInput
+        uint8_t fCoverageInput;     // casts to enum CoverageInput
+        uint8_t fOutputPM;          // cases to enum OutputPM
+        uint8_t fDualSrcOutput;     // casts to enum DualSrcOutput
+        int8_t fFirstCoverageStage;
+        SkBool8 fEmitsPointSize;
+        SkBool8 fEdgeAAConcave;
+        SkBool8 fColorMatrixEnabled;
+
+        int8_t fEdgeAANumEdges;
+        uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
+        int8_t fPadding[3];
+
+    } fProgramDesc;
+    GR_STATIC_ASSERT(!(sizeof(ProgramDesc) % 4));
+
+    // for code readability
+    typedef ProgramDesc::StageDesc StageDesc;
+
+private:
+
+    const ProgramDesc& getDesc() { return fProgramDesc; }
+    const char* adjustInColor(const GrStringBuilder& inColor) const;
+
+public:
+    enum {
+        kUnusedUniform = -1,
+        kSetAsAttribute = 1000,
+    };
+
+    struct StageUniLocations {
+        GrGLint fTextureMatrixUni;
+        GrGLint fNormalizedTexelSizeUni;
+        GrGLint fSamplerUni;
+        GrGLint fRadial2Uni;
+        GrGLint fTexDomUni;
+        GrGLint fKernelUni;
+        GrGLint fImageIncrementUni;
+        void reset() {
+            fTextureMatrixUni = kUnusedUniform;
+            fNormalizedTexelSizeUni = kUnusedUniform;
+            fSamplerUni = kUnusedUniform;
+            fRadial2Uni = kUnusedUniform;
+            fTexDomUni = kUnusedUniform;
+            fKernelUni = kUnusedUniform;
+            fImageIncrementUni = kUnusedUniform;
+        }
+    };
+
+    struct UniLocations {
+        GrGLint fViewMatrixUni;
+        GrGLint fColorUni;
+        GrGLint fCoverageUni;
+        GrGLint fEdgesUni;
+        GrGLint fColorFilterUni;
+        GrGLint fColorMatrixUni;
+        GrGLint fColorMatrixVecUni;
+        StageUniLocations fStages[GrDrawState::kNumStages];
+        void reset() {
+            fViewMatrixUni = kUnusedUniform;
+            fColorUni = kUnusedUniform;
+            fCoverageUni = kUnusedUniform;
+            fEdgesUni = kUnusedUniform;
+            fColorFilterUni = kUnusedUniform;
+            fColorMatrixUni = kUnusedUniform;
+            fColorMatrixVecUni = kUnusedUniform;
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                fStages[s].reset();
+            }
+        }
+    };
+
+    class CachedData : public ::GrNoncopyable {
+    public:
+        CachedData() {
+        }
+
+        ~CachedData() {
+        }
+
+        void copyAndTakeOwnership(CachedData& other) {
+            memcpy(this, &other, sizeof(*this));
+        }
+
+    public:
+
+        // IDs
+        GrGLuint    fVShaderID;
+        GrGLuint    fGShaderID;
+        GrGLuint    fFShaderID;
+        GrGLuint    fProgramID;
+        // shader uniform locations (-1 if shader doesn't use them)
+        UniLocations fUniLocations;
+
+        GrMatrix  fViewMatrix;
+
+        // these reflect the current values of uniforms
+        // (GL uniform values travel with program)
+        GrColor                     fColor;
+        GrColor                     fCoverage;
+        GrColor                     fColorFilterColor;
+        GrMatrix                    fTextureMatrices[GrDrawState::kNumStages];
+        // width and height used for normalized texel size
+        int                         fTextureWidth[GrDrawState::kNumStages];
+        int                         fTextureHeight[GrDrawState::kNumStages]; 
+        GrScalar                    fRadial2CenterX1[GrDrawState::kNumStages];
+        GrScalar                    fRadial2Radius0[GrDrawState::kNumStages];
+        bool                        fRadial2PosRoot[GrDrawState::kNumStages];
+        GrRect                      fTextureDomain[GrDrawState::kNumStages];
+
+    private:
+        enum Constants {
+            kUniLocationPreAllocSize = 8
+        };
+
+    }; // CachedData
+
+    enum Constants {
+        kProgramKeySize = sizeof(ProgramDesc)
+    };
+
+    // Provide an opaque ProgramDesc
+    const uint32_t* keyData() const{
+        return reinterpret_cast<const uint32_t*>(&fProgramDesc);
+    }
+
+private:
+
+    // Determines which uniforms will need to be bound.
+    void genStageCode(const GrGLInterface* gl,
+                      int stageNum,
+                      const ProgramDesc::StageDesc& desc,
+                      const char* fsInColor, // NULL means no incoming color
+                      const char* fsOutColor,
+                      const char* vsInCoord,
+                      ShaderCodeSegments* segments,
+                      StageUniLocations* locations) const;
+
+    void genGeometryShader(const GrGLInterface* gl,
+                           GrGLSLGeneration glslVersion,
+                           ShaderCodeSegments* segments) const;
+
+    // generates code to compute coverage based on edge AA.
+    void genEdgeCoverage(const GrGLInterface* gl,
+                         GrVertexLayout layout,
+                         CachedData* programData,
+                         GrStringBuilder* coverageVar,
+                         ShaderCodeSegments* segments) const;
+
+    static bool CompileShaders(const GrGLInterface* gl,
+                               GrGLSLGeneration glslVersion,
+                               const ShaderCodeSegments& segments, 
+                               CachedData* programData);
+
+    // Compiles a GL shader, returns shader ID or 0 if failed
+    // params have same meaning as glShaderSource
+    static GrGLuint CompileShader(const GrGLInterface* gl,
+                                  GrGLenum type, int stringCnt,
+                                  const char** strings,
+                                  int* stringLengths);
+
+    // Creates a GL program ID, binds shader attributes to GL vertex attrs, and
+    // links the program
+    bool bindOutputsAttribsAndLinkProgram(
+                const GrGLInterface* gl,
+                GrStringBuilder texCoordAttrNames[GrDrawState::kMaxTexCoords],
+                bool bindColorOut,
+                bool bindDualSrcOut,
+                CachedData* programData) const;
+
+    // Binds uniforms; initializes cache to invalid values.
+    void getUniformLocationsAndInitCache(const GrGLInterface* gl,
+                                         CachedData* programData) const;
+
+    friend class GrGpuGLShaders;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
new file mode 100644
index 0000000..c98914a
--- /dev/null
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -0,0 +1,97 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLRenderTarget.h"
+
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+void GrGLRenderTarget::init(const Desc& desc,
+                            const GrGLIRect& viewport,
+                            GrGLTexID* texID) {
+    fRTFBOID                = desc.fRTFBOID;
+    fTexFBOID               = desc.fTexFBOID;
+    fMSColorRenderbufferID  = desc.fMSColorRenderbufferID;
+    fViewport               = viewport;
+    fOwnIDs                 = desc.fOwnIDs;
+    fTexIDObj               = texID;
+    GrSafeRef(fTexIDObj);
+}
+
+GrGLRenderTarget::GrGLRenderTarget(GrGpuGL* gpu,
+                                   const Desc& desc,
+                                   const GrGLIRect& viewport,
+                                   GrGLTexID* texID,
+                                   GrGLTexture* texture)
+    : INHERITED(gpu,
+                texture,
+                viewport.fWidth,
+                viewport.fHeight,
+                desc.fConfig,
+                desc.fSampleCnt) {
+    GrAssert(NULL != texID);
+    GrAssert(NULL != texture);
+    // FBO 0 can't also be a texture, right?
+    GrAssert(0 != desc.fRTFBOID);
+    GrAssert(0 != desc.fTexFBOID);
+
+    // we assume this is true, TODO: get rid of viewport as a param.
+    GrAssert(viewport.fWidth == texture->width());
+    GrAssert(viewport.fHeight == texture->height());
+
+    this->init(desc, viewport, texID);
+}
+
+GrGLRenderTarget::GrGLRenderTarget(GrGpuGL* gpu,
+                                   const Desc& desc,
+                                   const GrGLIRect& viewport)
+    : INHERITED(gpu,
+                NULL,
+                viewport.fWidth,
+                viewport.fHeight,
+                desc.fConfig,
+                desc.fSampleCnt) {
+    this->init(desc, viewport, NULL);
+}
+
+void GrGLRenderTarget::onRelease() {
+    GPUGL->notifyRenderTargetDelete(this);
+    if (fOwnIDs) {
+        if (fTexFBOID) {
+            GL_CALL(DeleteFramebuffers(1, &fTexFBOID));
+        }
+        if (fRTFBOID && fRTFBOID != fTexFBOID) {
+            GL_CALL(DeleteFramebuffers(1, &fRTFBOID));
+        }
+        if (fMSColorRenderbufferID) {
+            GL_CALL(DeleteRenderbuffers(1, &fMSColorRenderbufferID));
+        }
+    }
+    fRTFBOID                = 0;
+    fTexFBOID               = 0;
+    fMSColorRenderbufferID  = 0;
+    GrSafeUnref(fTexIDObj);
+    fTexIDObj = NULL;
+    this->setStencilBuffer(NULL);
+}
+
+void GrGLRenderTarget::onAbandon() {
+    fRTFBOID                = 0;
+    fTexFBOID               = 0;
+    fMSColorRenderbufferID  = 0;
+    if (NULL != fTexIDObj) {
+        fTexIDObj->abandon();
+        fTexIDObj = NULL;
+    }
+    this->setStencilBuffer(NULL);
+}
+
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
new file mode 100644
index 0000000..eb817df
--- /dev/null
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -0,0 +1,110 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLRenderTarget_DEFINED
+#define GrGLRenderTarget_DEFINED
+
+#include "GrGLIRect.h"
+#include "GrRenderTarget.h"
+#include "GrScalar.h"
+
+class GrGpuGL;
+class GrGLTexture;
+class GrGLTexID;
+
+class GrGLRenderTarget : public GrRenderTarget {
+
+public:
+    // set fTexFBOID to this value to indicate that it is multisampled but
+    // Gr doesn't know how to resolve it.
+    enum { kUnresolvableFBOID = 0 };
+
+    struct Desc {
+        GrGLuint      fRTFBOID;
+        GrGLuint      fTexFBOID;
+        GrGLuint      fMSColorRenderbufferID;
+        bool          fOwnIDs;
+        GrPixelConfig fConfig;
+        int           fSampleCnt;
+    };
+
+    // creates a GrGLRenderTarget associated with a texture
+    GrGLRenderTarget(GrGpuGL*          gpu,
+                     const Desc&       desc,
+                     const GrGLIRect&  viewport,
+                     GrGLTexID*        texID,
+                     GrGLTexture*      texture);
+
+    // creates an independent GrGLRenderTarget
+    GrGLRenderTarget(GrGpuGL*          gpu,
+                     const Desc&       desc,
+                     const GrGLIRect&  viewport);
+
+    virtual ~GrGLRenderTarget() { this->release(); }
+
+    void setViewport(const GrGLIRect& rect) { fViewport = rect; }
+    const GrGLIRect& getViewport() const { return fViewport; }
+
+    // The following two functions return the same ID when a 
+    // texture-rendertarget is multisampled, and different IDs when
+    // it is.
+    // FBO ID used to render into
+    GrGLuint renderFBOID() const { return fRTFBOID; }
+    // FBO ID that has texture ID attached.
+    GrGLuint textureFBOID() const { return fTexFBOID; }
+
+    // override of GrRenderTarget 
+    virtual intptr_t getRenderTargetHandle() const {
+        return this->renderFBOID(); 
+    }
+    virtual intptr_t getRenderTargetResolvedHandle() const {
+        return this->textureFBOID();
+    }
+    virtual ResolveType getResolveType() const {
+
+        if (!this->isMultisampled() ||
+            fRTFBOID == fTexFBOID) {
+            // catches FBO 0 and non MSAA case
+            return kAutoResolves_ResolveType;
+        } else if (kUnresolvableFBOID == fTexFBOID) {
+            return kCantResolve_ResolveType;
+        } else {
+            return kCanResolve_ResolveType;
+        }
+    }
+
+protected:
+    // override of GrResource
+    virtual void onAbandon();
+    virtual void onRelease();
+
+private:
+    GrGLuint      fRTFBOID;
+    GrGLuint      fTexFBOID;
+
+    GrGLuint      fMSColorRenderbufferID;
+
+    // Should this object delete IDs when it is destroyed or does someone
+    // else own them.
+    bool        fOwnIDs;
+
+    // when we switch to this rendertarget we want to set the viewport to
+    // only render to to content area (as opposed to the whole allocation) and
+    // we want the rendering to be at top left (GL has origin in bottom left)
+    GrGLIRect fViewport;
+
+    // non-NULL if this RT was created by Gr with an associated GrGLTexture.
+    GrGLTexID* fTexIDObj;
+
+    void init(const Desc& desc, const GrGLIRect& viewport, GrGLTexID* texID);
+
+    typedef GrRenderTarget INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp
new file mode 100644
index 0000000..e933ee8
--- /dev/null
+++ b/src/gpu/gl/GrGLSL.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrGLSL.h"
+#include "GrGLShaderVar.h"
+
+GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
+                                   const GrGLInterface* gl) {
+    GrGLSLVersion ver = GrGLGetGLSLVersion(gl);
+    switch (binding) {
+        case kDesktop_GrGLBinding:
+            GrAssert(ver >= GR_GLSL_VER(1,10));
+            if (ver >= GR_GLSL_VER(1,50)) {
+                return k150_GrGLSLGeneration;
+            } else if (ver >= GR_GLSL_VER(1,30)) {
+                return k130_GrGLSLGeneration;
+            } else {
+                return k110_GrGLSLGeneration;
+            }
+        case kES2_GrGLBinding:
+            // version 1.00 of ES GLSL based on ver 1.20 of desktop GLSL
+            GrAssert(ver >= GR_GL_VER(1,00));
+            return k110_GrGLSLGeneration;
+        default:
+            GrCrash("Unknown GL Binding");
+            return k110_GrGLSLGeneration; // suppress warning
+    }
+}
+
+const char* GrGetGLSLVersionDecl(GrGLBinding binding,
+                                   GrGLSLGeneration gen) {
+    switch (gen) {
+        case k110_GrGLSLGeneration:
+            if (kES2_GrGLBinding == binding) {
+                // ES2s shader language is based on version 1.20 but is version
+                // 1.00 of the ES language.
+                return "#version 100\n";
+            } else {
+                GrAssert(kDesktop_GrGLBinding == binding);
+                return "#version 110\n";
+            }
+        case k130_GrGLSLGeneration:
+            GrAssert(kDesktop_GrGLBinding == binding);
+            return "#version 130\n";
+        case k150_GrGLSLGeneration:
+            GrAssert(kDesktop_GrGLBinding == binding);
+            return "#version 150\n";
+        default:
+            GrCrash("Unknown GL version.");
+            return ""; // suppress warning
+    }
+}
+
+const char* GrGetGLSLVarPrecisionDeclType(GrGLBinding binding) {
+    if (kES2_GrGLBinding == binding) {
+        return "mediump";
+    } else {
+        return " ";
+    }
+}
+
+const char* GrGetGLSLShaderPrecisionDecl(GrGLBinding binding) {
+    if (kES2_GrGLBinding == binding) {
+        return "precision mediump float;\n";
+    } else {
+        return "";
+    }
+}
+
+bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
+                             const char* nameIfDeclared,
+                             GrGLShaderVar* var) {
+    bool declaredOutput = k110_GrGLSLGeneration != gen;
+    var->set(GrGLShaderVar::kVec4f_Type,
+             GrGLShaderVar::kOut_TypeModifier,
+             declaredOutput ? nameIfDeclared : "gl_FragColor");
+    return declaredOutput;
+}
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
new file mode 100644
index 0000000..5b9c5b6
--- /dev/null
+++ b/src/gpu/gl/GrGLSL.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLSL_DEFINED
+#define GrGLSL_DEFINED
+
+#include "GrGLInterface.h"
+
+class GrGLShaderVar;
+
+// Limited set of GLSL versions we build shaders for. Caller should round
+// down the GLSL version to one of these enums.
+enum GrGLSLGeneration {
+    /**
+     * Desktop GLSL 1.10 and ES2 shading lang (based on desktop GLSL 1.20)
+     */
+    k110_GrGLSLGeneration,
+    /**
+     * Desktop GLSL 1.30
+     */
+    k130_GrGLSLGeneration,
+    /**
+     * Dekstop GLSL 1.50
+     */
+    k150_GrGLSLGeneration,
+};
+
+/**
+ * Gets the most recent GLSL Generation compatible with the OpenGL context.
+ */
+GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
+                                     const GrGLInterface* gl);
+
+/**
+ * Returns a string to include at the begining of a shader to declare the GLSL
+ * version.
+ */
+const char* GrGetGLSLVersionDecl(GrGLBinding binding,
+                                 GrGLSLGeneration v);
+
+/**
+ * Returns a string to include in a variable decleration to set the fp precision
+ * or an emptry string if precision is not required.
+ */
+const char* GrGetGLSLVarPrecisionDeclType(GrGLBinding binding);
+
+/**
+ * Returns a string to set the default fp precision for an entire shader, or
+ * an emptry string if precision is not required.
+ */
+const char* GrGetGLSLShaderPrecisionDecl(GrGLBinding binding);
+
+/**
+ * Depending on the GLSL version being emitted there may be an assumed output
+ * variable from the fragment shader for the color. Otherwise, the shader must
+ * declare an output variable for the color. If this function returns true:
+ *    * Parameter var's name will be set to nameIfDeclared
+ *    * The variable must be declared in the fragment shader
+ *    * The variable has to be bound as the color output 
+ *      (using glBindFragDataLocation)
+ *    If the function returns false:
+ *    * Parameter var's name will be set to the GLSL built-in color output name.
+ *    * Do not declare the variable in the shader.
+ *    * Do not use glBindFragDataLocation to bind the variable
+ * In either case var is initialized to represent the color output in the
+ * shader.
+ */
+ bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
+                             const char* nameIfDeclared,
+                             GrGLShaderVar* var);
+
+#endif
diff --git a/src/gpu/gl/GrGLShaderVar.h b/src/gpu/gl/GrGLShaderVar.h
new file mode 100644
index 0000000..95c54f3
--- /dev/null
+++ b/src/gpu/gl/GrGLShaderVar.h
@@ -0,0 +1,304 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrGLShaderVar_DEFINED
+#define GrGLShaderVar_DEFINED
+
+#include "GrGLInterface.h"
+#include "GrGLSL.h"
+#include "../GrStringBuilder.h"
+
+#define USE_UNIFORM_FLOAT_ARRAYS true
+
+/**
+ * Represents a variable in a shader
+ */
+class GrGLShaderVar {
+public:
+
+    enum Type {
+        kFloat_Type,
+        kVec2f_Type,
+        kVec3f_Type,
+        kVec4f_Type,
+        kMat33f_Type,
+        kMat44f_Type,
+        kSampler2D_Type,
+    };
+
+    /**
+     * Early versions of GLSL have Varying and Attribute; those are later
+     * deprecated, but we still need to know whether a Varying variable
+     * should be treated as In or Out.
+     */
+    enum TypeModifier {
+        kNone_TypeModifier,
+        kOut_TypeModifier,
+        kIn_TypeModifier,
+        kUniform_TypeModifier,
+        kAttribute_TypeModifier
+    };
+
+    /**
+     * Defaults to a float with no precision specifier
+     */
+    GrGLShaderVar() {
+        fType = kFloat_Type;
+        fTypeModifier = kNone_TypeModifier;
+        fCount = kNonArray;
+        fEmitPrecision = false;
+        fUseUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS;
+    }
+
+    GrGLShaderVar(const GrGLShaderVar& var)
+        : fType(var.fType)
+        , fTypeModifier(var.fTypeModifier)
+        , fName(var.fName)
+        , fCount(var.fCount)
+        , fEmitPrecision(var.fEmitPrecision)
+        , fUseUniformFloatArrays(var.fUseUniformFloatArrays) {}
+
+    /**
+     * Values for array count that have special meaning. We allow 1-sized arrays.
+     */
+    enum {
+        kNonArray     =  0, // not an array
+        kUnsizedArray = -1, // an unsized array (declared with [])
+    };
+
+    /**
+     * Sets as a non-array.
+     */
+    void set(Type type,
+             TypeModifier typeModifier,
+             const GrStringBuilder& name,
+             bool emitPrecision = false,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = kNonArray;
+        fEmitPrecision = emitPrecision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Sets as a non-array.
+     */
+    void set(Type type,
+             TypeModifier typeModifier,
+             const char* name,
+             bool specifyPrecision = false,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = kNonArray;
+        fEmitPrecision = specifyPrecision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Set all var options
+     */
+    void set(Type type,
+             TypeModifier typeModifier,
+             const GrStringBuilder& name,
+             int count,
+             bool specifyPrecision = false,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = count;
+        fEmitPrecision = specifyPrecision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Set all var options
+     */
+    void set(Type type,
+             TypeModifier typeModifier,
+             const char* name,
+             int count,
+             bool specifyPrecision = false,
+             bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        fType = type;
+        fTypeModifier = typeModifier;
+        fName = name;
+        fCount = count;
+        fEmitPrecision = specifyPrecision;
+        fUseUniformFloatArrays = useUniformFloatArrays;
+    }
+
+    /**
+     * Is the var an array.
+     */
+    bool isArray() const { return kNonArray != fCount; }
+    /**
+     * Is this an unsized array, (i.e. declared with []).
+     */
+    bool isUnsizedArray() const { return kUnsizedArray == fCount; }
+    /**
+     * Get the array length of the var.
+     */
+    int getArrayCount() const { return fCount; }
+    /**
+     * Set the array length of the var
+     */
+    void setArrayCount(int count) { fCount = count; }
+    /**
+     * Set to be a non-array.
+     */
+    void setNonArray() { fCount = kNonArray; }
+    /**
+     * Set to be an unsized array.
+     */
+    void setUnsizedArray() { fCount = kUnsizedArray; }
+
+    /**
+     * Access the var name as a writable string
+     */
+    GrStringBuilder* accessName() { return &fName; }
+    /**
+     * Set the var name
+     */
+    void setName(const GrStringBuilder& n) { fName = n; }
+    void setName(const char* n) { fName = n; }
+    /**
+     * Get the var name.
+     */
+    const GrStringBuilder& getName() const { return fName; }
+
+    /**
+     * Get the type of the var
+     */
+    Type getType() const { return fType; }
+    /**
+     * Set the type of the var
+     */
+    void setType(Type type) { fType = type; }
+
+    TypeModifier getTypeModifier() const { return fTypeModifier; }
+    void setTypeModifier(TypeModifier type) { fTypeModifier = type; }
+
+    /**
+     * Must the variable declaration emit a precision specifier
+     */
+    bool emitsPrecision() const { return fEmitPrecision; }
+    /**
+     * Specify whether the declaration should specify precision
+     */
+    void setEmitPrecision(bool p) { fEmitPrecision = p; }
+
+    /**
+     * Write a declaration of this variable to out.
+     */
+    void appendDecl(const GrGLInterface* gl, GrStringBuilder* out,
+                    GrGLSLGeneration gen) const {
+        if (this->getTypeModifier() != kNone_TypeModifier) {
+           out->append(TypeModifierString(this->getTypeModifier(), gen));
+           out->append(" ");
+        }
+        if (this->emitsPrecision()) {
+            out->append(PrecisionString(gl));
+            out->append(" ");
+        }
+        Type effectiveType = this->getType();
+        if (this->isArray()) {
+            if (this->isUnsizedArray()) {
+                out->appendf("%s %s[]", 
+                             TypeString(effectiveType), 
+                             this->getName().c_str());
+            } else {
+                GrAssert(this->getArrayCount() > 0);
+                out->appendf("%s %s[%d]", 
+                             TypeString(effectiveType),
+                             this->getName().c_str(),
+                             this->getArrayCount());
+            }
+        } else {
+            out->appendf("%s %s",
+                         TypeString(effectiveType),
+                         this->getName().c_str());
+        }
+        out->append(";\n");
+    }
+
+    static const char* TypeString(Type t) {
+        switch (t) {
+            case kFloat_Type:
+                return "float";
+            case kVec2f_Type:
+                return "vec2";
+            case kVec3f_Type:
+                return "vec3";
+            case kVec4f_Type:
+                return "vec4";
+            case kMat33f_Type:
+                return "mat3";
+            case kMat44f_Type:
+                return "mat4";
+            case kSampler2D_Type:
+                return "sampler2D";
+            default:
+                GrCrash("Unknown shader var type.");
+                return ""; // suppress warning
+        }
+    }
+
+    void appendArrayAccess(int index, GrStringBuilder* out) {
+        out->appendf("%s[%d]%s",
+                     this->getName().c_str(),
+                     index,
+                     fUseUniformFloatArrays ? "" : ".x");
+    }
+
+    void appendArrayAccess(const char* indexName, GrStringBuilder* out) {
+        out->appendf("%s[%s]%s",
+                     this->getName().c_str(),
+                     indexName,
+                     fUseUniformFloatArrays ? "" : ".x");
+    }
+
+private:
+    static const char* PrecisionString(const GrGLInterface* gl) {
+        return gl->supportsDesktop() ? "" : "mediump";
+    }
+
+    static const char* TypeModifierString(TypeModifier t,
+                                          GrGLSLGeneration gen) {
+        switch (t) {
+            case kNone_TypeModifier:
+                return "";
+            case kOut_TypeModifier:
+                return k110_GrGLSLGeneration == gen ? "varying" : "out";
+            case kIn_TypeModifier:
+                return k110_GrGLSLGeneration == gen ? "varying" : "in";
+            case kUniform_TypeModifier:
+                return "uniform";
+            case kAttribute_TypeModifier:
+                return k110_GrGLSLGeneration == gen ? "attribute" : "in";
+            default:
+                GrCrash("Unknown shader variable type modifier.");
+                return ""; // suppress warning
+        }
+    }
+
+    Type            fType;
+    TypeModifier    fTypeModifier;
+    GrStringBuilder fName;
+    int             fCount;
+    bool            fEmitPrecision;
+    /// Work around driver bugs on some hardware that don't correctly
+    /// support uniform float []
+    bool            fUseUniformFloatArrays;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLStencilBuffer.cpp b/src/gpu/gl/GrGLStencilBuffer.cpp
new file mode 100644
index 0000000..bc0bb36
--- /dev/null
+++ b/src/gpu/gl/GrGLStencilBuffer.cpp
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLStencilBuffer.h"
+#include "GrGpuGL.h"
+
+GrGLStencilBuffer::~GrGLStencilBuffer() {
+    this->release();
+}
+
+size_t GrGLStencilBuffer::sizeInBytes() const {
+    uint64_t size = this->width();
+    size *= this->height();
+    size *= fFormat.fTotalBits;
+    size *= GrMax(1,this->numSamples());
+    return static_cast<size_t>(size / 8);
+}
+
+void GrGLStencilBuffer::onRelease() {
+    if (0 != fRenderbufferID) {
+        GrGpuGL* gpuGL = (GrGpuGL*) this->getGpu();
+        const GrGLInterface* gl = gpuGL->glInterface();
+        GR_GL_CALL(gl, DeleteRenderbuffers(1, &fRenderbufferID));
+        fRenderbufferID = 0;
+    }
+    INHERITED::onRelease();
+}
+
+void GrGLStencilBuffer::onAbandon() {
+    fRenderbufferID = 0;
+    INHERITED::onAbandon();
+}
+
+
diff --git a/src/gpu/gl/GrGLStencilBuffer.h b/src/gpu/gl/GrGLStencilBuffer.h
new file mode 100644
index 0000000..02501e7
--- /dev/null
+++ b/src/gpu/gl/GrGLStencilBuffer.h
@@ -0,0 +1,60 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLStencilBuffer_DEFINED
+#define GrGLStencilBuffer_DEFINED
+
+#include "GrGLInterface.h"
+#include "../GrStencilBuffer.h"
+
+class GrGLStencilBuffer : public GrStencilBuffer {
+public:
+    static const GrGLenum kUnknownInternalFormat = ~0;
+    struct Format {
+        GrGLenum  fInternalFormat;
+        GrGLuint  fStencilBits;
+        GrGLuint  fTotalBits;
+        bool      fPacked;
+    };
+
+    GrGLStencilBuffer(GrGpu* gpu, GrGLint rbid, 
+                      int width, int height,
+                      int sampleCnt,
+                      const Format& format) 
+        : GrStencilBuffer(gpu, width, height, format.fStencilBits, sampleCnt)
+        , fFormat(format)
+        , fRenderbufferID(rbid) {
+    }
+
+    virtual ~GrGLStencilBuffer();
+
+    virtual size_t sizeInBytes() const;
+
+    GrGLuint renderbufferID() const {
+        return fRenderbufferID;
+    }
+
+    const Format& format() const { return fFormat; }
+
+protected:
+    virtual void onRelease();
+
+    virtual void onAbandon();
+
+private:
+    Format fFormat;
+    // may be zero for external SBs associated with external RTs
+    // (we don't require the client to give us the id, just tell
+    // us how many bits of stencil there are).
+    GrGLuint fRenderbufferID;
+
+    typedef GrStencilBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
new file mode 100644
index 0000000..0a38da3
--- /dev/null
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -0,0 +1,90 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLTexture.h"
+
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+const GrGLenum* GrGLTexture::WrapMode2GLWrap() {
+    static const GrGLenum repeatModes[] = {
+        GR_GL_CLAMP_TO_EDGE,
+        GR_GL_REPEAT,
+        GR_GL_MIRRORED_REPEAT
+    };
+    return repeatModes;
+};
+
+void GrGLTexture::init(GrGpuGL* gpu,
+                       const Desc& textureDesc,
+                       const GrGLRenderTarget::Desc* rtDesc) {
+
+    GrAssert(0 != textureDesc.fTextureID);
+
+    fTexParams.invalidate();
+    fTexParamsTimestamp = GrGpu::kExpiredTimestamp;
+    fTexIDObj           = new GrGLTexID(GPUGL->glInterface(),
+                                        textureDesc.fTextureID,
+                                        textureDesc.fOwnsID);
+    fOrientation        = textureDesc.fOrientation;
+
+    if (NULL != rtDesc) {
+        // we render to the top left
+        GrGLIRect vp;
+        vp.fLeft   = 0;
+        vp.fWidth  = textureDesc.fWidth;
+        vp.fBottom = 0;
+        vp.fHeight = textureDesc.fHeight;
+
+        fRenderTarget = new GrGLRenderTarget(gpu, *rtDesc, vp, fTexIDObj, this);
+    }
+}
+
+GrGLTexture::GrGLTexture(GrGpuGL* gpu,
+                         const Desc& textureDesc) 
+    : INHERITED(gpu,
+                textureDesc.fWidth,
+                textureDesc.fHeight,
+                textureDesc.fConfig) {
+    this->init(gpu, textureDesc, NULL);
+}
+
+GrGLTexture::GrGLTexture(GrGpuGL* gpu,
+                         const Desc& textureDesc,
+                         const GrGLRenderTarget::Desc& rtDesc)
+    : INHERITED(gpu,
+                textureDesc.fWidth,
+                textureDesc.fHeight,
+                textureDesc.fConfig) {
+    this->init(gpu, textureDesc, &rtDesc);
+}
+
+void GrGLTexture::onRelease() {
+    INHERITED::onRelease();
+    GPUGL->notifyTextureDelete(this);
+    if (NULL != fTexIDObj) {
+        fTexIDObj->unref();
+        fTexIDObj = NULL;
+    }
+}
+
+void GrGLTexture::onAbandon() {
+    INHERITED::onAbandon();
+    if (NULL != fTexIDObj) {
+        fTexIDObj->abandon();
+    }
+}
+
+intptr_t GrGLTexture::getTextureHandle() const {
+    return fTexIDObj->id();
+}
+
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
new file mode 100644
index 0000000..d13fc44
--- /dev/null
+++ b/src/gpu/gl/GrGLTexture.h
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrGLTexture_DEFINED
+#define GrGLTexture_DEFINED
+
+#include "../GrGpu.h"
+#include "GrGLRenderTarget.h"
+
+/**
+ * A ref counted tex id that deletes the texture in its destructor.
+ */
+class GrGLTexID : public GrRefCnt {
+
+public:
+    GrGLTexID(const GrGLInterface* gl, GrGLuint texID, bool ownsID)
+        : fGL(gl)
+        , fTexID(texID)
+        , fOwnsID(ownsID) {
+    }
+
+    virtual ~GrGLTexID() {
+        if (0 != fTexID && fOwnsID) {
+            GR_GL_CALL(fGL, DeleteTextures(1, &fTexID));
+        }
+    }
+
+    void abandon() { fTexID = 0; }
+    GrGLuint id() const { return fTexID; }
+
+private:
+    const GrGLInterface* fGL;
+    GrGLuint             fTexID;
+    bool                 fOwnsID;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+class GrGLTexture : public GrTexture {
+
+public:
+    enum Orientation {
+        kBottomUp_Orientation,
+        kTopDown_Orientation,
+    };
+
+    struct TexParams {
+        GrGLenum fFilter;
+        GrGLenum fWrapS;
+        GrGLenum fWrapT;
+        GrGLenum fSwizzleRGBA[4];
+        void invalidate() { memset(this, 0xff, sizeof(TexParams)); }
+    };
+
+    struct Desc {
+        int             fWidth;
+        int             fHeight;
+        GrPixelConfig   fConfig;
+        GrGLuint        fTextureID;
+        bool            fOwnsID;
+        Orientation     fOrientation;
+    };
+
+    // creates a texture that is also an RT
+    GrGLTexture(GrGpuGL* gpu,
+                const Desc& textureDesc,
+                const GrGLRenderTarget::Desc& rtDesc);
+
+    // creates a non-RT texture
+    GrGLTexture(GrGpuGL* gpu,
+                const Desc& textureDesc);
+
+
+    virtual ~GrGLTexture() { this->release(); }
+
+    virtual intptr_t getTextureHandle() const;
+
+    // these functions 
+    const TexParams& getCachedTexParams(GrGpu::ResetTimestamp* timestamp) const {
+        *timestamp = fTexParamsTimestamp;
+        return fTexParams;
+    }
+    void setCachedTexParams(const TexParams& texParams,
+                            GrGpu::ResetTimestamp timestamp) {
+        fTexParams = texParams;
+        fTexParamsTimestamp = timestamp;
+    }
+    GrGLuint textureID() const { return fTexIDObj->id(); }
+
+    // Ganesh assumes texture coordinates have their origin
+    // in the top-left corner of the image. OpenGL, however,
+    // has the origin in the lower-left corner. For content that
+    // is loaded by Ganesh we just push the content "upside down"
+    // (by GL's understanding of the world) in glTex*Image and the
+    // addressing just works out. However, content generated by GL
+    // (FBO or externally imported texture) will be updside down
+    // and it is up to the GrGpuGL derivative to handle y-mirroing.
+    Orientation orientation() const { return fOrientation; }
+
+    static const GrGLenum* WrapMode2GLWrap();
+
+protected:
+
+    // overrides of GrTexture
+    virtual void onAbandon();
+    virtual void onRelease();
+
+private:
+    TexParams                       fTexParams;
+    GrGpu::ResetTimestamp           fTexParamsTimestamp;
+    GrGLTexID*                      fTexIDObj;
+    Orientation                     fOrientation;
+
+    void init(GrGpuGL* gpu,
+              const Desc& textureDesc,
+              const GrGLRenderTarget::Desc* rtDesc);
+
+    typedef GrTexture INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLUtil.cpp b/src/gpu/gl/GrGLUtil.cpp
new file mode 100644
index 0000000..f12b407
--- /dev/null
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGLConfig.h"
+#include "GrGLInterface.h"
+
+void GrGLClearErr(const GrGLInterface* gl) {
+    while (GR_GL_NO_ERROR != gl->fGetError()) {}
+}
+
+void GrGLCheckErr(const GrGLInterface* gl,
+                  const char* location,
+                  const char* call) {
+    uint32_t err = GR_GL_GET_ERROR(gl);
+    if (GR_GL_NO_ERROR != err) {
+        GrPrintf("---- glGetError %x", err);
+        if (NULL != location) {
+            GrPrintf(" at\n\t%s", location);
+        }
+        if (NULL != call) {
+            GrPrintf("\n\t\t%s", call);
+        }
+        GrPrintf("\n");
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GR_GL_LOG_CALLS
+    bool gLogCallsGL = !!(GR_GL_LOG_CALLS_START);
+#endif
+
+#if GR_GL_CHECK_ERROR
+    bool gCheckErrorGL = !!(GR_GL_CHECK_ERROR_START);
+#endif
+
diff --git a/src/gpu/gl/GrGLVertexBuffer.cpp b/src/gpu/gl/GrGLVertexBuffer.cpp
new file mode 100644
index 0000000..33c1e7e
--- /dev/null
+++ b/src/gpu/gl/GrGLVertexBuffer.cpp
@@ -0,0 +1,126 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#include "GrGLVertexBuffer.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+
+GrGLVertexBuffer::GrGLVertexBuffer(GrGpuGL* gpu,
+                                   GrGLuint id,
+                                   size_t sizeInBytes,
+                                   bool dynamic)
+    : INHERITED(gpu, sizeInBytes, dynamic)
+    , fBufferID(id)
+    , fLockPtr(NULL) {
+}
+
+void GrGLVertexBuffer::onRelease() {
+    // make sure we've not been abandoned
+    if (fBufferID) {
+        GPUGL->notifyVertexBufferDelete(this);
+        GL_CALL(DeleteBuffers(1, &fBufferID));
+        fBufferID = 0;
+    }
+}
+
+void GrGLVertexBuffer::onAbandon() {
+    fBufferID = 0;
+    fLockPtr = NULL;
+}
+
+void GrGLVertexBuffer::bind() const {
+    GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, fBufferID));
+    GPUGL->notifyVertexBufferBind(this);
+}
+
+GrGLuint GrGLVertexBuffer::bufferID() const {
+    return fBufferID;
+}
+
+void* GrGLVertexBuffer::lock() {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    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,
+                           this->dynamic() ? GR_GL_DYNAMIC_DRAW :
+                                             GR_GL_STATIC_DRAW));
+        GR_GL_CALL_RET(GPUGL->glInterface(),
+                       fLockPtr,
+                       MapBuffer(GR_GL_ARRAY_BUFFER, GR_GL_WRITE_ONLY));
+        return fLockPtr;
+    }
+    return NULL;
+}
+
+void* GrGLVertexBuffer::lockPtr() const {
+    return fLockPtr;
+}
+
+void GrGLVertexBuffer::unlock() {
+
+    GrAssert(fBufferID);
+    GrAssert(isLocked());
+    GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
+
+    this->bind();
+    GL_CALL(UnmapBuffer(GR_GL_ARRAY_BUFFER));
+    fLockPtr = NULL;
+}
+
+bool GrGLVertexBuffer::isLocked() const {
+    GrAssert(!this->isValid() || fBufferID);
+#if GR_DEBUG
+    if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
+        GrGLint mapped;
+        this->bind();
+        GL_CALL(GetBufferParameteriv(GR_GL_ARRAY_BUFFER, 
+                                     GR_GL_BUFFER_MAPPED, &mapped));
+        GrAssert(!!mapped == !!fLockPtr);
+    }
+#endif
+    return NULL != fLockPtr;
+}
+
+bool GrGLVertexBuffer::updateData(const void* src, size_t srcSizeInBytes) {
+    GrAssert(fBufferID);
+    GrAssert(!isLocked());
+    if (srcSizeInBytes > this->sizeInBytes()) {
+        return false;
+    }
+    this->bind();
+    GrGLenum usage = dynamic() ? GR_GL_DYNAMIC_DRAW : GR_GL_STATIC_DRAW;
+#if !GR_GL_USE_BUFFER_DATA_NULL_HINT
+    // Note that we're cheating on the size here. Currently no methods
+    // allow a partial update that preserves contents of non-updated
+    // portions of the buffer (and lock() does a glBufferData(..size, NULL..))
+    GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+#else
+    if (this->sizeInBytes() == srcSizeInBytes) {
+        GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, srcSizeInBytes, src, usage));
+    } else {
+        // Before we call glBufferSubData we give the driver a hint using
+        // glBufferData with NULL. This makes the old buffer contents
+        // inaccessible to future draws. The GPU may still be processing draws
+        // that reference the old contents. With this hint it can assign a
+        // different allocation for the new contents to avoid flushing the gpu
+        // past draws consuming the old contents.
+        GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, 
+                           this->sizeInBytes(), NULL, usage));
+        GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src));
+    }
+#endif
+    return true;
+}
+
diff --git a/src/gpu/gl/GrGLVertexBuffer.h b/src/gpu/gl/GrGLVertexBuffer.h
new file mode 100644
index 0000000..ff03d7a
--- /dev/null
+++ b/src/gpu/gl/GrGLVertexBuffer.h
@@ -0,0 +1,52 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGLVertexBuffer_DEFINED
+#define GrGLVertexBuffer_DEFINED
+
+#include "../GrVertexBuffer.h"
+#include "GrGLInterface.h"
+
+class GrGpuGL;
+
+class GrGLVertexBuffer : public GrVertexBuffer {
+
+public:
+    virtual ~GrGLVertexBuffer() { this->release(); }
+    // overrides of GrVertexBuffer
+    virtual void* lock();
+    virtual void* lockPtr() const;
+    virtual void unlock();
+    virtual bool isLocked() const;
+    virtual bool updateData(const void* src, size_t srcSizeInBytes);
+    GrGLuint bufferID() const;
+
+protected:
+    GrGLVertexBuffer(GrGpuGL* gpu,
+                     GrGLuint id,
+                     size_t sizeInBytes,
+                     bool dynamic);
+
+    // overrides of GrResource
+    virtual void onAbandon();
+    virtual void onRelease();
+
+private:
+    void bind() const;
+
+    GrGLuint     fBufferID;
+    void*        fLockPtr;
+
+    friend class GrGpuGL;
+
+    typedef GrVertexBuffer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
new file mode 100644
index 0000000..6ce6089
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -0,0 +1,2544 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "GrGpuGL.h"
+#include "GrGLStencilBuffer.h"
+#include "GrTypes.h"
+#include "SkTemplates.h"
+
+static const GrGLuint GR_MAX_GLUINT = ~0;
+static const GrGLint  GR_INVAL_GLINT = ~0;
+
+#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
+#define GL_CALL_RET(RET, X) GR_GL_CALL_RET(this->glInterface(), RET, X)
+
+// we use a spare texture unit to avoid
+// mucking with the state of any of the stages.
+static const int SPARE_TEX_UNIT = GrDrawState::kNumStages;
+
+#define SKIP_CACHE_CHECK    true
+
+#if GR_GL_CHECK_ALLOC_WITH_GET_ERROR
+    #define CLEAR_ERROR_BEFORE_ALLOC(iface)   GrGLClearErr(iface)
+    #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL_NOERRCHECK(iface, call)
+    #define CHECK_ALLOC_ERROR(iface)          GR_GL_GET_ERROR(iface)
+#else 
+    #define CLEAR_ERROR_BEFORE_ALLOC(iface)
+    #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL(iface, call)
+    #define CHECK_ALLOC_ERROR(iface)          GR_GL_NO_ERROR
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::GLCaps::markConfigAsValidColorAttachment(GrPixelConfig config) {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+    return;
+#endif
+    GrAssert(config < kGrPixelConfigCount);
+    int u32Idx = config / 32;
+    int bitIdx = config % 32;
+    fVerifiedColorAttachmentConfigs[u32Idx] |= (1 << bitIdx);
+}
+
+bool GrGpuGL::GLCaps::isConfigVerifiedColorAttachment(
+                                                GrPixelConfig config) const {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+    return false;
+#endif
+    GrAssert((unsigned)config < kGrPixelConfigCount);
+    int u32Idx = config / 32;
+    int bitIdx = config % 32;
+    return SkToBool(fVerifiedColorAttachmentConfigs[u32Idx] & (1 << bitIdx));
+}
+
+void GrGpuGL::GLCaps::markColorConfigAndStencilFormatAsVerified(
+                                    GrPixelConfig config,
+                                    const GrGLStencilBuffer::Format& format) {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+    return;
+#endif
+    GrAssert((unsigned)config < kGrPixelConfigCount);
+    int count = fStencilFormats.count();
+    // we expect a really small number of possible formats so linear search
+    // should be OK
+    GrAssert(count < 16);
+    for (int i = 0; i < count; ++i) {
+        if (format.fInternalFormat ==
+            fStencilFormats[i].fFormat.fInternalFormat) {
+            int u32Idx = config / 32;
+            int bitIdx = config % 32;
+            fStencilFormats[i].fVerifiedColorConfigs[u32Idx] |= (1 << bitIdx);
+            return;
+        }
+    }
+    SkDEBUGFAIL("Why are we seeing a stencil format that GLCaps doesn't know about.");
+}
+
+bool GrGpuGL::GLCaps::isColorConfigAndStencilFormatVerified(
+                                GrPixelConfig config,
+                                const GrGLStencilBuffer::Format& format) const {
+#if !GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT
+    return false;
+#endif
+    GrAssert((unsigned)config < kGrPixelConfigCount);
+    int count = fStencilFormats.count();
+    // we expect a really small number of possible formats so linear search
+    // should be OK
+    GrAssert(count < 16);
+    for (int i = 0; i < count; ++i) {
+        if (format.fInternalFormat ==
+            fStencilFormats[i].fFormat.fInternalFormat) {
+            int u32Idx = config / 32;
+            int bitIdx = config % 32;
+            return SkToBool(fStencilFormats[i].fVerifiedColorConfigs[u32Idx] &
+                            (1 << bitIdx));
+        }
+    }
+    SkDEBUGFAIL("Why are we seeing a stencil format that GLCaps doesn't know about.");
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const GrGLenum gXfermodeCoeff2Blend[] = {
+    GR_GL_ZERO,
+    GR_GL_ONE,
+    GR_GL_SRC_COLOR,
+    GR_GL_ONE_MINUS_SRC_COLOR,
+    GR_GL_DST_COLOR,
+    GR_GL_ONE_MINUS_DST_COLOR,
+    GR_GL_SRC_ALPHA,
+    GR_GL_ONE_MINUS_SRC_ALPHA,
+    GR_GL_DST_ALPHA,
+    GR_GL_ONE_MINUS_DST_ALPHA,
+    GR_GL_CONSTANT_COLOR,
+    GR_GL_ONE_MINUS_CONSTANT_COLOR,
+    GR_GL_CONSTANT_ALPHA,
+    GR_GL_ONE_MINUS_CONSTANT_ALPHA,
+
+    // extended blend coeffs
+    GR_GL_SRC1_COLOR,
+    GR_GL_ONE_MINUS_SRC1_COLOR,
+    GR_GL_SRC1_ALPHA,
+    GR_GL_ONE_MINUS_SRC1_ALPHA,
+};
+
+bool GrGpuGL::BlendCoeffReferencesConstant(GrBlendCoeff coeff) {
+    static const bool gCoeffReferencesBlendConst[] = {
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        false,
+        true,
+        true,
+        true,
+        true,
+
+        // extended blend coeffs
+        false,
+        false,
+        false,
+        false,
+    };
+    return gCoeffReferencesBlendConst[coeff];
+    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+
+    GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
+    GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
+    GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
+    GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
+    GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
+    GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
+    GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
+    GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
+    GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
+    GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
+    GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
+    GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
+    GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
+    GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
+
+    GR_STATIC_ASSERT(14 == kS2C_BlendCoeff);
+    GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff);
+    GR_STATIC_ASSERT(16 == kS2A_BlendCoeff);
+    GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff);
+
+    // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
+    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
+                                  GrSamplerState::SampleMode mode,
+                                  GrMatrix* matrix) {
+    GrAssert(NULL != texture);
+    GrAssert(NULL != matrix);
+    GrGLTexture::Orientation orientation = texture->orientation();
+    if (GrGLTexture::kBottomUp_Orientation == orientation) {
+        GrMatrix invY;
+        invY.setAll(GR_Scalar1, 0,           0,
+                    0,          -GR_Scalar1, GR_Scalar1,
+                    0,          0,           GrMatrix::I()[8]);
+        matrix->postConcat(invY);
+    } else {
+        GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
+    }
+}
+
+bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
+                                      const GrSamplerState& sampler) {
+    GrAssert(NULL != texture);
+    if (!sampler.getMatrix().isIdentity()) {
+        return false;
+    }
+    GrGLTexture::Orientation orientation = texture->orientation();
+    if (GrGLTexture::kBottomUp_Orientation == orientation) {
+        return false;
+    } else {
+        GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool gPrintStartupSpew;
+
+static bool fbo_test(const GrGLInterface* gl, int w, int h) {
+
+    GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0 + SPARE_TEX_UNIT));
+
+    GrGLuint testFBO;
+    GR_GL_CALL(gl, GenFramebuffers(1, &testFBO));
+    GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, testFBO));
+    GrGLuint testRTTex;
+    GR_GL_CALL(gl, GenTextures(1, &testRTTex));
+    GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, testRTTex));
+    // some implementations require texture to be mip-map complete before
+    // FBO with level 0 bound as color attachment will be framebuffer complete.
+    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+                                 GR_GL_TEXTURE_MIN_FILTER,
+                                 GR_GL_NEAREST));
+    GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RGBA, w, h,
+                              0, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE, NULL));
+    GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, 0));
+    GR_GL_CALL(gl, FramebufferTexture2D(GR_GL_FRAMEBUFFER,
+                                        GR_GL_COLOR_ATTACHMENT0,
+                                        GR_GL_TEXTURE_2D, testRTTex, 0));
+    GrGLenum status;
+    GR_GL_CALL_RET(gl, status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+    GR_GL_CALL(gl, DeleteFramebuffers(1, &testFBO));
+    GR_GL_CALL(gl, DeleteTextures(1, &testRTTex));
+
+    return status == GR_GL_FRAMEBUFFER_COMPLETE;
+}
+
+GrGpuGL::GrGpuGL(const GrGLContextInfo& ctxInfo) : fGLContextInfo(ctxInfo) {
+
+    GrAssert(ctxInfo.isInitialized());
+
+    fPrintedCaps = false;
+
+    GrGLClearErr(fGLContextInfo.interface());
+
+    if (gPrintStartupSpew) {
+        const GrGLubyte* ext;
+        GL_CALL_RET(ext, GetString(GR_GL_EXTENSIONS));
+        const GrGLubyte* vendor;
+        const GrGLubyte* renderer;
+        const GrGLubyte* version;
+        GL_CALL_RET(vendor, GetString(GR_GL_VENDOR));
+        GL_CALL_RET(renderer, GetString(GR_GL_RENDERER));
+        GL_CALL_RET(version, GetString(GR_GL_VERSION));
+        GrPrintf("------------------------- create GrGpuGL %p --------------\n",
+                 this);
+        GrPrintf("------ VENDOR %s\n", vendor);
+        GrPrintf("------ RENDERER %s\n", renderer);
+        GrPrintf("------ VERSION %s\n",  version);
+        GrPrintf("------ EXTENSIONS\n %s \n", ext);
+    }
+
+    this->resetDirtyFlags();
+
+    this->initCaps();
+
+    fLastSuccessfulStencilFmtIdx = 0;
+}
+
+GrGpuGL::~GrGpuGL() {
+    // This must be called by before the GrDrawTarget destructor
+    this->releaseGeometry();
+    // This subclass must do this before the base class destructor runs
+    // since we will unref the GrGLInterface.
+    this->releaseResources();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+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.
+    const GrGLInterface* gl = this->glInterface();
+    GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+    GrAssert(maxTextureUnits > GrDrawState::kNumStages);
+    if (kES2_GrGLBinding != this->glBinding()) {
+        GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
+        GrAssert(maxTextureUnits > GrDrawState::kNumStages);
+    }
+    if (kES2_GrGLBinding == this->glBinding()) {
+        GR_GL_GetIntegerv(gl, GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS,
+                          &fGLCaps.fMaxFragmentUniformVectors);
+    } else if (kDesktop_GrGLBinding != this->glBinding()) {
+        GrGLint max;
+        GR_GL_GetIntegerv(gl, GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max);
+        fGLCaps.fMaxFragmentUniformVectors = max / 4;
+    } else {
+        fGLCaps.fMaxFragmentUniformVectors = 16;
+    }
+
+    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) {
+            fCaps.f8BitPaletteSupport = true;
+            break;
+        }
+    }
+
+    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 = (this->glVersion() >= GR_GL_VER(2,0));
+        // supported on GL 1.4 and higher or by extension
+        fCaps.fStencilWrapOpsSupport = (this->glVersion() >= GR_GL_VER(1,4)) ||
+                                       this->hasExtension("GL_EXT_stencil_wrap");
+    } else {
+        // ES 2 has two sided stencil and stencil wrap
+        fCaps.fTwoSidedStencilSupport = true;
+        fCaps.fStencilWrapOpsSupport = true;
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fGLCaps.fRGBA8RenderbufferSupport = true;
+    } else {
+        fGLCaps.fRGBA8RenderbufferSupport =
+                                    this->hasExtension("GL_OES_rgb8_rgba8") ||
+                                    this->hasExtension("GL_ARM_rgba8");
+    }
+
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fGLCaps.fBGRAFormatSupport = this->glVersion() >= GR_GL_VER(1,2) ||
+                                     this->hasExtension("GL_EXT_bgra");
+    } else {
+        bool hasBGRAExt = false;
+        if (this->hasExtension("GL_APPLE_texture_format_BGRA8888")) {
+            fGLCaps.fBGRAFormatSupport = true;
+        } else if (this->hasExtension("GL_EXT_texture_format_BGRA8888")) {
+            fGLCaps.fBGRAFormatSupport = true;
+            fGLCaps.fBGRAIsInternalFormat = true;
+        }
+        GrAssert(fGLCaps.fBGRAFormatSupport ||
+                 kSkia8888_PM_GrPixelConfig != kBGRA_8888_PM_GrPixelConfig);
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fGLCaps.fTextureSwizzleSupport = this->glVersion() >= GR_GL_VER(3,3) ||
+                                  this->hasExtension("GL_ARB_texture_swizzle");
+    } else {
+        fGLCaps.fTextureSwizzleSupport = false;
+    }
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fGLCaps.fUnpackRowLengthSupport = true;
+        fGLCaps.fUnpackFlipYSupport = false;
+        fGLCaps.fPackRowLengthSupport = true;
+        fGLCaps.fPackFlipYSupport = false;
+    } else {
+        fGLCaps.fUnpackRowLengthSupport =this->hasExtension("GL_EXT_unpack_subimage");
+        fGLCaps.fUnpackFlipYSupport = this->hasExtension("GL_CHROMIUM_flipy");
+        // no extension for pack row length
+        fGLCaps.fPackRowLengthSupport = false;
+        fGLCaps.fPackFlipYSupport =
+            this->hasExtension("GL_ANGLE_pack_reverse_row_order");
+    }
+
+    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 (this->glVersion() >= GR_GL_VER(2,0) || 
+            this->hasExtension("GL_ARB_texture_non_power_of_two")) {
+            fCaps.fNPOTTextureTileSupport = true;
+        } else {
+            fCaps.fNPOTTextureTileSupport = false;
+        }
+    } else {
+        // Unextended ES2 supports NPOT textures with clamp_to_edge and non-mip filters only
+        fCaps.fNPOTTextureTileSupport = this->hasExtension("GL_OES_texture_npot");
+    }
+
+    fGLCaps.fTextureUsageSupport = (kES2_GrGLBinding == this->glBinding()) &&
+                                   this->hasExtension("GL_ANGLE_texture_usage");
+
+    // Tex storage is in desktop 4.2 and can be an extension to desktop or ES.
+    fGLCaps.fTexStorageSupport = (kDesktop_GrGLBinding == this->glBinding() &&
+                                  this->glVersion() >= GR_GL_VER(4,2)) ||
+                                 this->hasExtension("GL_ARB_texture_storage") ||
+                                 this->hasExtension("GL_EXT_texture_storage");
+
+    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.
+
+    GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_SIZE, &fCaps.fMaxTextureSize);
+    GR_GL_GetIntegerv(gl, 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);
+
+    this->initFSAASupport();
+    this->initStencilFormats();
+}
+
+void GrGpuGL::initFSAASupport() {
+
+    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 ((this->glVersion() >= 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;
+        }
+    }
+
+    fCaps.fFSAASupport = GLCaps::kNone_MSFBO != fGLCaps.fMSFBOType;
+}
+
+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
+
+    // Omitting fVerifiedColorConfigs from initializer list should init to 0.
+    static const GLCaps::StencilFormat
+                  // internal Format      stencil bits      total bits        packed?
+        gS8    = {{GR_GL_STENCIL_INDEX8,   8,                8,                false}, {0U}},
+        gS16   = {{GR_GL_STENCIL_INDEX16,  16,               16,               false}, {0U}},
+        gD24S8 = {{GR_GL_DEPTH24_STENCIL8, 8,                32,               true }, {0U}},
+        gS4    = {{GR_GL_STENCIL_INDEX4,   4,                4,                false}, {0U}},
+        gS     = {{GR_GL_STENCIL_INDEX,    kUnknownBitCount, kUnknownBitCount, false}, {0U}},
+        gDS    = {{GR_GL_DEPTH_STENCIL,    kUnknownBitCount, kUnknownBitCount, true }, {0U}};
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        bool supportsPackedDS = this->glVersion() >= 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 but requires extensions
+        // for other formats.
+        // ES doesn't support using the unsized format.
+
+        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;
+        }
+    }
+#if GR_DEBUG
+    // ensure that initially all color / stencil format combos have unverified
+    // fbo status.
+    for (int i = 0; i < fGLCaps.fStencilFormats.count(); ++i) {
+        int numU32 =
+            GR_ARRAY_COUNT(fGLCaps.fStencilFormats[i].fVerifiedColorConfigs);
+        for (int j = 0; j < numU32; ++j) {
+            GrAssert(0 == fGLCaps.fStencilFormats[i].fVerifiedColorConfigs[j]);
+        }
+    }
+#endif
+}
+
+GrPixelConfig GrGpuGL::preferredReadPixelsConfig(GrPixelConfig config) const {
+    if (GR_GL_RGBA_8888_PIXEL_OPS_SLOW && GrPixelConfigIsRGBA8888(config)) {
+        return GrPixelConfigSwapRAndB(config);
+    } else {
+        return config;
+    }
+}
+
+GrPixelConfig GrGpuGL::preferredWritePixelsConfig(GrPixelConfig config) const {
+    if (GR_GL_RGBA_8888_PIXEL_OPS_SLOW && GrPixelConfigIsRGBA8888(config)) {
+        return GrPixelConfigSwapRAndB(config);
+    } else {
+        return config;
+    }
+}
+
+bool GrGpuGL::fullReadPixelsIsFasterThanPartial() const {
+    return SkToBool(GR_GL_FULL_READPIXELS_FASTER_THAN_PARTIAL);
+}
+
+void GrGpuGL::onResetContext() {
+    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));
+
+    // we don't use the zb at all
+    GL_CALL(Disable(GR_GL_DEPTH_TEST));
+    GL_CALL(DepthMask(GR_GL_FALSE));
+
+    GL_CALL(Disable(GR_GL_CULL_FACE));
+    GL_CALL(FrontFace(GR_GL_CCW));
+    fHWDrawState.setDrawFace(GrDrawState::kBoth_DrawFace);
+
+    GL_CALL(Disable(GR_GL_DITHER));
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        GL_CALL(Disable(GR_GL_LINE_SMOOTH));
+        GL_CALL(Disable(GR_GL_POINT_SMOOTH));
+        GL_CALL(Disable(GR_GL_MULTISAMPLE));
+        fHWAAState.fMSAAEnabled = false;
+        fHWAAState.fSmoothLineEnabled = false;
+    }
+
+    GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+    fHWDrawState.resetStateFlags();
+
+    // we only ever use lines in hairline mode
+    GL_CALL(LineWidth(1));
+
+    // invalid
+    fActiveTextureUnitIdx = -1;
+
+    // illegal values
+    fHWDrawState.setBlendFunc((GrBlendCoeff)0xFF, (GrBlendCoeff)0xFF);
+
+    fHWDrawState.setBlendConstant(0x00000000);
+    GL_CALL(BlendColor(0,0,0,0));
+
+    fHWDrawState.setColor(GrColor_ILLEGAL);
+
+    fHWDrawState.setViewMatrix(GrMatrix::InvalidMatrix());
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        fHWDrawState.setTexture(s, NULL);
+        fHWDrawState.sampler(s)->setRadial2Params(-GR_ScalarMax,
+                                                  -GR_ScalarMax,
+                                                  true);
+        *fHWDrawState.sampler(s)->matrix() = GrMatrix::InvalidMatrix();
+        fHWDrawState.sampler(s)->setConvolutionParams(0, NULL, NULL);
+    }
+
+    fHWBounds.fScissorRect.invalidate();
+    fHWBounds.fScissorEnabled = false;
+    GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+    fHWBounds.fViewportRect.invalidate();
+
+    fHWDrawState.stencil()->invalidate();
+    fHWStencilClip = false;
+    fClipInStencil = false;
+
+    fHWGeometryState.fIndexBuffer = NULL;
+    fHWGeometryState.fVertexBuffer = NULL;
+    
+    fHWGeometryState.fArrayPtrsDirty = true;
+
+    GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+    fHWDrawState.setRenderTarget(NULL);
+
+    // we assume these values
+    if (this->glCaps().fUnpackRowLengthSupport) {
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+    }
+    if (this->glCaps().fPackRowLengthSupport) {
+        GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
+    }
+    if (this->glCaps().fUnpackFlipYSupport) {
+        GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE));
+    }
+    if (this->glCaps().fPackFlipYSupport) {
+        GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, GR_GL_FALSE));
+    }
+}
+
+GrTexture* GrGpuGL::onCreatePlatformTexture(const GrPlatformTextureDesc& desc) {
+    GrGLTexture::Desc glTexDesc;
+    if (!configToGLFormats(desc.fConfig, false, NULL, NULL, NULL)) {
+        return NULL;
+    }
+
+    glTexDesc.fWidth = desc.fWidth;
+    glTexDesc.fHeight = desc.fHeight;
+    glTexDesc.fConfig = desc.fConfig;
+    glTexDesc.fTextureID = static_cast<GrGLuint>(desc.fTextureHandle);
+    glTexDesc.fOwnsID = false;
+    glTexDesc.fOrientation = GrGLTexture::kBottomUp_Orientation;
+
+    GrGLTexture* texture = NULL;
+    if (desc.fFlags & kRenderTarget_GrPlatformTextureFlag) {
+        GrGLRenderTarget::Desc glRTDesc;
+        glRTDesc.fRTFBOID = 0;
+        glRTDesc.fTexFBOID = 0;
+        glRTDesc.fMSColorRenderbufferID = 0;
+        glRTDesc.fOwnIDs = true;
+        glRTDesc.fConfig = desc.fConfig;
+        glRTDesc.fSampleCnt = desc.fSampleCnt;
+        if (!this->createRenderTargetObjects(glTexDesc.fWidth,
+                                             glTexDesc.fHeight,
+                                             glTexDesc.fTextureID,
+                                             &glRTDesc)) {
+            return NULL;
+        }
+        texture = new GrGLTexture(this, glTexDesc, glRTDesc);
+    } else {
+        texture = new GrGLTexture(this, glTexDesc);
+    }
+    if (NULL == texture) {
+        return NULL;
+    }
+    
+    this->setSpareTextureUnit();
+    return texture;
+}
+
+GrRenderTarget* GrGpuGL::onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
+    GrGLRenderTarget::Desc glDesc;
+    glDesc.fConfig = desc.fConfig;
+    glDesc.fRTFBOID = static_cast<GrGLuint>(desc.fRenderTargetHandle);
+    glDesc.fMSColorRenderbufferID = 0;
+    glDesc.fTexFBOID = GrGLRenderTarget::kUnresolvableFBOID;
+    glDesc.fSampleCnt = desc.fSampleCnt;
+    glDesc.fOwnIDs = false;
+    GrGLIRect viewport;
+    viewport.fLeft   = 0;
+    viewport.fBottom = 0;
+    viewport.fWidth  = desc.fWidth;
+    viewport.fHeight = desc.fHeight;
+    
+    GrRenderTarget* tgt = new GrGLRenderTarget(this, glDesc, viewport);
+    if (desc.fStencilBits) {
+        GrGLStencilBuffer::Format format;
+        format.fInternalFormat = GrGLStencilBuffer::kUnknownInternalFormat;
+        format.fPacked = false;
+        format.fStencilBits = desc.fStencilBits;
+        format.fTotalBits = desc.fStencilBits;
+        GrGLStencilBuffer* sb = new GrGLStencilBuffer(this,
+                                                      0,
+                                                      desc.fWidth,
+                                                      desc.fHeight,
+                                                      desc.fSampleCnt,
+                                                      format);
+        tgt->setStencilBuffer(sb);
+        sb->unref();
+    }
+    return tgt;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::onWriteTexturePixels(GrTexture* texture,
+                                   int left, int top, int width, int height,
+                                   GrPixelConfig config, const void* buffer,
+                                   size_t rowBytes) {
+    if (NULL == buffer) {
+        return;
+    }
+    GrGLTexture* glTex = static_cast<GrGLTexture*>(texture);
+
+    this->setSpareTextureUnit();
+    GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTex->textureID()));
+    GrGLTexture::Desc desc;
+    desc.fConfig = glTex->config();
+    desc.fWidth = glTex->width();
+    desc.fHeight = glTex->height();
+    desc.fOrientation = glTex->orientation();
+    desc.fTextureID = glTex->textureID();
+
+    this->uploadTexData(desc, false,
+                        left, top, width, height, 
+                        config, buffer, rowBytes);
+}
+
+namespace {
+bool adjust_pixel_ops_params(int surfaceWidth,
+                             int surfaceHeight,
+                             size_t bpp,
+                             int* left, int* top, int* width, int* height,
+                             const void** data,
+                             size_t* rowBytes) {
+    if (!*rowBytes) {
+        *rowBytes = *width * bpp;
+    }
+
+    GrIRect subRect = GrIRect::MakeXYWH(*left, *top, *width, *height);
+    GrIRect bounds = GrIRect::MakeWH(surfaceWidth, surfaceHeight);
+
+    if (!subRect.intersect(bounds)) {
+        return false;
+    }
+    *data = reinterpret_cast<const void*>(reinterpret_cast<intptr_t>(*data) +
+          (subRect.fTop - *top) * *rowBytes + (subRect.fLeft - *left) * bpp);
+
+    *left = subRect.fLeft;
+    *top = subRect.fTop;
+    *width = subRect.width();
+    *height = subRect.height();
+    return true;
+}
+}
+
+bool GrGpuGL::uploadTexData(const GrGLTexture::Desc& desc,
+                            bool isNewTexture,
+                            int left, int top, int width, int height,
+                            GrPixelConfig dataConfig,
+                            const void* data,
+                            size_t rowBytes) {
+    GrAssert(NULL != data || isNewTexture);
+
+    size_t bpp = GrBytesPerPixel(dataConfig);
+    if (!adjust_pixel_ops_params(desc.fWidth, desc.fHeight, bpp, &left, &top,
+                                 &width, &height, &data, &rowBytes)) {
+        return false;
+    }
+    size_t trimRowBytes = width * bpp;
+
+    // in case we need a temporary, trimmed copy of the src pixels
+    SkAutoSMalloc<128 * 128> tempStorage;
+
+    bool useTexStorage = isNewTexture &&
+                         this->glCaps().fTexStorageSupport;
+    if (useTexStorage) {
+        if (kDesktop_GrGLBinding == this->glBinding()) {
+            // 565 is not a sized internal format on desktop GL. So on desktop
+            // with 565 we always use an unsized internal format to let the
+            // system pick the best sized format to convert the 565 data to.
+            // Since glTexStorage only allows sized internal formats we will
+            // instead fallback to glTexImage2D.
+            useTexStorage = desc.fConfig != kRGB_565_GrPixelConfig;
+        } else {
+            // ES doesn't allow paletted textures to be used with tex storage
+            useTexStorage = desc.fConfig != kIndex_8_GrPixelConfig;
+        }
+    }
+
+    GrGLenum internalFormat;
+    GrGLenum externalFormat;
+    GrGLenum externalType;
+    // glTexStorage requires sized internal formats on both desktop and ES. ES
+    // glTexImage requires an unsized format.
+    if (!this->configToGLFormats(dataConfig, useTexStorage, &internalFormat,
+                                 &externalFormat, &externalType)) {
+        return false;
+    }
+
+    if (!isNewTexture && GR_GL_PALETTE8_RGBA8 == internalFormat) {
+        // paletted textures cannot be updated
+        return false;
+    }
+
+    /*
+     *  check whether to allocate a temporary buffer for flipping y or
+     *  because our srcData has extra bytes past each row. If so, we need
+     *  to trim those off here, since GL ES may not let us specify
+     *  GL_UNPACK_ROW_LENGTH.
+     */
+    bool restoreGLRowLength = false;
+    bool swFlipY = false;
+    bool glFlipY = false;
+    if (NULL != data) {
+        if (GrGLTexture::kBottomUp_Orientation == desc.fOrientation) {
+            if (this->glCaps().fUnpackFlipYSupport) {
+                glFlipY = true;
+            } else {
+                swFlipY = true;
+            }
+        }
+        if (this->glCaps().fUnpackRowLengthSupport && !swFlipY) {
+            // can't use this for flipping, only non-neg values allowed. :(
+            if (rowBytes != trimRowBytes) {
+                GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
+                GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, rowLength));
+                restoreGLRowLength = true;
+            }
+        } else {
+            if (trimRowBytes != rowBytes || swFlipY) {
+                // copy data into our new storage, skipping the trailing bytes
+                size_t trimSize = height * trimRowBytes;
+                const char* src = (const char*)data;
+                if (swFlipY) {
+                    src += (height - 1) * rowBytes;
+                }
+                char* dst = (char*)tempStorage.reset(trimSize);
+                for (int y = 0; y < height; y++) {
+                    memcpy(dst, src, trimRowBytes);
+                    if (swFlipY) {
+                        src -= rowBytes;
+                    } else {
+                        src += rowBytes;
+                    }
+                    dst += trimRowBytes;
+                }
+                // now point data to our copied version
+                data = tempStorage.get();
+            }
+        }
+        if (glFlipY) {
+            GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_TRUE));
+        }
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, static_cast<GrGLint>(bpp)));
+    }
+    bool succeeded = true;
+    if (isNewTexture && 
+        0 == left && 0 == top &&
+        desc.fWidth == width && desc.fHeight == height) {
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        if (useTexStorage) {
+            // We never resize  or change formats of textures. We don't use
+            // mipmaps currently.
+            GL_ALLOC_CALL(this->glInterface(),
+                          TexStorage2D(GR_GL_TEXTURE_2D,
+                                       1, // levels
+                                       internalFormat,
+                                       desc.fWidth, desc.fHeight));
+        } else {
+            if (GR_GL_PALETTE8_RGBA8 == internalFormat) {
+                GrGLsizei imageSize = desc.fWidth * desc.fHeight +
+                                      kGrColorTableSize;
+                GL_ALLOC_CALL(this->glInterface(),
+                              CompressedTexImage2D(GR_GL_TEXTURE_2D,
+                                                   0, // level
+                                                   internalFormat,
+                                                   desc.fWidth, desc.fHeight,
+                                                   0, // border
+                                                   imageSize,
+                                                   data));
+            } else {
+                GL_ALLOC_CALL(this->glInterface(),
+                              TexImage2D(GR_GL_TEXTURE_2D,
+                                         0, // level
+                                         internalFormat,
+                                         desc.fWidth, desc.fHeight,
+                                         0, // border
+                                         externalFormat, externalType,
+                                         data));
+            }
+        }
+        GrGLenum error = CHECK_ALLOC_ERROR(this->glInterface());
+        if (error != GR_GL_NO_ERROR) {
+            succeeded = false;
+        } else {
+            // if we have data and we used TexStorage to create the texture, we
+            // now upload with TexSubImage.
+            if (NULL != data && useTexStorage) {
+                GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D,
+                                      0, // level
+                                      left, top,
+                                      width, height,
+                                      externalFormat, externalType,
+                                      data));
+            }
+        }
+    } else {
+        if (swFlipY || glFlipY) {
+            top = desc.fHeight - (top + height);
+        }
+        GL_CALL(TexSubImage2D(GR_GL_TEXTURE_2D,
+                              0, // level
+                              left, top,
+                              width, height,
+                              externalFormat, externalType, data));
+    }
+
+    if (restoreGLRowLength) {
+        GrAssert(this->glCaps().fUnpackRowLengthSupport);
+        GL_CALL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
+    }
+    if (glFlipY) {
+        GL_CALL(PixelStorei(GR_GL_UNPACK_FLIP_Y, GR_GL_FALSE));
+    }
+    return succeeded;
+}
+
+bool GrGpuGL::createRenderTargetObjects(int width, int height,
+                                        GrGLuint texID,
+                                        GrGLRenderTarget::Desc* desc) {
+    desc->fMSColorRenderbufferID = 0;
+    desc->fRTFBOID = 0;
+    desc->fTexFBOID = 0;
+    desc->fOwnIDs = true;
+
+    GrGLenum status;
+    GrGLint err;
+
+    GrGLenum msColorFormat = 0; // suppress warning
+
+    GL_CALL(GenFramebuffers(1, &desc->fTexFBOID));
+    if (!desc->fTexFBOID) {
+        goto FAILED;
+    }
+    
+
+    // 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 > 0) {
+        if (GLCaps::kNone_MSFBO == fGLCaps.fMSFBOType) {
+            goto FAILED;
+        }
+        GL_CALL(GenFramebuffers(1, &desc->fRTFBOID));
+        GL_CALL(GenRenderbuffers(1, &desc->fMSColorRenderbufferID));
+        if (!desc->fRTFBOID ||
+            !desc->fMSColorRenderbufferID || 
+            !this->configToGLFormats(desc->fConfig,
+                                     // GLES requires sized internal formats
+                                     kES2_GrGLBinding == this->glBinding(),
+                                     &msColorFormat, NULL, NULL)) {
+            goto FAILED;
+        }
+    } else {
+        desc->fRTFBOID = desc->fTexFBOID;
+    }
+
+    // below here we may bind the FBO
+    fHWDrawState.setRenderTarget(NULL);
+    if (desc->fRTFBOID != desc->fTexFBOID) {
+        GrAssert(desc->fSampleCnt > 1);
+        GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER,
+                               desc->fMSColorRenderbufferID));
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        GL_ALLOC_CALL(this->glInterface(),
+                      RenderbufferStorageMultisample(GR_GL_RENDERBUFFER, 
+                                                     desc->fSampleCnt,
+                                                     msColorFormat,
+                                                     width, height));
+        err = CHECK_ALLOC_ERROR(this->glInterface());
+        if (err != GR_GL_NO_ERROR) {
+            goto FAILED;
+        }
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fRTFBOID));
+        GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, 
+                                      GR_GL_COLOR_ATTACHMENT0,
+                                      GR_GL_RENDERBUFFER,
+                                      desc->fMSColorRenderbufferID));
+        if (!fGLCaps.isConfigVerifiedColorAttachment(desc->fConfig)) {
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+                goto FAILED;
+            }
+            fGLCaps.markConfigAsValidColorAttachment(desc->fConfig);
+        }
+    }
+    GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fTexFBOID));
+
+    GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER,
+                                 GR_GL_COLOR_ATTACHMENT0,
+                                 GR_GL_TEXTURE_2D,
+                                 texID, 0));
+    if (!fGLCaps.isConfigVerifiedColorAttachment(desc->fConfig)) {
+        GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+        if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+            goto FAILED;
+        }
+        fGLCaps.markConfigAsValidColorAttachment(desc->fConfig);
+    }
+
+    return true;
+
+FAILED:
+    if (desc->fMSColorRenderbufferID) {
+        GL_CALL(DeleteRenderbuffers(1, &desc->fMSColorRenderbufferID));
+    }
+    if (desc->fRTFBOID != desc->fTexFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &desc->fRTFBOID));
+    }
+    if (desc->fTexFBOID) {
+        GL_CALL(DeleteFramebuffers(1, &desc->fTexFBOID));
+    }
+    return false;
+}
+
+// good to set a break-point here to know when createTexture fails
+static GrTexture* return_null_texture() {
+//    GrAssert(!"null texture");
+    return NULL;
+}
+
+#if GR_DEBUG
+static size_t as_size_t(int x) {
+    return x;
+}
+#endif
+
+GrTexture* GrGpuGL::onCreateTexture(const GrTextureDesc& desc,
+                                    const void* srcData,
+                                    size_t rowBytes) {
+
+#if GR_COLLECT_STATS
+    ++fStats.fTextureCreateCnt;
+#endif
+
+    GrGLTexture::Desc glTexDesc;
+    GrGLRenderTarget::Desc  glRTDesc;
+
+    // Attempt to catch un- or wrongly initialized sample counts;
+    GrAssert(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
+
+    glTexDesc.fWidth  = desc.fWidth;
+    glTexDesc.fHeight = desc.fHeight;
+    glTexDesc.fConfig = desc.fConfig;
+    glTexDesc.fOwnsID = true;
+
+    glRTDesc.fMSColorRenderbufferID = 0;
+    glRTDesc.fRTFBOID = 0;
+    glRTDesc.fTexFBOID = 0;
+    glRTDesc.fOwnIDs = true;
+    glRTDesc.fConfig = glTexDesc.fConfig;
+
+    bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit);
+
+    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;
+
+    glRTDesc.fSampleCnt = desc.fSampleCnt;
+    if (GLCaps::kNone_MSFBO == fGLCaps.fMSFBOType &&
+        desc.fSampleCnt) {
+        GrPrintf("MSAA RT requested but not supported on this platform.");
+    }
+
+    if (renderTarget) {
+        if (glTexDesc.fWidth > caps.fMaxRenderTargetSize ||
+            glTexDesc.fHeight > caps.fMaxRenderTargetSize) {
+            return return_null_texture();
+        }
+    }
+
+    GL_CALL(GenTextures(1, &glTexDesc.fTextureID));
+    if (renderTarget && this->glCaps().fTextureUsageSupport) {
+        // provides a hint about how this texture will be used
+        GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                              GR_GL_TEXTURE_USAGE,
+                              GR_GL_FRAMEBUFFER_ATTACHMENT));
+    }
+    if (!glTexDesc.fTextureID) {
+        return return_null_texture();
+    }
+
+    this->setSpareTextureUnit();
+    GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTexDesc.fTextureID));
+
+    // Some drivers like to know filter/wrap before seeing glTexImage2D. Some
+    // drivers have a bug where an FBO won't be complete if it includes a
+    // texture that is not mipmap complete (considering the filter in use).
+    GrGLTexture::TexParams initialTexParams;
+    // we only set a subset here so invalidate first
+    initialTexParams.invalidate();
+    initialTexParams.fFilter = GR_GL_NEAREST;
+    initialTexParams.fWrapS = GR_GL_CLAMP_TO_EDGE;
+    initialTexParams.fWrapT = GR_GL_CLAMP_TO_EDGE;
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_MAG_FILTER,
+                          initialTexParams.fFilter));
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_MIN_FILTER,
+                          initialTexParams.fFilter));
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_WRAP_S,
+                          initialTexParams.fWrapS));
+    GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                          GR_GL_TEXTURE_WRAP_T,
+                          initialTexParams.fWrapT));
+    if (!this->uploadTexData(glTexDesc, true, 0, 0,
+                             glTexDesc.fWidth, glTexDesc.fHeight,
+                             desc.fConfig, srcData, rowBytes)) {
+        GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
+        return return_null_texture();
+    }
+
+    GrGLTexture* tex;
+    if (renderTarget) {
+#if GR_COLLECT_STATS
+        ++fStats.fRenderTargetCreateCnt;
+#endif
+        if (!this->createRenderTargetObjects(glTexDesc.fWidth,
+                                             glTexDesc.fHeight,
+                                             glTexDesc.fTextureID,
+                                             &glRTDesc)) {
+            GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
+            return return_null_texture();
+        }
+        tex = new GrGLTexture(this, glTexDesc, glRTDesc);
+    } else {
+        tex = new GrGLTexture(this, glTexDesc);
+    }
+    tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
+#ifdef TRACE_TEXTURE_CREATION
+    GrPrintf("--- new texture [%d] size=(%d %d) config=%d\n",
+             glTexDesc.fTextureID, desc.fWidth, desc.fHeight, desc.fConfig);
+#endif
+    return tex;
+}
+
+namespace {
+void inline get_stencil_rb_sizes(const GrGLInterface* gl,
+                                 GrGLuint rb, 
+                                 GrGLStencilBuffer::Format* format) {
+    // we shouldn't ever know one size and not the other
+    GrAssert((kUnknownBitCount == format->fStencilBits) ==
+             (kUnknownBitCount == format->fTotalBits));
+    if (kUnknownBitCount == format->fStencilBits) {
+        GR_GL_GetRenderbufferParameteriv(gl, GR_GL_RENDERBUFFER,
+                                         GR_GL_RENDERBUFFER_STENCIL_SIZE,
+                                         (GrGLint*)&format->fStencilBits);
+        if (format->fPacked) {
+            GR_GL_GetRenderbufferParameteriv(gl, GR_GL_RENDERBUFFER,
+                                             GR_GL_RENDERBUFFER_DEPTH_SIZE,
+                                             (GrGLint*)&format->fTotalBits);
+            format->fTotalBits += format->fStencilBits;
+        } else {
+            format->fTotalBits = format->fStencilBits;
+        }
+    }
+}
+}
+
+bool GrGpuGL::createStencilBufferForRenderTarget(GrRenderTarget* rt,
+                                                 int width, int height) {
+
+    // All internally created RTs are also textures. We don't create
+    // SBs for a client's standalone RT (that is RT that isnt also a texture).
+    GrAssert(rt->asTexture());
+    GrAssert(width >= rt->width());
+    GrAssert(height >= rt->height());
+
+    int samples = rt->numSamples();
+    GrGLuint sbID;
+    GL_CALL(GenRenderbuffers(1, &sbID));
+    if (!sbID) {
+        return false;
+    }
+
+    GrGLStencilBuffer* sb = NULL;
+
+    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 GLCaps::StencilFormat& sFmt = fGLCaps.fStencilFormats[sIdx];
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        // 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) {
+            GL_ALLOC_CALL(this->glInterface(),
+                          RenderbufferStorageMultisample(GR_GL_RENDERBUFFER,
+                                                         samples,
+                                                         sFmt.fFormat.fInternalFormat,
+                                                         width, height));
+        } else {
+            GL_ALLOC_CALL(this->glInterface(),
+                          RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                              sFmt.fFormat.fInternalFormat,
+                                              width, height));
+        }
+
+        GrGLenum err = CHECK_ALLOC_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 = sFmt.fFormat;
+            get_stencil_rb_sizes(this->glInterface(), sbID, &format);
+            sb = new GrGLStencilBuffer(this, sbID, width, height, 
+                                       samples, format);
+            if (this->attachStencilBufferToRenderTarget(sb, rt)) {
+                fLastSuccessfulStencilFmtIdx = sIdx;
+                rt->setStencilBuffer(sb);
+                sb->unref();
+                return true;
+           }
+           sb->abandon(); // otherwise we lose sbID
+           sb->unref();
+        }
+    }
+    GL_CALL(DeleteRenderbuffers(1, &sbID));
+    return false;
+}
+
+bool GrGpuGL::attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
+                                                GrRenderTarget* rt) {
+    GrGLRenderTarget* glrt = (GrGLRenderTarget*) rt;
+
+    GrGLuint fbo = glrt->renderFBOID();
+
+    if (NULL == sb) {
+        if (NULL != rt->getStencilBuffer()) {
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_STENCIL_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, 0));
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_DEPTH_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, 0));
+#if GR_DEBUG
+            GrGLenum status;
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            GrAssert(GR_GL_FRAMEBUFFER_COMPLETE == status);
+#endif
+        }
+        return true;
+    } else {
+        GrGLStencilBuffer* glsb = (GrGLStencilBuffer*) sb;
+        GrGLuint rb = glsb->renderbufferID();
+
+        fHWDrawState.setRenderTarget(NULL);
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo));
+        GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                      GR_GL_STENCIL_ATTACHMENT,
+                                      GR_GL_RENDERBUFFER, rb));
+        if (glsb->format().fPacked) {
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_DEPTH_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, rb));
+        } else {
+            GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                          GR_GL_DEPTH_ATTACHMENT,
+                                          GR_GL_RENDERBUFFER, 0));
+        }
+
+        GrGLenum status;
+        if (!fGLCaps.isColorConfigAndStencilFormatVerified(rt->config(),
+                                                           glsb->format())) {
+            GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+            if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+                GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                              GR_GL_STENCIL_ATTACHMENT,
+                                              GR_GL_RENDERBUFFER, 0));
+                if (glsb->format().fPacked) {
+                    GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                                  GR_GL_DEPTH_ATTACHMENT,
+                                                  GR_GL_RENDERBUFFER, 0));
+                }
+                return false;
+            } else {
+                fGLCaps.markColorConfigAndStencilFormatAsVerified(
+                    rt->config(),
+                    glsb->format());
+            }
+        }
+        return true;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+GrVertexBuffer* GrGpuGL::onCreateVertexBuffer(uint32_t size, bool dynamic) {
+    GrGLuint id;
+    GL_CALL(GenBuffers(1, &id));
+    if (id) {
+        GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, id));
+        fHWGeometryState.fArrayPtrsDirty = true;
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        // make sure driver can allocate memory for this buffer
+        GL_ALLOC_CALL(this->glInterface(),
+                      BufferData(GR_GL_ARRAY_BUFFER,
+                                 size,
+                                 NULL,   // data ptr
+                                 dynamic ? GR_GL_DYNAMIC_DRAW :
+                                           GR_GL_STATIC_DRAW));
+        if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) {
+            GL_CALL(DeleteBuffers(1, &id));
+            // deleting bound buffer does implicit bind to 0
+            fHWGeometryState.fVertexBuffer = NULL;
+            return NULL;
+        }
+        GrGLVertexBuffer* vertexBuffer = new GrGLVertexBuffer(this, id,
+                                                              size, dynamic);
+        fHWGeometryState.fVertexBuffer = vertexBuffer;
+        return vertexBuffer;
+    }
+    return NULL;
+}
+
+GrIndexBuffer* GrGpuGL::onCreateIndexBuffer(uint32_t size, bool dynamic) {
+    GrGLuint id;
+    GL_CALL(GenBuffers(1, &id));
+    if (id) {
+        GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, id));
+        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
+        // make sure driver can allocate memory for this buffer
+        GL_ALLOC_CALL(this->glInterface(),
+                      BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
+                                 size,
+                                 NULL,  // data ptr
+                                 dynamic ? GR_GL_DYNAMIC_DRAW :
+                                           GR_GL_STATIC_DRAW));
+        if (CHECK_ALLOC_ERROR(this->glInterface()) != GR_GL_NO_ERROR) {
+            GL_CALL(DeleteBuffers(1, &id));
+            // deleting bound buffer does implicit bind to 0
+            fHWGeometryState.fIndexBuffer = NULL;
+            return NULL;
+        }
+        GrIndexBuffer* indexBuffer = new GrGLIndexBuffer(this, id,
+                                                         size, dynamic);
+        fHWGeometryState.fIndexBuffer = indexBuffer;
+        return indexBuffer;
+    }
+    return NULL;
+}
+
+void GrGpuGL::flushScissor(const GrIRect* rect) {
+    const GrDrawState& drawState = this->getDrawState();
+    const GrGLRenderTarget* rt =
+        static_cast<const GrGLRenderTarget*>(drawState.getRenderTarget());
+
+    GrAssert(NULL != rt);
+    const GrGLIRect& vp = rt->getViewport();
+
+    GrGLIRect scissor;
+    if (NULL != rect) {
+        scissor.setRelativeTo(vp, rect->fLeft, rect->fTop,
+                              rect->width(), rect->height());
+        if (scissor.contains(vp)) {
+            rect = NULL;
+        }
+    }
+
+    if (NULL != rect) {
+        if (fHWBounds.fScissorRect != scissor) {
+            scissor.pushToGLScissor(this->glInterface());
+            fHWBounds.fScissorRect = scissor;
+        }
+        if (!fHWBounds.fScissorEnabled) {
+            GL_CALL(Enable(GR_GL_SCISSOR_TEST));
+            fHWBounds.fScissorEnabled = true;
+        }
+    } else {
+        if (fHWBounds.fScissorEnabled) {
+            GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+            fHWBounds.fScissorEnabled = false;
+        }
+    }
+}
+
+void GrGpuGL::onClear(const GrIRect* rect, GrColor color) {
+    const GrDrawState& drawState = this->getDrawState();
+    const GrRenderTarget* rt = drawState.getRenderTarget();
+    // parent class should never let us get here with no RT
+    GrAssert(NULL != rt);
+
+    GrIRect clippedRect;
+    if (NULL != rect) {
+        // flushScissor expects rect to be clipped to the target.
+        clippedRect = *rect;
+        GrIRect rtRect = SkIRect::MakeWH(rt->width(), rt->height());
+        if (clippedRect.intersect(rtRect)) {
+            rect = &clippedRect;
+        } else {
+            return;
+        }
+    }
+    this->flushRenderTarget(rect);
+    this->flushScissor(rect);
+
+    GrGLfloat r, g, b, a;
+    static const GrGLfloat scale255 = 1.f / 255.f;
+    a = GrColorUnpackA(color) * scale255;
+    GrGLfloat scaleRGB = scale255;
+    if (GrPixelConfigIsUnpremultiplied(rt->config())) {
+        scaleRGB *= a;
+    }
+    r = GrColorUnpackR(color) * scaleRGB;
+    g = GrColorUnpackG(color) * scaleRGB;
+    b = GrColorUnpackB(color) * scaleRGB;
+
+    GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+    fHWDrawState.disableState(GrDrawState::kNoColorWrites_StateBit);
+    GL_CALL(ClearColor(r, g, b, a));
+    GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
+}
+
+void GrGpuGL::clearStencil() {
+    if (NULL == this->getDrawState().getRenderTarget()) {
+        return;
+    }
+    
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
+
+    if (fHWBounds.fScissorEnabled) {
+        GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+        fHWBounds.fScissorEnabled = false;
+    }
+    GL_CALL(StencilMask(0xffffffff));
+    GL_CALL(ClearStencil(0));
+    GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
+    fHWDrawState.stencil()->invalidate();
+}
+
+void GrGpuGL::clearStencilClip(const GrIRect& rect, bool insideClip) {
+    const GrDrawState& drawState = this->getDrawState();
+    const GrRenderTarget* rt = drawState.getRenderTarget();
+    GrAssert(NULL != rt);
+
+    // this should only be called internally when we know we have a
+    // stencil buffer.
+    GrAssert(NULL != rt->getStencilBuffer());
+    GrGLint stencilBitCount =  rt->getStencilBuffer()->bits();
+#if 0
+    GrAssert(stencilBitCount > 0);
+    GrGLint clipStencilMask  = (1 << (stencilBitCount - 1));
+#else
+    // we could just clear the clip bit but when we go through
+    // ANGLE a partial stencil mask will cause clears to be
+    // turned into draws. Our contract on GrDrawTarget says that
+    // changing the clip between stencil passes may or may not
+    // zero the client's clip bits. So we just clear the whole thing.
+    static const GrGLint clipStencilMask  = ~0;
+#endif
+    GrGLint value;
+    if (insideClip) {
+        value = (1 << (stencilBitCount - 1));
+    } else {
+        value = 0;
+    }
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
+    this->flushScissor(&rect);
+    GL_CALL(StencilMask(clipStencilMask));
+    GL_CALL(ClearStencil(value));
+    GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
+    fHWDrawState.stencil()->invalidate();
+}
+
+void GrGpuGL::onForceRenderTargetFlush() {
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
+}
+
+bool GrGpuGL::readPixelsWillPayForYFlip(GrRenderTarget* renderTarget,
+                                        int left, int top,
+                                        int width, int height,
+                                        GrPixelConfig config,
+                                        size_t rowBytes) const {
+    // if GL can do the flip then we'll never pay for it.
+    if (this->glCaps().fPackFlipYSupport) {
+        return false;
+    }
+
+    // If we have to do memcpy to handle non-trim rowBytes then we
+    // get the flip for free. Otherwise it costs.
+    if (this->glCaps().fPackRowLengthSupport) {
+        return true;
+    }
+    // If we have to do memcpys to handle rowBytes then y-flip is free
+    // Note the rowBytes might be tight to the passed in data, but if data
+    // gets clipped in x to the target the rowBytes will no longer be tight.
+    if (left >= 0 && (left + width) < renderTarget->width()) {
+           return 0 == rowBytes ||
+                  GrBytesPerPixel(config) * width == rowBytes;
+    } else {
+        return false;
+    }
+}
+
+bool GrGpuGL::onReadPixels(GrRenderTarget* target,
+                           int left, int top,
+                           int width, int height,
+                           GrPixelConfig config,
+                           void* buffer,
+                           size_t rowBytes,
+                           bool invertY) {
+    GrGLenum format;
+    GrGLenum type;
+    if (!this->configToGLFormats(config, false, NULL, &format, &type)) {
+        return false;
+    }
+    size_t bpp = GrBytesPerPixel(config);
+    if (!adjust_pixel_ops_params(target->width(), target->height(), bpp,
+                                 &left, &top, &width, &height,
+                                 const_cast<const void**>(&buffer),
+                                 &rowBytes)) {
+        return false;
+    }
+
+    // resolve the render target if necessary
+    GrGLRenderTarget* tgt = static_cast<GrGLRenderTarget*>(target);
+    GrDrawState::AutoRenderTargetRestore artr;
+    switch (tgt->getResolveType()) {
+        case GrGLRenderTarget::kCantResolve_ResolveType:
+            return false;
+        case GrGLRenderTarget::kAutoResolves_ResolveType:
+            artr.set(this->drawState(), target);
+            this->flushRenderTarget(&GrIRect::EmptyIRect());
+            break;
+        case GrGLRenderTarget::kCanResolve_ResolveType:
+            this->onResolveRenderTarget(tgt);
+            // we don't track the state of the READ FBO ID.
+            GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
+                                    tgt->textureFBOID()));
+            break;
+        default:
+            GrCrash("Unknown resolve type");
+    }
+
+    const GrGLIRect& glvp = tgt->getViewport();
+
+    // the read rect is viewport-relative
+    GrGLIRect readRect;
+    readRect.setRelativeTo(glvp, left, top, width, height);
+    
+    size_t tightRowBytes = bpp * width;
+    if (0 == rowBytes) {
+        rowBytes = tightRowBytes;
+    }
+    size_t readDstRowBytes = tightRowBytes;
+    void* readDst = buffer;
+    
+    // determine if GL can read using the passed rowBytes or if we need
+    // a scratch buffer.
+    SkAutoSMalloc<32 * sizeof(GrColor)> scratch;
+    if (rowBytes != tightRowBytes) {
+        if (this->glCaps().fPackRowLengthSupport) {
+            GrAssert(!(rowBytes % sizeof(GrColor)));
+            GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, rowBytes / sizeof(GrColor)));
+            readDstRowBytes = rowBytes;
+        } else {
+            scratch.reset(tightRowBytes * height);
+            readDst = scratch.get();
+        }
+    }
+    if (!invertY && this->glCaps().fPackFlipYSupport) {
+        GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, 1));
+    }
+    GL_CALL(ReadPixels(readRect.fLeft, readRect.fBottom,
+                       readRect.fWidth, readRect.fHeight,
+                       format, type, readDst));
+    if (readDstRowBytes != tightRowBytes) {
+        GrAssert(this->glCaps().fPackRowLengthSupport);
+        GL_CALL(PixelStorei(GR_GL_PACK_ROW_LENGTH, 0));
+    }
+    if (!invertY && this->glCaps().fPackFlipYSupport) {
+        GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, 0));
+        invertY = true;
+    }
+
+    // now reverse the order of the rows, since GL's are bottom-to-top, but our
+    // API presents top-to-bottom. We must preserve the padding contents. Note
+    // that the above readPixels did not overwrite the padding.
+    if (readDst == buffer) {
+        GrAssert(rowBytes == readDstRowBytes);
+        if (!invertY) {
+            scratch.reset(tightRowBytes);
+            void* tmpRow = scratch.get();
+            // flip y in-place by rows
+            const int halfY = height >> 1;
+            char* top = reinterpret_cast<char*>(buffer);
+            char* bottom = top + (height - 1) * rowBytes;
+            for (int y = 0; y < halfY; y++) {
+                memcpy(tmpRow, top, tightRowBytes);
+                memcpy(top, bottom, tightRowBytes);
+                memcpy(bottom, tmpRow, tightRowBytes);
+                top += rowBytes;
+                bottom -= rowBytes;
+            }
+        }
+    } else {
+        GrAssert(readDst != buffer);        GrAssert(rowBytes != tightRowBytes);
+        // copy from readDst to buffer while flipping y
+        const int halfY = height >> 1;
+        const char* src = reinterpret_cast<const char*>(readDst);
+        char* dst = reinterpret_cast<char*>(buffer);
+        if (!invertY) {
+            dst += (height-1) * rowBytes;
+        }
+        for (int y = 0; y < height; y++) {
+            memcpy(dst, src, tightRowBytes);
+            src += readDstRowBytes;
+            if (invertY) {
+                dst += rowBytes;
+            } else {
+                dst -= rowBytes;
+            }
+        }
+    }
+    return true;
+}
+
+void GrGpuGL::flushRenderTarget(const GrIRect* bound) {
+
+    GrGLRenderTarget* rt =
+        static_cast<GrGLRenderTarget*>(this->drawState()->getRenderTarget());
+    GrAssert(NULL != rt);
+
+    if (fHWDrawState.getRenderTarget() != rt) {
+        GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, rt->renderFBOID()));
+    #if GR_COLLECT_STATS
+        ++fStats.fRenderTargetChngCnt;
+    #endif
+    #if GR_DEBUG
+        GrGLenum status;
+        GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+        if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
+            GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status);
+        }
+    #endif
+        fDirtyFlags.fRenderTargetChanged = true;
+        fHWDrawState.setRenderTarget(rt);
+        const GrGLIRect& vp = rt->getViewport();
+        if (fHWBounds.fViewportRect != vp) {
+            vp.pushToGLViewport(this->glInterface());
+            fHWBounds.fViewportRect = vp;
+        }
+    }
+    if (NULL == bound || !bound->isEmpty()) {
+        rt->flagAsNeedingResolve(bound);
+    }
+}
+
+GrGLenum gPrimitiveType2GLMode[] = {
+    GR_GL_TRIANGLES,
+    GR_GL_TRIANGLE_STRIP,
+    GR_GL_TRIANGLE_FAN,
+    GR_GL_POINTS,
+    GR_GL_LINES,
+    GR_GL_LINE_STRIP
+};
+
+#define SWAP_PER_DRAW 0
+
+#if SWAP_PER_DRAW
+    #if GR_MAC_BUILD
+        #include <AGL/agl.h>
+    #elif GR_WIN32_BUILD
+        void SwapBuf() {
+            DWORD procID = GetCurrentProcessId();
+            HWND hwnd = GetTopWindow(GetDesktopWindow());
+            while(hwnd) {
+                DWORD wndProcID = 0;
+                GetWindowThreadProcessId(hwnd, &wndProcID);
+                if(wndProcID == procID) {
+                    SwapBuffers(GetDC(hwnd));
+                }
+                hwnd = GetNextWindow(hwnd, GW_HWNDNEXT);
+            }
+         }
+    #endif
+#endif
+
+void GrGpuGL::onGpuDrawIndexed(GrPrimitiveType type,
+                               uint32_t startVertex,
+                               uint32_t startIndex,
+                               uint32_t vertexCount,
+                               uint32_t indexCount) {
+    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+
+    GrGLvoid* indices = (GrGLvoid*)(sizeof(uint16_t) * startIndex);
+
+    GrAssert(NULL != fHWGeometryState.fIndexBuffer);
+    GrAssert(NULL != fHWGeometryState.fVertexBuffer);
+
+    // our setupGeometry better have adjusted this to zero since
+    // DrawElements always draws from the begining of the arrays for idx 0.
+    GrAssert(0 == startVertex);
+
+    GL_CALL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
+                         GR_GL_UNSIGNED_SHORT, indices));
+#if SWAP_PER_DRAW
+    glFlush();
+    #if GR_MAC_BUILD
+        aglSwapBuffers(aglGetCurrentContext());
+        int set_a_break_pt_here = 9;
+        aglSwapBuffers(aglGetCurrentContext());
+    #elif GR_WIN32_BUILD
+        SwapBuf();
+        int set_a_break_pt_here = 9;
+        SwapBuf();
+    #endif
+#endif
+}
+
+void GrGpuGL::onGpuDrawNonIndexed(GrPrimitiveType type,
+                                  uint32_t startVertex,
+                                  uint32_t vertexCount) {
+    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+
+    GrAssert(NULL != fHWGeometryState.fVertexBuffer);
+
+    // our setupGeometry better have adjusted this to zero.
+    // DrawElements doesn't take an offset so we always adjus the startVertex.
+    GrAssert(0 == startVertex);
+
+    // pass 0 for parameter first. We have to adjust gl*Pointer() to
+    // account for startVertex in the DrawElements case. So we always
+    // rely on setupGeometry to have accounted for startVertex.
+    GL_CALL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
+#if SWAP_PER_DRAW
+    glFlush();
+    #if GR_MAC_BUILD
+        aglSwapBuffers(aglGetCurrentContext());
+        int set_a_break_pt_here = 9;
+        aglSwapBuffers(aglGetCurrentContext());
+    #elif GR_WIN32_BUILD
+        SwapBuf();
+        int set_a_break_pt_here = 9;
+        SwapBuf();
+    #endif
+#endif
+}
+
+void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) {
+
+    GrGLRenderTarget* rt = static_cast<GrGLRenderTarget*>(target);
+
+    if (rt->needsResolve()) {
+        GrAssert(GLCaps::kNone_MSFBO != fGLCaps.fMSFBOType);
+        GrAssert(rt->textureFBOID() != rt->renderFBOID());
+        GL_CALL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER,
+                                rt->renderFBOID()));
+        GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER,
+                                rt->textureFBOID()));
+    #if GR_COLLECT_STATS
+        ++fStats.fRenderTargetChngCnt;
+    #endif
+        // make sure we go through flushRenderTarget() since we've modified
+        // the bound DRAW FBO ID.
+        fHWDrawState.setRenderTarget(NULL);
+        const GrGLIRect& vp = rt->getViewport();
+        const GrIRect dirtyRect = rt->getResolveRect();
+        GrGLIRect r;
+        r.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop, 
+                        dirtyRect.width(), dirtyRect.height());
+
+        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,
+                            r.fWidth, r.fHeight));
+            GL_CALL(ResolveMultisampleFramebuffer());
+            fHWBounds.fScissorRect.invalidate();
+            fHWBounds.fScissorEnabled = true;
+        } else {
+            if (GLCaps::kDesktopARB_MSFBO != fGLCaps.fMSFBOType) {
+                // this respects the scissor during the blit, so disable it.
+                GrAssert(GLCaps::kDesktopEXT_MSFBO == fGLCaps.fMSFBOType);
+                this->flushScissor(NULL);
+            }
+            int right = r.fLeft + r.fWidth;
+            int top = r.fBottom + r.fHeight;
+            GL_CALL(BlitFramebuffer(r.fLeft, r.fBottom, right, top,
+                                    r.fLeft, r.fBottom, right, top,
+                                    GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
+        }
+        rt->flagAsResolved();
+    }
+}
+
+static const GrGLenum grToGLStencilFunc[] = {
+    GR_GL_ALWAYS,           // kAlways_StencilFunc
+    GR_GL_NEVER,            // kNever_StencilFunc
+    GR_GL_GREATER,          // kGreater_StencilFunc
+    GR_GL_GEQUAL,           // kGEqual_StencilFunc
+    GR_GL_LESS,             // kLess_StencilFunc
+    GR_GL_LEQUAL,           // kLEqual_StencilFunc,
+    GR_GL_EQUAL,            // kEqual_StencilFunc,
+    GR_GL_NOTEQUAL,         // kNotEqual_StencilFunc,
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilFunc) == kBasicStencilFuncCount);
+GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+GR_STATIC_ASSERT(1 == kNever_StencilFunc);
+GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
+GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
+GR_STATIC_ASSERT(4 == kLess_StencilFunc);
+GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
+GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
+GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
+
+static const GrGLenum grToGLStencilOp[] = {
+    GR_GL_KEEP,        // kKeep_StencilOp
+    GR_GL_REPLACE,     // kReplace_StencilOp
+    GR_GL_INCR_WRAP,   // kIncWrap_StencilOp
+    GR_GL_INCR,        // kIncClamp_StencilOp
+    GR_GL_DECR_WRAP,   // kDecWrap_StencilOp
+    GR_GL_DECR,        // kDecClamp_StencilOp
+    GR_GL_ZERO,        // kZero_StencilOp
+    GR_GL_INVERT,      // kInvert_StencilOp
+};
+GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilOp) == kStencilOpCount);
+GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+GR_STATIC_ASSERT(1 == kReplace_StencilOp);
+GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
+GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
+GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
+GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
+GR_STATIC_ASSERT(6 == kZero_StencilOp);
+GR_STATIC_ASSERT(7 == kInvert_StencilOp);
+
+void GrGpuGL::flushStencil() {
+    const GrDrawState& drawState = this->getDrawState();
+
+    const GrStencilSettings* settings = &drawState.getStencil();
+
+    // use stencil for clipping if clipping is enabled and the clip
+    // has been written into the stencil.
+    bool stencilClip = fClipInStencil && drawState.isClipState();
+    bool drawClipToStencil =
+        drawState.isStateFlagEnabled(kModifyStencilClip_StateBit);
+    bool stencilChange = (fHWDrawState.getStencil() != *settings) ||
+                         (fHWStencilClip != stencilClip) ||
+                         (fHWDrawState.isStateFlagEnabled(kModifyStencilClip_StateBit) !=
+                          drawClipToStencil);
+
+    if (stencilChange) {
+
+        // we can't simultaneously perform stencil-clipping and 
+        // modify the stencil clip
+        GrAssert(!stencilClip || !drawClipToStencil);
+
+        if (settings->isDisabled()) {
+            if (stencilClip) {
+                settings = &gClipStencilSettings;
+            }
+        }
+
+        if (settings->isDisabled()) {
+            GL_CALL(Disable(GR_GL_STENCIL_TEST));
+        } else {
+            GL_CALL(Enable(GR_GL_STENCIL_TEST));
+    #if GR_DEBUG
+            if (!this->getCaps().fStencilWrapOpsSupport) {
+                GrAssert(settings->frontPassOp() != kIncWrap_StencilOp);
+                GrAssert(settings->frontPassOp() != kDecWrap_StencilOp);
+                GrAssert(settings->frontFailOp() != kIncWrap_StencilOp);
+                GrAssert(settings->backFailOp() != kDecWrap_StencilOp);
+                GrAssert(settings->backPassOp() != kIncWrap_StencilOp);
+                GrAssert(settings->backPassOp() != kDecWrap_StencilOp);
+                GrAssert(settings->backFailOp() != kIncWrap_StencilOp);
+                GrAssert(settings->frontFailOp() != kDecWrap_StencilOp);
+            }
+    #endif
+            int stencilBits = 0;
+            GrStencilBuffer* stencilBuffer =
+                drawState.getRenderTarget()->getStencilBuffer();
+            if (NULL != stencilBuffer) {
+                stencilBits = stencilBuffer->bits();
+            }
+            // TODO: dynamically attach a stencil buffer
+            GrAssert(stencilBits || settings->isDisabled());
+
+            GrGLuint clipStencilMask = 0;
+            GrGLuint userStencilMask = ~0;
+            if (stencilBits > 0) {
+                clipStencilMask =  1 << (stencilBits - 1);
+                userStencilMask = clipStencilMask - 1;
+            }
+
+            unsigned int frontRef  = settings->frontFuncRef();
+            unsigned int frontMask = settings->frontFuncMask();
+            unsigned int frontWriteMask = settings->frontWriteMask();
+            GrGLenum frontFunc;
+
+            if (drawClipToStencil) {
+                GrAssert(settings->frontFunc() < kBasicStencilFuncCount);
+                frontFunc = grToGLStencilFunc[settings->frontFunc()];
+            } else {
+                frontFunc = grToGLStencilFunc[ConvertStencilFunc(
+                        stencilClip, settings->frontFunc())];
+
+                ConvertStencilFuncAndMask(settings->frontFunc(),
+                                          stencilClip,
+                                          clipStencilMask,
+                                          userStencilMask,
+                                          &frontRef,
+                                          &frontMask);
+                frontWriteMask &= userStencilMask;
+            }
+            GrAssert((size_t)
+                settings->frontFailOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+            GrAssert((size_t)
+                settings->frontPassOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+            GrAssert((size_t)
+                settings->backFailOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+            GrAssert((size_t)
+                settings->backPassOp() < GR_ARRAY_COUNT(grToGLStencilOp));
+            if (this->getCaps().fTwoSidedStencilSupport) {
+                GrGLenum backFunc;
+
+                unsigned int backRef  = settings->backFuncRef();
+                unsigned int backMask = settings->backFuncMask();
+                unsigned int backWriteMask = settings->backWriteMask();
+
+
+                if (drawClipToStencil) {
+                    GrAssert(settings->backFunc() < kBasicStencilFuncCount);
+                    backFunc = grToGLStencilFunc[settings->backFunc()];
+                } else {
+                    backFunc = grToGLStencilFunc[ConvertStencilFunc(
+                        stencilClip, settings->backFunc())];
+                    ConvertStencilFuncAndMask(settings->backFunc(),
+                                              stencilClip,
+                                              clipStencilMask,
+                                              userStencilMask,
+                                              &backRef,
+                                              &backMask);
+                    backWriteMask &= userStencilMask;
+                }
+
+                GL_CALL(StencilFuncSeparate(GR_GL_FRONT, frontFunc,
+                                            frontRef, frontMask));
+                GL_CALL(StencilMaskSeparate(GR_GL_FRONT, frontWriteMask));
+                GL_CALL(StencilFuncSeparate(GR_GL_BACK, backFunc,
+                                            backRef, backMask));
+                GL_CALL(StencilMaskSeparate(GR_GL_BACK, backWriteMask));
+                GL_CALL(StencilOpSeparate(GR_GL_FRONT,
+                                    grToGLStencilOp[settings->frontFailOp()],
+                                    grToGLStencilOp[settings->frontPassOp()],
+                                    grToGLStencilOp[settings->frontPassOp()]));
+
+                GL_CALL(StencilOpSeparate(GR_GL_BACK,
+                                    grToGLStencilOp[settings->backFailOp()],
+                                    grToGLStencilOp[settings->backPassOp()],
+                                    grToGLStencilOp[settings->backPassOp()]));
+            } else {
+                GL_CALL(StencilFunc(frontFunc, frontRef, frontMask));
+                GL_CALL(StencilMask(frontWriteMask));
+                GL_CALL(StencilOp(grToGLStencilOp[settings->frontFailOp()],
+                                grToGLStencilOp[settings->frontPassOp()],
+                                grToGLStencilOp[settings->frontPassOp()]));
+            }
+        }
+        *fHWDrawState.stencil() = *settings;
+        fHWStencilClip = stencilClip;
+    }
+}
+
+void GrGpuGL::flushAAState(GrPrimitiveType type) {
+    const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        // ES doesn't support toggling GL_MULTISAMPLE and doesn't have
+        // smooth lines.
+
+        // we prefer smooth lines over multisampled lines
+        // msaa should be disabled if drawing smooth lines.
+        if (GrIsPrimTypeLines(type)) {
+            bool smooth = this->willUseHWAALines();
+            if (!fHWAAState.fSmoothLineEnabled && smooth) {
+                GL_CALL(Enable(GR_GL_LINE_SMOOTH));
+                fHWAAState.fSmoothLineEnabled = true;
+            } else if (fHWAAState.fSmoothLineEnabled && !smooth) {
+                GL_CALL(Disable(GR_GL_LINE_SMOOTH));
+                fHWAAState.fSmoothLineEnabled = false;
+            }
+            if (rt->isMultisampled() && 
+                fHWAAState.fMSAAEnabled) {
+                GL_CALL(Disable(GR_GL_MULTISAMPLE));
+                fHWAAState.fMSAAEnabled = false;
+            }
+        } else if (rt->isMultisampled() &&
+                   this->getDrawState().isHWAntialiasState() !=
+                   fHWAAState.fMSAAEnabled) {
+            if (fHWAAState.fMSAAEnabled) {
+                GL_CALL(Disable(GR_GL_MULTISAMPLE));
+                fHWAAState.fMSAAEnabled = false;
+            } else {
+                GL_CALL(Enable(GR_GL_MULTISAMPLE));
+                fHWAAState.fMSAAEnabled = true;
+            }
+        }
+    }
+}
+
+void GrGpuGL::flushBlend(GrPrimitiveType type,
+                         GrBlendCoeff srcCoeff,
+                         GrBlendCoeff dstCoeff) {
+    if (GrIsPrimTypeLines(type) && this->willUseHWAALines()) {
+        if (fHWBlendDisabled) {
+            GL_CALL(Enable(GR_GL_BLEND));
+            fHWBlendDisabled = false;
+        }
+        if (kSA_BlendCoeff != fHWDrawState.getSrcBlendCoeff() ||
+            kISA_BlendCoeff != fHWDrawState.getDstBlendCoeff()) {
+            GL_CALL(BlendFunc(gXfermodeCoeff2Blend[kSA_BlendCoeff],
+                              gXfermodeCoeff2Blend[kISA_BlendCoeff]));
+            fHWDrawState.setBlendFunc(kSA_BlendCoeff, kISA_BlendCoeff);
+        }
+    } else {
+        // any optimization to disable blending should
+        // have already been applied and tweaked the coeffs
+        // to (1, 0).
+        bool blendOff = kOne_BlendCoeff == srcCoeff &&
+                        kZero_BlendCoeff == dstCoeff;
+        if (fHWBlendDisabled != blendOff) {
+            if (blendOff) {
+                GL_CALL(Disable(GR_GL_BLEND));
+            } else {
+                GL_CALL(Enable(GR_GL_BLEND));
+            }
+            fHWBlendDisabled = blendOff;
+        }
+        if (!blendOff) {
+            if (fHWDrawState.getSrcBlendCoeff() != srcCoeff ||
+                fHWDrawState.getDstBlendCoeff() != dstCoeff) {
+                GL_CALL(BlendFunc(gXfermodeCoeff2Blend[srcCoeff],
+                                  gXfermodeCoeff2Blend[dstCoeff]));
+                fHWDrawState.setBlendFunc(srcCoeff, dstCoeff);
+            }
+            GrColor blendConst = fCurrDrawState.getBlendConstant();
+            if ((BlendCoeffReferencesConstant(srcCoeff) ||
+                 BlendCoeffReferencesConstant(dstCoeff)) &&
+                fHWDrawState.getBlendConstant() != blendConst) {
+
+                float c[] = {
+                    GrColorUnpackR(blendConst) / 255.f,
+                    GrColorUnpackG(blendConst) / 255.f,
+                    GrColorUnpackB(blendConst) / 255.f,
+                    GrColorUnpackA(blendConst) / 255.f
+                };
+                GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
+                fHWDrawState.setBlendConstant(blendConst);
+            }
+        }
+    }
+}
+
+namespace {
+
+unsigned gr_to_gl_filter(GrSamplerState::Filter filter) {
+    switch (filter) {
+        case GrSamplerState::kBilinear_Filter:
+        case GrSamplerState::k4x4Downsample_Filter:
+            return GR_GL_LINEAR;
+        case GrSamplerState::kNearest_Filter:
+        case GrSamplerState::kConvolution_Filter:
+            return GR_GL_NEAREST;
+        default:
+            GrAssert(!"Unknown filter type");
+            return GR_GL_LINEAR;
+    }
+}
+
+const GrGLenum* get_swizzle(GrPixelConfig config,
+                            const GrSamplerState& sampler) {
+    if (GrPixelConfigIsAlphaOnly(config)) {
+        static const GrGLenum gAlphaSmear[] = { GR_GL_ALPHA, GR_GL_ALPHA,
+                                                GR_GL_ALPHA, GR_GL_ALPHA };
+        return gAlphaSmear;
+    } else if (sampler.swapsRAndB()) {
+        static const GrGLenum gRedBlueSwap[] = { GR_GL_BLUE, GR_GL_GREEN,
+                                                 GR_GL_RED,  GR_GL_ALPHA };
+        return gRedBlueSwap;
+    } else {
+        static const GrGLenum gStraight[] = { GR_GL_RED, GR_GL_GREEN,
+                                              GR_GL_BLUE,  GR_GL_ALPHA };
+        return gStraight;
+    }
+}
+
+void set_tex_swizzle(GrGLenum swizzle[4], const GrGLInterface* gl) {
+    // should add texparameteri to interface to make 1 instead of 4 calls here
+    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+                                 GR_GL_TEXTURE_SWIZZLE_R,
+                                 swizzle[0]));
+    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+                                 GR_GL_TEXTURE_SWIZZLE_G,
+                                 swizzle[1]));
+    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+                                 GR_GL_TEXTURE_SWIZZLE_B,
+                                 swizzle[2]));
+    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
+                                 GR_GL_TEXTURE_SWIZZLE_A,
+                                 swizzle[3]));
+}
+}
+
+bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
+
+    GrDrawState* drawState = this->drawState();
+    // GrGpu::setupClipAndFlushState should have already checked this
+    // and bailed if not true.
+    GrAssert(NULL != drawState->getRenderTarget());
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        // bind texture and set sampler state
+        if (this->isStageEnabled(s)) {
+            GrGLTexture* nextTexture = 
+                static_cast<GrGLTexture*>(drawState->getTexture(s));
+
+            // true for now, but maybe not with GrEffect.
+            GrAssert(NULL != nextTexture);
+            // if we created a rt/tex and rendered to it without using a
+            // texture and now we're texuring from the rt it will still be
+            // the last bound texture, but it needs resolving. So keep this
+            // out of the "last != next" check.
+            GrGLRenderTarget* texRT = 
+                static_cast<GrGLRenderTarget*>(nextTexture->asRenderTarget());
+            if (NULL != texRT) {
+                this->onResolveRenderTarget(texRT);
+            }
+
+            if (fHWDrawState.getTexture(s) != nextTexture) {
+                setTextureUnit(s);
+                GL_CALL(BindTexture(GR_GL_TEXTURE_2D, nextTexture->textureID()));
+            #if GR_COLLECT_STATS
+                ++fStats.fTextureChngCnt;
+            #endif
+                //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
+                fHWDrawState.setTexture(s, nextTexture);
+                // The texture matrix has to compensate for texture width/height
+                // and NPOT-embedded-in-POT
+                fDirtyFlags.fTextureChangedMask |= (1 << s);
+            }
+
+            const GrSamplerState& sampler = drawState->getSampler(s);
+            ResetTimestamp timestamp;
+            const GrGLTexture::TexParams& oldTexParams =
+                                    nextTexture->getCachedTexParams(&timestamp);
+            bool setAll = timestamp < this->getResetTimestamp();
+            GrGLTexture::TexParams newTexParams;
+
+            newTexParams.fFilter = gr_to_gl_filter(sampler.getFilter());
+
+            const GrGLenum* wraps =  GrGLTexture::WrapMode2GLWrap();
+            newTexParams.fWrapS = wraps[sampler.getWrapX()];
+            newTexParams.fWrapT = wraps[sampler.getWrapY()];
+            memcpy(newTexParams.fSwizzleRGBA,
+                   get_swizzle(nextTexture->config(), sampler),
+                   sizeof(newTexParams.fSwizzleRGBA));
+            if (setAll || newTexParams.fFilter != oldTexParams.fFilter) {
+                setTextureUnit(s);
+                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                                        GR_GL_TEXTURE_MAG_FILTER,
+                                        newTexParams.fFilter));
+                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                                        GR_GL_TEXTURE_MIN_FILTER,
+                                        newTexParams.fFilter));
+            }
+            if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) {
+                setTextureUnit(s);
+                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                                        GR_GL_TEXTURE_WRAP_S,
+                                        newTexParams.fWrapS));
+            }
+            if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) {
+                setTextureUnit(s);
+                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                                        GR_GL_TEXTURE_WRAP_T,
+                                        newTexParams.fWrapT));
+            }
+            if (this->glCaps().fTextureSwizzleSupport &&
+                (setAll ||
+                 memcmp(newTexParams.fSwizzleRGBA,
+                        oldTexParams.fSwizzleRGBA,
+                        sizeof(newTexParams.fSwizzleRGBA)))) {
+                setTextureUnit(s);
+                set_tex_swizzle(newTexParams.fSwizzleRGBA,
+                                this->glInterface());
+            }
+            nextTexture->setCachedTexParams(newTexParams,
+                                            this->getResetTimestamp());
+        }
+    }
+
+    GrIRect* rect = NULL;
+    GrIRect clipBounds;
+    if (drawState->isClipState() &&
+        fClip.hasConservativeBounds()) {
+        fClip.getConservativeBounds().roundOut(&clipBounds);
+        rect = &clipBounds;
+    }
+    this->flushRenderTarget(rect);
+    this->flushAAState(type);
+    
+    if (drawState->isDitherState() != fHWDrawState.isDitherState()) {
+        if (drawState->isDitherState()) {
+            GL_CALL(Enable(GR_GL_DITHER));
+        } else {
+            GL_CALL(Disable(GR_GL_DITHER));
+        }
+    }
+
+    if (drawState->isColorWriteDisabled() !=
+        fHWDrawState.isColorWriteDisabled()) {
+        GrGLenum mask;
+        if (drawState->isColorWriteDisabled()) {
+            mask = GR_GL_FALSE;
+        } else {
+            mask = GR_GL_TRUE;
+        }
+        GL_CALL(ColorMask(mask, mask, mask, mask));
+    }
+
+    if (fHWDrawState.getDrawFace() != drawState->getDrawFace()) {
+        switch (fCurrDrawState.getDrawFace()) {
+            case GrDrawState::kCCW_DrawFace:
+                GL_CALL(Enable(GR_GL_CULL_FACE));
+                GL_CALL(CullFace(GR_GL_BACK));
+                break;
+            case GrDrawState::kCW_DrawFace:
+                GL_CALL(Enable(GR_GL_CULL_FACE));
+                GL_CALL(CullFace(GR_GL_FRONT));
+                break;
+            case GrDrawState::kBoth_DrawFace:
+                GL_CALL(Disable(GR_GL_CULL_FACE));
+                break;
+            default:
+                GrCrash("Unknown draw face.");
+        }
+        fHWDrawState.setDrawFace(drawState->getDrawFace());
+    }
+
+#if GR_DEBUG
+    // check for circular rendering
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrAssert(!this->isStageEnabled(s) ||
+                 NULL == drawState->getRenderTarget() ||
+                 NULL == drawState->getTexture(s) ||
+                 drawState->getTexture(s)->asRenderTarget() !=
+                    drawState->getRenderTarget());
+    }
+#endif
+
+    this->flushStencil();
+
+    // This copy must happen after flushStencil() is called. flushStencil()
+    // relies on detecting when the kModifyStencilClip_StateBit state has
+    // changed since the last draw.
+    fHWDrawState.copyStateFlags(*drawState);
+    return true;
+}
+
+void GrGpuGL::notifyVertexBufferBind(const GrGLVertexBuffer* buffer) {
+    if (fHWGeometryState.fVertexBuffer != buffer) {
+        fHWGeometryState.fArrayPtrsDirty = true;
+        fHWGeometryState.fVertexBuffer = buffer;
+    }
+}
+
+void GrGpuGL::notifyVertexBufferDelete(const GrGLVertexBuffer* buffer) {
+    if (fHWGeometryState.fVertexBuffer == buffer) {
+        // deleting bound buffer does implied bind to 0
+        fHWGeometryState.fVertexBuffer = NULL;
+        fHWGeometryState.fArrayPtrsDirty = true;
+    }
+}
+
+void GrGpuGL::notifyIndexBufferBind(const GrGLIndexBuffer* buffer) {
+    fHWGeometryState.fIndexBuffer = buffer;
+}
+
+void GrGpuGL::notifyIndexBufferDelete(const GrGLIndexBuffer* buffer) {
+    if (fHWGeometryState.fIndexBuffer == buffer) {
+        // deleting bound buffer does implied bind to 0
+        fHWGeometryState.fIndexBuffer = NULL;
+    }
+}
+
+void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
+    GrAssert(NULL != renderTarget);
+    GrDrawState* drawState = this->drawState();
+    if (drawState->getRenderTarget() == renderTarget) {
+        drawState->setRenderTarget(NULL);
+    }
+    if (fHWDrawState.getRenderTarget() == renderTarget) {
+        fHWDrawState.setRenderTarget(NULL);
+    }
+}
+
+void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrDrawState* drawState = this->drawState();
+        if (drawState->getTexture(s) == texture) {
+            fCurrDrawState.setTexture(s, NULL);
+        }
+        if (fHWDrawState.getTexture(s) == texture) {
+            // deleting bound texture does implied bind to 0
+            fHWDrawState.setTexture(s, NULL);
+       }
+    }
+}
+
+bool GrGpuGL::configToGLFormats(GrPixelConfig config,
+                                bool getSizedInternalFormat,
+                                GrGLenum* internalFormat,
+                                GrGLenum* externalFormat,
+                                GrGLenum* externalType) {
+    GrGLenum dontCare;
+    if (NULL == internalFormat) {
+        internalFormat = &dontCare;
+    }
+    if (NULL == externalFormat) {
+        externalFormat = &dontCare;
+    }
+    if (NULL == externalType) {
+        externalType = &dontCare;
+    }
+
+    switch (config) {
+        case kRGBA_8888_PM_GrPixelConfig:
+        case kRGBA_8888_UPM_GrPixelConfig:
+            *internalFormat = GR_GL_RGBA;
+            *externalFormat = GR_GL_RGBA;
+            if (getSizedInternalFormat) {
+                *internalFormat = GR_GL_RGBA8;
+            } else {
+                *internalFormat = GR_GL_RGBA;
+            }
+            *externalType = GR_GL_UNSIGNED_BYTE;
+            break;
+        case kBGRA_8888_PM_GrPixelConfig:
+        case kBGRA_8888_UPM_GrPixelConfig:
+            if (!fGLCaps.fBGRAFormatSupport) {
+                return false;
+            }
+            if (fGLCaps.fBGRAIsInternalFormat) {
+                if (getSizedInternalFormat) {
+                    *internalFormat = GR_GL_BGRA8;
+                } else {
+                    *internalFormat = GR_GL_BGRA;
+                }
+            } else {
+                if (getSizedInternalFormat) {
+                    *internalFormat = GR_GL_RGBA8;
+                } else {
+                    *internalFormat = GR_GL_RGBA;
+                }
+            }
+            *externalFormat = GR_GL_BGRA;
+            *externalType = GR_GL_UNSIGNED_BYTE;
+            break;
+        case kRGB_565_GrPixelConfig:
+            *internalFormat = GR_GL_RGB;
+            *externalFormat = GR_GL_RGB;
+            if (getSizedInternalFormat) {
+                if (this->glBinding() == kDesktop_GrGLBinding) {
+                    return false;
+                } else {
+                    *internalFormat = GR_GL_RGB565;
+                }
+            } else {
+                *internalFormat = GR_GL_RGB;
+            }
+            *externalType = GR_GL_UNSIGNED_SHORT_5_6_5;
+            break;
+        case kRGBA_4444_GrPixelConfig:
+            *internalFormat = GR_GL_RGBA;
+            *externalFormat = GR_GL_RGBA;
+            if (getSizedInternalFormat) {
+                *internalFormat = GR_GL_RGBA4;
+            } else {
+                *internalFormat = GR_GL_RGBA;
+            }
+            *externalType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
+            break;
+        case kIndex_8_GrPixelConfig:
+            if (this->getCaps().f8BitPaletteSupport) {
+                *internalFormat = GR_GL_PALETTE8_RGBA8;
+                // glCompressedTexImage doesn't take external params
+                *externalFormat = GR_GL_PALETTE8_RGBA8;
+                // no sized/unsized internal format distinction here
+                *internalFormat = GR_GL_PALETTE8_RGBA8;
+                // unused with CompressedTexImage
+                *externalType = GR_GL_UNSIGNED_BYTE;
+            } else {
+                return false;
+            }
+            break;
+        case kAlpha_8_GrPixelConfig:
+            *internalFormat = GR_GL_ALPHA;
+            *externalFormat = GR_GL_ALPHA;
+            if (getSizedInternalFormat) {
+                *internalFormat = GR_GL_ALPHA8;
+            } else {
+                *internalFormat = GR_GL_ALPHA;
+            }
+            *externalType = GR_GL_UNSIGNED_BYTE;
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+void GrGpuGL::setTextureUnit(int unit) {
+    GrAssert(unit >= 0 && unit < GrDrawState::kNumStages);
+    if (fActiveTextureUnitIdx != unit) {
+        GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + unit));
+        fActiveTextureUnitIdx = unit;
+    }
+}
+
+void GrGpuGL::setSpareTextureUnit() {
+    if (fActiveTextureUnitIdx != (GR_GL_TEXTURE0 + SPARE_TEX_UNIT)) {
+        GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + SPARE_TEX_UNIT));
+        fActiveTextureUnitIdx = SPARE_TEX_UNIT;
+    }
+}
+
+void GrGpuGL::resetDirtyFlags() {
+    Gr_bzero(&fDirtyFlags, sizeof(fDirtyFlags));
+}
+
+void GrGpuGL::setBuffers(bool indexed,
+                         int* extraVertexOffset,
+                         int* extraIndexOffset) {
+
+    GrAssert(NULL != extraVertexOffset);
+
+    const GeometryPoolState& geoPoolState = this->getGeomPoolState();
+
+    GrGLVertexBuffer* vbuf;
+    switch (this->getGeomSrc().fVertexSrc) {
+    case kBuffer_GeometrySrcType:
+        *extraVertexOffset = 0;
+        vbuf = (GrGLVertexBuffer*) this->getGeomSrc().fVertexBuffer;
+        break;
+    case kArray_GeometrySrcType:
+    case kReserved_GeometrySrcType:
+        this->finalizeReservedVertices();
+        *extraVertexOffset = geoPoolState.fPoolStartVertex;
+        vbuf = (GrGLVertexBuffer*) geoPoolState.fPoolVertexBuffer;
+        break;
+    default:
+        vbuf = NULL; // suppress warning
+        GrCrash("Unknown geometry src type!");
+    }
+
+    GrAssert(NULL != vbuf);
+    GrAssert(!vbuf->isLocked());
+    if (fHWGeometryState.fVertexBuffer != vbuf) {
+        GL_CALL(BindBuffer(GR_GL_ARRAY_BUFFER, vbuf->bufferID()));
+        fHWGeometryState.fArrayPtrsDirty = true;
+        fHWGeometryState.fVertexBuffer = vbuf;
+    }
+
+    if (indexed) {
+        GrAssert(NULL != extraIndexOffset);
+
+        GrGLIndexBuffer* ibuf;
+        switch (this->getGeomSrc().fIndexSrc) {
+        case kBuffer_GeometrySrcType:
+            *extraIndexOffset = 0;
+            ibuf = (GrGLIndexBuffer*)this->getGeomSrc().fIndexBuffer;
+            break;
+        case kArray_GeometrySrcType:
+        case kReserved_GeometrySrcType:
+            this->finalizeReservedIndices();
+            *extraIndexOffset = geoPoolState.fPoolStartIndex;
+            ibuf = (GrGLIndexBuffer*) geoPoolState.fPoolIndexBuffer;
+            break;
+        default:
+            ibuf = NULL; // suppress warning
+            GrCrash("Unknown geometry src type!");
+        }
+
+        GrAssert(NULL != ibuf);
+        GrAssert(!ibuf->isLocked());
+        if (fHWGeometryState.fIndexBuffer != ibuf) {
+            GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, ibuf->bufferID()));
+            fHWGeometryState.fIndexBuffer = ibuf;
+        }
+    }
+}
+
+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(fGLCaps.fMaxFragmentUniformVectors - 8,
+                     GrDrawState::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].fFormat.fStencilBits,
+                 fStencilFormats[i].fFormat.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]);
+    GrPrintf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
+    GrPrintf("Support RGBA8 Render Buffer: %s\n",
+             (fRGBA8RenderbufferSupport ? "YES": "NO"));
+    GrPrintf("BGRA is an internal format: %s\n",
+             (fBGRAIsInternalFormat ? "YES": "NO"));
+    GrPrintf("Support texture swizzle: %s\n",
+             (fTextureSwizzleSupport ? "YES": "NO"));
+    GrPrintf("Unpack Row length support: %s\n",
+             (fUnpackRowLengthSupport ? "YES": "NO"));
+    GrPrintf("Unpack Flip Y support: %s\n",
+             (fUnpackFlipYSupport ? "YES": "NO"));
+    GrPrintf("Pack Row length support: %s\n",
+             (fPackRowLengthSupport ? "YES": "NO"));
+    GrPrintf("Pack Flip Y support: %s\n",
+             (fPackFlipYSupport ? "YES": "NO"));
+}
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
new file mode 100644
index 0000000..2e7e90b
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL.h
@@ -0,0 +1,365 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGpuGL_DEFINED
+#define GrGpuGL_DEFINED
+
+#include "../GrDrawState.h"
+#include "../GrGpu.h"
+#include "GrGLContextInfo.h"
+#include "GrGLIndexBuffer.h"
+#include "GrGLIRect.h"
+#include "GrGLStencilBuffer.h"
+#include "GrGLTexture.h"
+#include "GrGLVertexBuffer.h"
+
+class GrGpuGL : public GrGpu {
+public:
+    virtual ~GrGpuGL();
+
+    const GrGLInterface* glInterface() const { 
+        return fGLContextInfo.interface();
+    }
+    GrGLBinding glBinding() const { return fGLContextInfo.binding(); }
+    GrGLVersion glVersion() const { return fGLContextInfo.version(); }
+    GrGLSLGeneration glslGeneration() const {
+        return fGLContextInfo.glslGeneration();
+    }
+
+    // GrGpu overrides
+    virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
+                                                            const SK_OVERRIDE;
+    virtual GrPixelConfig preferredWritePixelsConfig(GrPixelConfig config)
+                                                            const SK_OVERRIDE;
+    virtual bool readPixelsWillPayForYFlip(
+                                    GrRenderTarget* renderTarget,
+                                    int left, int top,
+                                    int width, int height,
+                                    GrPixelConfig config,
+                                    size_t rowBytes) const SK_OVERRIDE;
+    virtual bool fullReadPixelsIsFasterThanPartial() const SK_OVERRIDE;
+
+protected:
+    GrGpuGL(const GrGLContextInfo& ctxInfo);
+
+    struct GLCaps {
+        GLCaps()
+            // make defaults be the most restrictive
+            : fStencilFormats(8) // prealloc space for    stencil formats
+            , fMSFBOType(kNone_MSFBO)
+            , fMaxFragmentUniformVectors(0)
+            , fRGBA8RenderbufferSupport(false)
+            , fBGRAFormatSupport(false)
+            , fBGRAIsInternalFormat(false)
+            , fTextureSwizzleSupport(false)
+            , fUnpackRowLengthSupport(false)
+            , fUnpackFlipYSupport(false)
+            , fPackRowLengthSupport(false)
+            , fPackFlipYSupport(false)
+            , fTextureUsageSupport(false)
+            , fTexStorageSupport(false) {
+            memset(&fVerifiedColorAttachmentConfigs, 0,
+                   sizeof(fVerifiedColorAttachmentConfigs));
+        }
+
+        // Call to note that a color config has been verified as a valid
+        // color attachment. This may save future calls to
+        // CheckFramebufferStatus
+        void markConfigAsValidColorAttachment(GrPixelConfig config);
+
+        // Call to check whether a config has been verified as a valid color
+        // attachment.
+        bool isConfigVerifiedColorAttachment(GrPixelConfig config) const;
+
+        // Call to note that a color config / stencil format pair passed
+        // FBO status check. We may skip calling CheckFramebufferStatus for
+        // this combination in the future.
+        void markColorConfigAndStencilFormatAsVerified(
+                        GrPixelConfig config,
+                        const GrGLStencilBuffer::Format& format);
+
+        // Call to check whether color config / stencil format pair has already
+        // passed FBO status check.
+        bool isColorConfigAndStencilFormatVerified(
+                        GrPixelConfig config,
+                        const GrGLStencilBuffer::Format& format) const;
+
+        void print() const;
+
+        struct StencilFormat {
+            GrGLStencilBuffer::Format fFormat;
+            uint32_t fVerifiedColorConfigs[(kGrPixelConfigCount  + 31) / 32];
+        };
+
+        SkTArray<StencilFormat, 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;
+
+        // The maximum number of fragment uniform vectors (GLES has min. 16).
+        int fMaxFragmentUniformVectors;
+
+        // ES requires an extension to support RGBA8 in RenderBufferStorage
+        bool fRGBA8RenderbufferSupport;
+
+        // Is GL_BGRA supported
+        bool fBGRAFormatSupport;
+
+        // Depending on the ES extensions present the BGRA external format may
+        // correspond either a BGRA or RGBA internalFormat. On desktop GL it is
+        // RGBA
+        bool fBGRAIsInternalFormat;
+
+        // GL_ARB_texture_swizzle support
+        bool fTextureSwizzleSupport;
+    
+        // Is there support for GL_UNPACK_ROW_LENGTH
+        bool fUnpackRowLengthSupport;
+
+        // Is there support for GL_UNPACK_FLIP_Y
+        bool fUnpackFlipYSupport;
+
+        // Is there support for GL_PACK_ROW_LENGTH
+        bool fPackRowLengthSupport;
+
+        // Is there support for GL_PACK_REVERSE_ROW_ORDER
+        bool fPackFlipYSupport;
+
+        // Is there support for texture parameter GL_TEXTURE_USAGE
+        bool fTextureUsageSupport;
+
+        // Is there support for glTexStorage
+        bool fTexStorageSupport;
+
+    private:
+        uint32_t fVerifiedColorAttachmentConfigs[(kGrPixelConfigCount  + 31) / 32];
+    } fGLCaps;
+
+    struct {
+        size_t                  fVertexOffset;
+        GrVertexLayout          fVertexLayout;
+        const GrVertexBuffer*   fVertexBuffer;
+        const GrIndexBuffer*    fIndexBuffer;
+        bool                    fArrayPtrsDirty;
+    } fHWGeometryState;
+
+    struct AAState {
+        bool fMSAAEnabled;
+        bool fSmoothLineEnabled;
+    } fHWAAState;
+
+    GrDrawState fHWDrawState;
+    bool        fHWStencilClip;
+
+    // As flush of GL state proceeds it updates fHDrawState
+    // to reflect the new state. Later parts of the state flush
+    // may perform cascaded changes but cannot refer to fHWDrawState.
+    // These code paths can refer to the dirty flags. Subclass should
+    // call resetDirtyFlags after its flush is complete
+    struct {
+        bool fRenderTargetChanged : 1;
+        int  fTextureChangedMask;
+    } fDirtyFlags;
+    GR_STATIC_ASSERT(8 * sizeof(int) >= GrDrawState::kNumStages);
+
+    // clears the dirty flags
+    void resetDirtyFlags();
+
+    // last scissor / viewport scissor state seen by the GL.
+    struct {
+        bool        fScissorEnabled;
+        GrGLIRect   fScissorRect;
+        GrGLIRect   fViewportRect;
+    } fHWBounds;
+
+    const GLCaps& glCaps() const { return fGLCaps; }
+
+    // GrGpu overrides
+    virtual void onResetContext() SK_OVERRIDE;
+
+    virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
+                                       const void* srcData,
+                                       size_t rowBytes);
+    virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
+                                                 bool dynamic);
+    virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
+                                               bool dynamic);
+    virtual GrTexture* onCreatePlatformTexture(const GrPlatformTextureDesc& desc) SK_OVERRIDE;
+    virtual GrRenderTarget* onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) SK_OVERRIDE;
+    virtual bool createStencilBufferForRenderTarget(GrRenderTarget* rt,
+                                                    int width, int height);
+    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
+                                                   GrRenderTarget* rt);
+
+    virtual void onClear(const GrIRect* rect, GrColor color);
+
+    virtual void onForceRenderTargetFlush();
+
+    virtual bool onReadPixels(GrRenderTarget* target,
+                              int left, int top, 
+                              int width, int height,
+                              GrPixelConfig, 
+                              void* buffer,
+                              size_t rowBytes,
+                              bool invertY) SK_OVERRIDE;
+
+    virtual void onWriteTexturePixels(GrTexture* texture,
+                                      int left, int top, int width, int height,
+                                      GrPixelConfig config, const void* buffer,
+                                      size_t rowBytes) SK_OVERRIDE;
+
+    virtual void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE;
+
+
+    virtual void onGpuDrawIndexed(GrPrimitiveType type,
+                                  uint32_t startVertex,
+                                  uint32_t startIndex,
+                                  uint32_t vertexCount,
+                                  uint32_t indexCount);
+    virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
+                                     uint32_t vertexCount,
+                                     uint32_t numVertices);
+    virtual void flushScissor(const GrIRect* rect);
+    virtual void clearStencil();
+    virtual void clearStencilClip(const GrIRect& rect, bool insideClip);
+    virtual int getMaxEdges() const;
+
+    // binds texture unit in GL
+    void setTextureUnit(int unitIdx);
+
+    // binds appropriate vertex and index buffers, also returns any extra
+    // extra verts or indices to offset by.
+    void setBuffers(bool indexed,
+                    int* extraVertexOffset,
+                    int* extraIndexOffset);
+
+    // flushes state that is common to fixed and programmable GL
+    // dither
+    // line smoothing
+    // texture binding
+    // sampler state (filtering, tiling)
+    // FBO binding
+    // line width
+    bool flushGLStateCommon(GrPrimitiveType type);
+
+    // Subclasses should call this to flush the blend state.
+    // The params should be the final coeffecients to apply
+    // (after any blending optimizations or dual source blending considerations
+    // have been accounted for).
+    void flushBlend(GrPrimitiveType type,
+                    GrBlendCoeff srcCoeff,
+                    GrBlendCoeff dstCoeff);
+
+    bool hasExtension(const char* ext) const {
+        return fGLContextInfo.hasExtension(ext);
+}
+
+    // adjusts texture matrix to account for orientation
+    static void AdjustTextureMatrix(const GrGLTexture* texture,
+                                    GrSamplerState::SampleMode mode,
+                                    GrMatrix* matrix);
+
+    // subclass may try to take advantage of identity tex matrices.
+    // This helper determines if matrix will be identity after all
+    // adjustments are applied.
+    static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
+                                        const GrSamplerState& sampler);
+
+    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 initStencilFormats();
+
+    // notify callbacks to update state tracking when related
+    // objects are bound to GL or deleted outside of the class
+    void notifyVertexBufferBind(const GrGLVertexBuffer* buffer);
+    void notifyVertexBufferDelete(const GrGLVertexBuffer* buffer);
+    void notifyIndexBufferBind(const GrGLIndexBuffer* buffer);
+    void notifyIndexBufferDelete(const GrGLIndexBuffer* buffer);
+    void notifyTextureDelete(GrGLTexture* texture);
+    void notifyRenderTargetDelete(GrRenderTarget* renderTarget);
+
+    void setSpareTextureUnit();
+
+    // bound is region that may be modified and therefore has to be resolved.
+    // NULL means whole target. Can be an empty rect.
+    void flushRenderTarget(const GrIRect* bound);
+    void flushStencil();
+    void flushAAState(GrPrimitiveType type);
+
+    bool configToGLFormats(GrPixelConfig config,
+                           bool getSizedInternal,
+                           GrGLenum* internalFormat,
+                           GrGLenum* externalFormat,
+                           GrGLenum* externalType);
+    // helper for onCreateTexture and writeTexturePixels
+    bool uploadTexData(const GrGLTexture::Desc& desc,
+                       bool isNewTexture,
+                       int left, int top, int width, int height,
+                       GrPixelConfig dataConfig,
+                       const void* data,
+                       size_t rowBytes);
+
+    bool createRenderTargetObjects(int width, int height,
+                                   GrGLuint texID,
+                                   GrGLRenderTarget::Desc* desc);
+
+    friend class GrGLVertexBuffer;
+    friend class GrGLIndexBuffer;
+    friend class GrGLTexture;
+    friend class GrGLRenderTarget;
+
+    GrGLContextInfo fGLContextInfo;
+
+    // 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.
+    GrGLuint fStencilClearFBO;
+
+    bool fHWBlendDisabled;
+
+    int fActiveTextureUnitIdx;
+
+    // we record what stencil format worked last time to hopefully exit early
+    // from our loop that tries stencil formats and calls check fb status.
+    int fLastSuccessfulStencilFmtIdx;
+
+
+    bool fPrintedCaps;
+
+    typedef GrGpu INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/gl/GrGpuGLShaders.cpp b/src/gpu/gl/GrGpuGLShaders.cpp
new file mode 100644
index 0000000..968b893
--- /dev/null
+++ b/src/gpu/gl/GrGpuGLShaders.cpp
@@ -0,0 +1,1188 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include "../GrBinHashKey.h"
+#include "GrGLProgram.h"
+#include "GrGLSL.h"
+#include "GrGpuGLShaders.h"
+#include "../GrGpuVertex.h"
+#include "GrNoncopyable.h"
+#include "../GrStringBuilder.h"
+#include "../GrRandom.h"
+
+#define SKIP_CACHE_CHECK    true
+#define GR_UINT32_MAX   static_cast<uint32_t>(-1)
+
+#include "../GrTHashCache.h"
+
+class GrGpuGLShaders::ProgramCache : public ::GrNoncopyable {
+private:
+    class Entry;
+
+    typedef GrBinHashKey<Entry, GrGLProgram::kProgramKeySize> ProgramHashKey;
+
+    class Entry : public ::GrNoncopyable {
+    public:
+        Entry() {}
+        void copyAndTakeOwnership(Entry& entry) {
+            fProgramData.copyAndTakeOwnership(entry.fProgramData);
+            fKey = entry.fKey; // ownership transfer
+            fLRUStamp = entry.fLRUStamp;
+        }
+
+    public:
+        int compare(const ProgramHashKey& key) const { return fKey.compare(key); }
+
+    public:
+        GrGLProgram::CachedData fProgramData;
+        ProgramHashKey          fKey;
+        unsigned int            fLRUStamp;
+    };
+
+    GrTHashTable<Entry, ProgramHashKey, 8> fHashCache;
+
+    // We may have kMaxEntries+1 shaders in the GL context because
+    // we create a new shader before evicting from the cache.
+    enum {
+        kMaxEntries = 32
+    };
+    Entry                       fEntries[kMaxEntries];
+    int                         fCount;
+    unsigned int                fCurrLRUStamp;
+    const GrGLInterface*        fGL;
+    GrGLSLGeneration            fGLSLGeneration;
+
+public:
+    ProgramCache(const GrGLInterface* gl,
+                 GrGLSLGeneration glslGeneration) 
+        : fCount(0)
+        , fCurrLRUStamp(0)
+        , fGL(gl)
+        , fGLSLGeneration(glslGeneration) {
+    }
+
+    ~ProgramCache() {
+        for (int i = 0; i < fCount; ++i) {
+            GrGpuGLShaders::DeleteProgram(fGL, &fEntries[i].fProgramData);
+        }
+    }
+
+    void abandon() {
+        fCount = 0;
+    }
+
+    void invalidateViewMatrices() {
+        for (int i = 0; i < fCount; ++i) {
+            // set to illegal matrix
+            fEntries[i].fProgramData.fViewMatrix = GrMatrix::InvalidMatrix();
+        }
+    }
+
+    GrGLProgram::CachedData* getProgramData(const GrGLProgram& desc) {
+        Entry newEntry;
+        newEntry.fKey.setKeyData(desc.keyData());
+        
+        Entry* entry = fHashCache.find(newEntry.fKey);
+        if (NULL == entry) {
+            if (!desc.genProgram(fGL, fGLSLGeneration,
+                                 &newEntry.fProgramData)) {
+                return NULL;
+            }
+            if (fCount < kMaxEntries) {
+                entry = fEntries + fCount;
+                ++fCount;
+            } else {
+                GrAssert(kMaxEntries == fCount);
+                entry = fEntries;
+                for (int i = 1; i < kMaxEntries; ++i) {
+                    if (fEntries[i].fLRUStamp < entry->fLRUStamp) {
+                        entry = fEntries + i;
+                    }
+                }
+                fHashCache.remove(entry->fKey, entry);
+                GrGpuGLShaders::DeleteProgram(fGL, &entry->fProgramData);
+            }
+            entry->copyAndTakeOwnership(newEntry);
+            fHashCache.insert(entry->fKey, entry);
+        }
+
+        entry->fLRUStamp = fCurrLRUStamp;
+        if (GR_UINT32_MAX == fCurrLRUStamp) {
+            // wrap around! just trash our LRU, one time hit.
+            for (int i = 0; i < fCount; ++i) {
+                fEntries[i].fLRUStamp = 0;
+            }
+        }
+        ++fCurrLRUStamp;
+        return &entry->fProgramData;
+    }
+};
+
+void GrGpuGLShaders::abandonResources(){
+    INHERITED::abandonResources();
+    fProgramCache->abandon();
+}
+
+void GrGpuGLShaders::DeleteProgram(const GrGLInterface* gl,
+                                   CachedData* programData) {
+    GR_GL_CALL(gl, DeleteShader(programData->fVShaderID));
+    if (programData->fGShaderID) {
+        GR_GL_CALL(gl, DeleteShader(programData->fGShaderID));
+    }
+    GR_GL_CALL(gl, DeleteShader(programData->fFShaderID));
+    GR_GL_CALL(gl, DeleteProgram(programData->fProgramID));
+    GR_DEBUGCODE(memset(programData, 0, sizeof(*programData));)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
+
+namespace {
+
+// GrRandoms nextU() values have patterns in the low bits
+// So using nextU() % array_count might never take some values.
+int random_int(GrRandom* r, int count) {
+    return (int)(r->nextF() * count);
+}
+
+// min is inclusive, max is exclusive
+int random_int(GrRandom* r, int min, int max) {
+    return (int)(r->nextF() * (max-min)) + min;
+}
+
+bool random_bool(GrRandom* r) {
+    return r->nextF() > .5f;
+}
+
+}
+
+bool GrGpuGLShaders::programUnitTest() {
+
+    GrGLSLGeneration glslGeneration = 
+            GrGetGLSLGeneration(this->glBinding(), this->glInterface());
+    static const int STAGE_OPTS[] = {
+        0,
+        StageDesc::kNoPerspective_OptFlagBit,
+        StageDesc::kIdentity_CoordMapping
+    };
+    static const int IN_CONFIG_FLAGS[] = {
+        StageDesc::kNone_InConfigFlag,
+        StageDesc::kSwapRAndB_InConfigFlag,
+        StageDesc::kSwapRAndB_InConfigFlag | StageDesc::kMulRGBByAlpha_InConfigFlag,
+        StageDesc::kMulRGBByAlpha_InConfigFlag,
+        StageDesc::kSmearAlpha_InConfigFlag,
+    };
+    GrGLProgram program;
+    ProgramDesc& pdesc = program.fProgramDesc;
+
+    static const int NUM_TESTS = 512;
+
+    GrRandom random;
+    for (int t = 0; t < NUM_TESTS; ++t) {
+
+#if 0
+        GrPrintf("\nTest Program %d\n-------------\n", t);
+        static const int stop = -1;
+        if (t == stop) {
+            int breakpointhere = 9;
+        }
+#endif
+
+        pdesc.fVertexLayout = 0;
+        pdesc.fEmitsPointSize = random.nextF() > .5f;
+        pdesc.fColorInput = random_int(&random, ProgramDesc::kColorInputCnt);
+        pdesc.fCoverageInput = random_int(&random, ProgramDesc::kColorInputCnt);
+
+        pdesc.fColorFilterXfermode = random_int(&random, SkXfermode::kCoeffModesCnt);
+
+        pdesc.fFirstCoverageStage = random_int(&random, GrDrawState::kNumStages);
+
+        pdesc.fVertexLayout |= random_bool(&random) ?
+                                    GrDrawTarget::kCoverage_VertexLayoutBit :
+                                    0;
+
+#if GR_GL_EXPERIMENTAL_GS
+        pdesc.fExperimentalGS = this->getCaps().fGeometryShaderSupport &&
+                                random_bool(&random);
+#endif
+        pdesc.fOutputPM =  random_int(&random, ProgramDesc::kOutputPMCnt);
+
+        bool edgeAA = random_bool(&random);
+        if (edgeAA) {
+            bool vertexEdgeAA = random_bool(&random);
+            if (vertexEdgeAA) {
+                pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
+                if (this->getCaps().fShaderDerivativeSupport) {
+                    pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
+                } else {
+                    pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+                }
+                pdesc.fEdgeAANumEdges = 0;
+            } else {
+                pdesc.fEdgeAANumEdges = random_int(&random, 1, this->getMaxEdges());
+                pdesc.fEdgeAAConcave = random_bool(&random);
+            }
+        } else {
+            pdesc.fEdgeAANumEdges = 0;
+        }
+
+        pdesc.fColorMatrixEnabled = random_bool(&random);
+
+        if (this->getCaps().fDualSourceBlendingSupport) {
+            pdesc.fDualSrcOutput = random_int(&random, ProgramDesc::kDualSrcOutputCnt);
+        } else {
+            pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+        }
+
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            // enable the stage?
+            if (random_bool(&random)) {
+                // use separate tex coords?
+                if (random_bool(&random)) {
+                    int t = random_int(&random, GrDrawState::kMaxTexCoords);
+                    pdesc.fVertexLayout |= StageTexCoordVertexLayoutBit(s, t);
+                } else {
+                    pdesc.fVertexLayout |= StagePosAsTexCoordVertexLayoutBit(s);
+                }
+            }
+            // use text-formatted verts?
+            if (random_bool(&random)) {
+                pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
+            }
+            StageDesc& stage = pdesc.fStages[s];
+            stage.fOptFlags = STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))];
+            stage.fInConfigFlags = IN_CONFIG_FLAGS[random_int(&random, GR_ARRAY_COUNT(IN_CONFIG_FLAGS))];
+            stage.fCoordMapping =  random_int(&random, StageDesc::kCoordMappingCnt);
+            stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt);
+            // convolution shaders don't work with persp tex matrix
+            if (stage.fFetchMode == StageDesc::kConvolution_FetchMode) {
+                stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+            }
+            stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
+            switch (stage.fFetchMode) {
+                case StageDesc::kSingle_FetchMode:
+                    stage.fKernelWidth = 0;
+                    break;
+                case StageDesc::kConvolution_FetchMode:
+                    stage.fKernelWidth = random_int(&random, 2, 8);
+                    stage.fInConfigFlags &= ~StageDesc::kMulRGBByAlpha_InConfigFlag;
+                    break;
+                case StageDesc::k2x2_FetchMode:
+                    stage.fKernelWidth = 0;
+                    stage.fInConfigFlags &= ~StageDesc::kMulRGBByAlpha_InConfigFlag;
+                    break;
+            }
+        }
+        CachedData cachedData;
+        if (!program.genProgram(this->glInterface(),
+                                glslGeneration,
+                                &cachedData)) {
+            return false;
+        }
+        DeleteProgram(this->glInterface(), &cachedData);
+    }
+    return true;
+}
+
+GrGpuGLShaders::GrGpuGLShaders(const GrGLContextInfo& ctxInfo)
+    : GrGpuGL(ctxInfo) {
+
+    // Enable supported shader-related caps
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        fCaps.fDualSourceBlendingSupport =
+                            this->glVersion() >= GR_GL_VER(3,3) ||
+                            this->hasExtension("GL_ARB_blend_func_extended");
+        fCaps.fShaderDerivativeSupport = true;
+        // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS
+        fCaps.fGeometryShaderSupport = 
+                                this->glVersion() >= GR_GL_VER(3,2) &&
+                                this->glslGeneration() >= k150_GrGLSLGeneration;
+    } else {
+        fCaps.fShaderDerivativeSupport =
+                            this->hasExtension("GL_OES_standard_derivatives");
+    }
+
+    GR_GL_GetIntegerv(this->glInterface(),
+                      GR_GL_MAX_VERTEX_ATTRIBS,
+                      &fMaxVertexAttribs);
+
+    fProgramData = NULL;
+    fProgramCache = new ProgramCache(this->glInterface(),
+                                     this->glslGeneration());
+
+#if 0
+    this->programUnitTest();
+#endif
+}
+
+GrGpuGLShaders::~GrGpuGLShaders() {
+    delete fProgramCache;
+}
+
+const GrMatrix& GrGpuGLShaders::getHWViewMatrix() {
+    GrAssert(fProgramData);
+
+    if (GrGLProgram::kSetAsAttribute == 
+        fProgramData->fUniLocations.fViewMatrixUni) {
+        return fHWDrawState.getViewMatrix();
+    } else {
+        return fProgramData->fViewMatrix;
+    }
+}
+
+void GrGpuGLShaders::recordHWViewMatrix(const GrMatrix& matrix) {
+    GrAssert(fProgramData);
+    if (GrGLProgram::kSetAsAttribute == 
+        fProgramData->fUniLocations.fViewMatrixUni) {
+        fHWDrawState.setViewMatrix(matrix);
+    } else {
+        fProgramData->fViewMatrix = matrix;
+    }
+}
+
+const GrMatrix& GrGpuGLShaders::getHWSamplerMatrix(int stage) {
+    GrAssert(fProgramData);
+
+    if (GrGLProgram::kSetAsAttribute == 
+        fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
+        return fHWDrawState.getSampler(stage).getMatrix();
+    } else {
+        return fProgramData->fTextureMatrices[stage];
+    }
+}
+
+void GrGpuGLShaders::recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
+    GrAssert(fProgramData);
+    if (GrGLProgram::kSetAsAttribute == 
+        fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
+        *fHWDrawState.sampler(stage)->matrix() = matrix;
+    } else {
+        fProgramData->fTextureMatrices[stage] = matrix;
+    }
+}
+
+void GrGpuGLShaders::onResetContext() {
+    INHERITED::onResetContext();
+
+    fHWGeometryState.fVertexOffset = ~0;
+
+    // Third party GL code may have left vertex attributes enabled. Some GL
+    // implementations (osmesa) may read vetex attributes that are not required
+    // by the current shader. Therefore, we have to ensure that only the
+    // attributes we require for the current draw are enabled or we may cause an
+    // invalid read.
+
+    // Disable all vertex layout bits so that next flush will assume all
+    // optional vertex attributes are disabled.
+    fHWGeometryState.fVertexLayout = 0;
+
+    // We always use the this attribute and assume it is always enabled.
+    int posAttrIdx = GrGLProgram::PositionAttributeIdx();
+    GL_CALL(EnableVertexAttribArray(posAttrIdx));
+    // Disable all other vertex attributes.
+    for  (int va = 0; va < fMaxVertexAttribs; ++va) {
+        if (va != posAttrIdx) {
+            GL_CALL(DisableVertexAttribArray(va));
+        }
+    }
+
+    fHWProgramID = 0;
+}
+
+void GrGpuGLShaders::flushViewMatrix() {
+    const GrMatrix& vm = this->getDrawState().getViewMatrix();
+    if (GrGpuGLShaders::getHWViewMatrix() != vm) {
+
+        const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
+        GrAssert(NULL != rt);
+        GrMatrix m;
+        m.setAll(
+            GrIntToScalar(2) / rt->width(), 0, -GR_Scalar1,
+            0,-GrIntToScalar(2) / rt->height(), GR_Scalar1,
+            0, 0, GrMatrix::I()[8]);
+        m.setConcat(m, vm);
+
+        // ES doesn't allow you to pass true to the transpose param,
+        // so do our own transpose
+        GrGLfloat mt[]  = {
+            GrScalarToFloat(m[GrMatrix::kMScaleX]),
+            GrScalarToFloat(m[GrMatrix::kMSkewY]),
+            GrScalarToFloat(m[GrMatrix::kMPersp0]),
+            GrScalarToFloat(m[GrMatrix::kMSkewX]),
+            GrScalarToFloat(m[GrMatrix::kMScaleY]),
+            GrScalarToFloat(m[GrMatrix::kMPersp1]),
+            GrScalarToFloat(m[GrMatrix::kMTransX]),
+            GrScalarToFloat(m[GrMatrix::kMTransY]),
+            GrScalarToFloat(m[GrMatrix::kMPersp2])
+        };
+
+        if (GrGLProgram::kSetAsAttribute ==  
+            fProgramData->fUniLocations.fViewMatrixUni) {
+            int baseIdx = GrGLProgram::ViewMatrixAttributeIdx();
+            GL_CALL(VertexAttrib4fv(baseIdx + 0, mt+0));
+            GL_CALL(VertexAttrib4fv(baseIdx + 1, mt+3));
+            GL_CALL(VertexAttrib4fv(baseIdx + 2, mt+6));
+        } else {
+            GrAssert(GrGLProgram::kUnusedUniform != 
+                     fProgramData->fUniLocations.fViewMatrixUni);
+            GL_CALL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni,
+                                     1, false, mt));
+        }
+        this->recordHWViewMatrix(vm);
+    }
+}
+
+void GrGpuGLShaders::flushTextureDomain(int s) {
+    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni;
+    const GrDrawState& drawState = this->getDrawState();
+    if (GrGLProgram::kUnusedUniform != uni) {
+        const GrRect &texDom = drawState.getSampler(s).getTextureDomain();
+
+        if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
+            fProgramData->fTextureDomain[s] != texDom) {
+
+            fProgramData->fTextureDomain[s] = texDom;
+
+            float values[4] = {
+                GrScalarToFloat(texDom.left()),
+                GrScalarToFloat(texDom.top()),
+                GrScalarToFloat(texDom.right()),
+                GrScalarToFloat(texDom.bottom())
+            };
+
+            const GrGLTexture* texture =
+                static_cast<const GrGLTexture*>(drawState.getTexture(s));
+            GrGLTexture::Orientation orientation = texture->orientation();
+
+            // vertical flip if necessary
+            if (GrGLTexture::kBottomUp_Orientation == orientation) {
+                values[1] = 1.0f - values[1];
+                values[3] = 1.0f - values[3];
+                // The top and bottom were just flipped, so correct the ordering
+                // of elements so that values = (l, t, r, b).
+                SkTSwap(values[1], values[3]);
+            }
+
+            GL_CALL(Uniform4fv(uni, 1, values));
+        }
+    }
+}
+
+void GrGpuGLShaders::flushTextureMatrix(int s) {
+    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
+    const GrDrawState& drawState = this->getDrawState();
+    const GrGLTexture* texture =
+        static_cast<const GrGLTexture*>(drawState.getTexture(s));
+    if (NULL != texture) {
+        if (GrGLProgram::kUnusedUniform != uni &&
+            (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
+            this->getHWSamplerMatrix(s) != drawState.getSampler(s).getMatrix())) {
+
+            GrMatrix m = drawState.getSampler(s).getMatrix();
+            GrSamplerState::SampleMode mode =
+                drawState.getSampler(s).getSampleMode();
+            AdjustTextureMatrix(texture, mode, &m);
+
+            // ES doesn't allow you to pass true to the transpose param,
+            // so do our own transpose
+            GrGLfloat mt[]  = {
+                GrScalarToFloat(m[GrMatrix::kMScaleX]),
+                GrScalarToFloat(m[GrMatrix::kMSkewY]),
+                GrScalarToFloat(m[GrMatrix::kMPersp0]),
+                GrScalarToFloat(m[GrMatrix::kMSkewX]),
+                GrScalarToFloat(m[GrMatrix::kMScaleY]),
+                GrScalarToFloat(m[GrMatrix::kMPersp1]),
+                GrScalarToFloat(m[GrMatrix::kMTransX]),
+                GrScalarToFloat(m[GrMatrix::kMTransY]),
+                GrScalarToFloat(m[GrMatrix::kMPersp2])
+            };
+
+            if (GrGLProgram::kSetAsAttribute ==
+                fProgramData->fUniLocations.fStages[s].fTextureMatrixUni) {
+                int baseIdx = GrGLProgram::TextureMatrixAttributeIdx(s);
+                GL_CALL(VertexAttrib4fv(baseIdx + 0, mt+0));
+                GL_CALL(VertexAttrib4fv(baseIdx + 1, mt+3));
+                GL_CALL(VertexAttrib4fv(baseIdx + 2, mt+6));
+            } else {
+                GL_CALL(UniformMatrix3fv(uni, 1, false, mt));
+            }
+            this->recordHWSamplerMatrix(s, drawState.getSampler(s).getMatrix());
+        }
+    }
+}
+
+void GrGpuGLShaders::flushRadial2(int s) {
+
+    const int &uni = fProgramData->fUniLocations.fStages[s].fRadial2Uni;
+    const GrSamplerState& sampler = this->getDrawState().getSampler(s);
+    if (GrGLProgram::kUnusedUniform != uni &&
+        (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
+         fProgramData->fRadial2Radius0[s]  != sampler.getRadial2Radius0()  ||
+         fProgramData->fRadial2PosRoot[s]  != sampler.isRadial2PosRoot())) {
+
+        GrScalar centerX1 = sampler.getRadial2CenterX1();
+        GrScalar radius0 = sampler.getRadial2Radius0();
+
+        GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
+
+        // when were in the degenerate (linear) case the second
+        // value will be INF but the program doesn't read it. (We
+        // use the same 6 uniforms even though we don't need them
+        // all in the linear case just to keep the code complexity
+        // down).
+        float values[6] = {
+            GrScalarToFloat(a),
+            1 / (2.f * GrScalarToFloat(a)),
+            GrScalarToFloat(centerX1),
+            GrScalarToFloat(radius0),
+            GrScalarToFloat(GrMul(radius0, radius0)),
+            sampler.isRadial2PosRoot() ? 1.f : -1.f
+        };
+        GL_CALL(Uniform1fv(uni, 6, values));
+        fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
+        fProgramData->fRadial2Radius0[s]  = sampler.getRadial2Radius0();
+        fProgramData->fRadial2PosRoot[s]  = sampler.isRadial2PosRoot();
+    }
+}
+
+void GrGpuGLShaders::flushConvolution(int s) {
+    const GrSamplerState& sampler = this->getDrawState().getSampler(s);
+    int kernelUni = fProgramData->fUniLocations.fStages[s].fKernelUni;
+    if (GrGLProgram::kUnusedUniform != kernelUni) {
+        GL_CALL(Uniform1fv(kernelUni, sampler.getKernelWidth(),
+                           sampler.getKernel()));
+    }
+    int imageIncrementUni = fProgramData->fUniLocations.fStages[s].fImageIncrementUni;
+    if (GrGLProgram::kUnusedUniform != imageIncrementUni) {
+        GL_CALL(Uniform2fv(imageIncrementUni, 1, sampler.getImageIncrement()));
+    }
+}
+
+void GrGpuGLShaders::flushTexelSize(int s) {
+    const int& uni = fProgramData->fUniLocations.fStages[s].fNormalizedTexelSizeUni;
+    if (GrGLProgram::kUnusedUniform != uni) {
+        const GrGLTexture* texture =
+            static_cast<const GrGLTexture*>(this->getDrawState().getTexture(s));
+        if (texture->width() != fProgramData->fTextureWidth[s] ||
+            texture->height() != fProgramData->fTextureHeight[s]) {
+
+            float texelSize[] = {1.f / texture->width(),
+                                 1.f / texture->height()};
+            GL_CALL(Uniform2fv(uni, 1, texelSize));
+            fProgramData->fTextureWidth[s] = texture->width();
+            fProgramData->fTextureHeight[s] = texture->height();
+        }
+    }
+}
+
+void GrGpuGLShaders::flushEdgeAAData() {
+    const int& uni = fProgramData->fUniLocations.fEdgesUni;
+    if (GrGLProgram::kUnusedUniform != uni) {
+        int count = this->getDrawState().getNumAAEdges();
+        GrDrawState::Edge edges[GrDrawState::kMaxEdges];
+        // Flip the edges in Y
+        float height = 
+            static_cast<float>(this->getDrawState().getRenderTarget()->height());
+        for (int i = 0; i < count; ++i) {
+            edges[i] = this->getDrawState().getAAEdges()[i];
+            float b = edges[i].fY;
+            edges[i].fY = -b;
+            edges[i].fZ += b * height;
+        }
+        GL_CALL(Uniform3fv(uni, count, &edges[0].fX));
+    }
+}
+
+void GrGpuGLShaders::flushColorMatrix() {
+    const ProgramDesc& desc = fCurrentProgram.getDesc();
+    int matrixUni = fProgramData->fUniLocations.fColorMatrixUni;
+    int vecUni = fProgramData->fUniLocations.fColorMatrixVecUni;
+    if (GrGLProgram::kUnusedUniform != matrixUni
+     && GrGLProgram::kUnusedUniform != vecUni) {
+        const float* m = this->getDrawState().getColorMatrix();
+        GrGLfloat mt[]  = {
+            m[0], m[5], m[10], m[15],
+            m[1], m[6], m[11], m[16],
+            m[2], m[7], m[12], m[17],
+            m[3], m[8], m[13], m[18],
+        };
+        static float scale = 1.0f / 255.0f;
+        GrGLfloat vec[] = {
+            m[4] * scale, m[9] * scale, m[14] * scale, m[19] * scale,
+        };
+        GL_CALL(UniformMatrix4fv(matrixUni, 1, false, mt));
+        GL_CALL(Uniform4fv(vecUni, 1, vec));
+    }
+}
+
+static const float ONE_OVER_255 = 1.f / 255.f;
+
+#define GR_COLOR_TO_VEC4(color) {\
+    GrColorUnpackR(color) * ONE_OVER_255,\
+    GrColorUnpackG(color) * ONE_OVER_255,\
+    GrColorUnpackB(color) * ONE_OVER_255,\
+    GrColorUnpackA(color) * ONE_OVER_255 \
+}
+
+void GrGpuGLShaders::flushColor(GrColor color) {
+    const ProgramDesc& desc = fCurrentProgram.getDesc();
+    const GrDrawState& drawState = this->getDrawState();
+
+    if (this->getGeomSrc().fVertexLayout & kColor_VertexLayoutBit) {
+        // color will be specified per-vertex as an attribute
+        // invalidate the const vertex attrib color
+        fHWDrawState.setColor(GrColor_ILLEGAL);
+    } else {
+        switch (desc.fColorInput) {
+            case ProgramDesc::kAttribute_ColorInput:
+                if (fHWDrawState.getColor() != color) {
+                    // OpenGL ES only supports the float varieties of
+                    // glVertexAttrib
+                    float c[] = GR_COLOR_TO_VEC4(color);
+                    GL_CALL(VertexAttrib4fv(GrGLProgram::ColorAttributeIdx(), 
+                                            c));
+                    fHWDrawState.setColor(color);
+                }
+                break;
+            case ProgramDesc::kUniform_ColorInput:
+                if (fProgramData->fColor != color) {
+                    // OpenGL ES doesn't support unsigned byte varieties of
+                    // glUniform
+                    float c[] = GR_COLOR_TO_VEC4(color);
+                    GrAssert(GrGLProgram::kUnusedUniform != 
+                             fProgramData->fUniLocations.fColorUni);
+                    GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorUni,
+                                        1, c));
+                    fProgramData->fColor = color;
+                }
+                break;
+            case ProgramDesc::kSolidWhite_ColorInput:
+            case ProgramDesc::kTransBlack_ColorInput:
+                break;
+            default:
+                GrCrash("Unknown color type.");
+        }
+    }
+    if (fProgramData->fUniLocations.fColorFilterUni
+                != GrGLProgram::kUnusedUniform
+            && fProgramData->fColorFilterColor
+                != drawState.getColorFilterColor()) {
+        float c[] = GR_COLOR_TO_VEC4(drawState.getColorFilterColor());
+        GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorFilterUni, 1, c));
+        fProgramData->fColorFilterColor = drawState.getColorFilterColor();
+    }
+}
+
+void GrGpuGLShaders::flushCoverage(GrColor coverage) {
+    const ProgramDesc& desc = fCurrentProgram.getDesc();
+    const GrDrawState& drawState = this->getDrawState();
+
+
+    if (this->getGeomSrc().fVertexLayout & kCoverage_VertexLayoutBit) {
+        // coverage will be specified per-vertex as an attribute
+        // invalidate the const vertex attrib coverage
+        fHWDrawState.setCoverage4(GrColor_ILLEGAL);
+    } else {
+        switch (desc.fCoverageInput) {
+            case ProgramDesc::kAttribute_ColorInput:
+                if (fHWDrawState.getCoverage() != coverage) {
+                    // OpenGL ES only supports the float varieties of
+                    // glVertexAttrib
+                    float c[] = GR_COLOR_TO_VEC4(coverage);
+                    GL_CALL(VertexAttrib4fv(GrGLProgram::CoverageAttributeIdx(), 
+                                            c));
+                    fHWDrawState.setCoverage(coverage);
+                }
+                break;
+            case ProgramDesc::kUniform_ColorInput:
+                if (fProgramData->fCoverage != coverage) {
+                    // OpenGL ES doesn't support unsigned byte varieties of
+                    // glUniform
+                    float c[] = GR_COLOR_TO_VEC4(coverage);
+                    GrAssert(GrGLProgram::kUnusedUniform != 
+                             fProgramData->fUniLocations.fCoverageUni);
+                    GL_CALL(Uniform4fv(fProgramData->fUniLocations.fCoverageUni,
+                                        1, c));
+                    fProgramData->fCoverage = coverage;
+                }
+                break;
+            case ProgramDesc::kSolidWhite_ColorInput:
+            case ProgramDesc::kTransBlack_ColorInput:
+                break;
+            default:
+                GrCrash("Unknown coverage type.");
+        }
+    }
+}
+
+bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
+    if (!flushGLStateCommon(type)) {
+        return false;
+    }
+
+    const GrDrawState& drawState = this->getDrawState();
+
+    if (fDirtyFlags.fRenderTargetChanged) {
+        // our coords are in pixel space and the GL matrices map to NDC
+        // so if the viewport changed, our matrix is now wrong.
+        fHWDrawState.setViewMatrix(GrMatrix::InvalidMatrix());
+        // we assume all shader matrices may be wrong after viewport changes
+        fProgramCache->invalidateViewMatrices();
+    }
+
+    GrBlendCoeff srcCoeff;
+    GrBlendCoeff dstCoeff;
+    BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
+    if (kSkipDraw_BlendOptFlag & blendOpts) {
+        return false;
+    }
+
+    this->buildProgram(type, blendOpts, dstCoeff);
+    fProgramData = fProgramCache->getProgramData(fCurrentProgram);
+    if (NULL == fProgramData) {
+        GrAssert(!"Failed to create program!");
+        return false;
+    }
+
+    if (fHWProgramID != fProgramData->fProgramID) {
+        GL_CALL(UseProgram(fProgramData->fProgramID));
+        fHWProgramID = fProgramData->fProgramID;
+    }
+    fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
+    this->flushBlend(type, srcCoeff, dstCoeff);
+
+    GrColor color;
+    GrColor coverage;
+    if (blendOpts & kEmitTransBlack_BlendOptFlag) {
+        color = 0;
+        coverage = 0;
+    } else if (blendOpts & kEmitCoverage_BlendOptFlag) {
+        color = 0xffffffff;
+        coverage = drawState.getCoverage();
+    } else {
+        color = drawState.getColor();
+        coverage = drawState.getCoverage();
+    }
+    this->flushColor(color);
+    this->flushCoverage(coverage);
+
+    this->flushViewMatrix();
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if (this->isStageEnabled(s)) {
+            this->flushTextureMatrix(s);
+
+            this->flushRadial2(s);
+
+            this->flushConvolution(s);
+
+            this->flushTexelSize(s);
+
+            this->flushTextureDomain(s);
+        }
+    }
+    this->flushEdgeAAData();
+    this->flushColorMatrix();
+    resetDirtyFlags();
+    return true;
+}
+
+void GrGpuGLShaders::postDraw() {
+}
+
+void GrGpuGLShaders::setupGeometry(int* startVertex,
+                                    int* startIndex,
+                                    int vertexCount,
+                                    int indexCount) {
+
+    int newColorOffset;
+    int newCoverageOffset;
+    int newTexCoordOffsets[GrDrawState::kMaxTexCoords];
+    int newEdgeOffset;
+
+    GrGLsizei newStride = VertexSizeAndOffsetsByIdx(
+                                            this->getGeomSrc().fVertexLayout,
+                                            newTexCoordOffsets,
+                                            &newColorOffset,
+                                            &newCoverageOffset,
+                                            &newEdgeOffset);
+    int oldColorOffset;
+    int oldCoverageOffset;
+    int oldTexCoordOffsets[GrDrawState::kMaxTexCoords];
+    int oldEdgeOffset;
+
+    GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(
+                                            fHWGeometryState.fVertexLayout,
+                                            oldTexCoordOffsets,
+                                            &oldColorOffset,
+                                            &oldCoverageOffset,
+                                            &oldEdgeOffset);
+    bool indexed = NULL != startIndex;
+
+    int extraVertexOffset;
+    int extraIndexOffset;
+    this->setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);
+
+    GrGLenum scalarType;
+    bool texCoordNorm;
+    if (this->getGeomSrc().fVertexLayout & kTextFormat_VertexLayoutBit) {
+        scalarType = GrGLTextType;
+        texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
+    } else {
+        scalarType = GrGLType;
+        texCoordNorm = false;
+    }
+
+    size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride;
+    *startVertex = 0;
+    if (indexed) {
+        *startIndex += extraIndexOffset;
+    }
+
+    // all the Pointers must be set if any of these are true
+    bool allOffsetsChange =  fHWGeometryState.fArrayPtrsDirty ||
+                             vertexOffset != fHWGeometryState.fVertexOffset ||
+                             newStride != oldStride;
+
+    // position and tex coord offsets change if above conditions are true
+    // or the type/normalization changed based on text vs nontext type coords.
+    bool posAndTexChange = allOffsetsChange ||
+                           (((GrGLTextType != GrGLType) || GR_GL_TEXT_TEXTURE_NORMALIZED) &&
+                                (kTextFormat_VertexLayoutBit &
+                                  (fHWGeometryState.fVertexLayout ^
+                                   this->getGeomSrc().fVertexLayout)));
+
+    if (posAndTexChange) {
+        int idx = GrGLProgram::PositionAttributeIdx();
+        GL_CALL(VertexAttribPointer(idx, 2, scalarType, false, newStride, 
+                                  (GrGLvoid*)vertexOffset));
+        fHWGeometryState.fVertexOffset = vertexOffset;
+    }
+
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (newTexCoordOffsets[t] > 0) {
+            GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]);
+            int idx = GrGLProgram::TexCoordAttributeIdx(t);
+            if (oldTexCoordOffsets[t] <= 0) {
+                GL_CALL(EnableVertexAttribArray(idx));
+                GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, 
+                                          newStride, texCoordOffset));
+            } else if (posAndTexChange ||
+                       newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
+                GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, 
+                                          newStride, texCoordOffset));
+            }
+        } else if (oldTexCoordOffsets[t] > 0) {
+            GL_CALL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
+        }
+    }
+
+    if (newColorOffset > 0) {
+        GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset);
+        int idx = GrGLProgram::ColorAttributeIdx();
+        if (oldColorOffset <= 0) {
+            GL_CALL(EnableVertexAttribArray(idx));
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                      true, newStride, colorOffset));
+        } else if (allOffsetsChange || newColorOffset != oldColorOffset) {
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                      true, newStride, colorOffset));
+        }
+    } else if (oldColorOffset > 0) {
+        GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
+    }
+
+    if (newCoverageOffset > 0) {
+        GrGLvoid* coverageOffset = (int8_t*)(vertexOffset + newCoverageOffset);
+        int idx = GrGLProgram::CoverageAttributeIdx();
+        if (oldCoverageOffset <= 0) {
+            GL_CALL(EnableVertexAttribArray(idx));
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                        true, newStride, coverageOffset));
+        } else if (allOffsetsChange || newCoverageOffset != oldCoverageOffset) {
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                        true, newStride, coverageOffset));
+        }
+    } else if (oldCoverageOffset > 0) {
+        GL_CALL(DisableVertexAttribArray(GrGLProgram::CoverageAttributeIdx()));
+    }
+
+    if (newEdgeOffset > 0) {
+        GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset);
+        int idx = GrGLProgram::EdgeAttributeIdx();
+        if (oldEdgeOffset <= 0) {
+            GL_CALL(EnableVertexAttribArray(idx));
+            GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+                                        false, newStride, edgeOffset));
+        } else if (allOffsetsChange || newEdgeOffset != oldEdgeOffset) {
+            GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+                                        false, newStride, edgeOffset));
+        }
+    } else if (oldEdgeOffset > 0) {
+        GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
+    }
+
+    fHWGeometryState.fVertexLayout = this->getGeomSrc().fVertexLayout;
+    fHWGeometryState.fArrayPtrsDirty = false;
+}
+
+void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
+                                  BlendOptFlags blendOpts,
+                                  GrBlendCoeff dstCoeff) {
+    ProgramDesc& desc = fCurrentProgram.fProgramDesc;
+    const GrDrawState& drawState = this->getDrawState();
+
+    // This should already have been caught
+    GrAssert(!(kSkipDraw_BlendOptFlag & blendOpts));
+
+    bool skipCoverage = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+
+    bool skipColor = SkToBool(blendOpts & (kEmitTransBlack_BlendOptFlag |
+                                           kEmitCoverage_BlendOptFlag));
+
+    // The descriptor is used as a cache key. Thus when a field of the
+    // descriptor will not affect program generation (because of the vertex
+    // layout in use or other descriptor field settings) it should be set
+    // to a canonical value to avoid duplicate programs with different keys.
+
+    // Must initialize all fields or cache will have false negatives!
+    desc.fVertexLayout = this->getGeomSrc().fVertexLayout;
+
+    desc.fEmitsPointSize = kPoints_PrimitiveType == type;
+
+    bool requiresAttributeColors = 
+        !skipColor && SkToBool(desc.fVertexLayout & kColor_VertexLayoutBit);
+    bool requiresAttributeCoverage = 
+        !skipCoverage && SkToBool(desc.fVertexLayout &
+                                  kCoverage_VertexLayoutBit);
+
+    // fColorInput/fCoverageInput records how colors are specified for the.
+    // program. So we strip the bits from the layout to avoid false negatives
+    // when searching for an existing program in the cache.
+    desc.fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit);
+
+    desc.fColorFilterXfermode = skipColor ?
+                                SkXfermode::kDst_Mode :
+                                drawState.getColorFilterMode();
+
+    desc.fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit);
+
+    // no reason to do edge aa or look at per-vertex coverage if coverage is
+    // ignored
+    if (skipCoverage) {
+        desc.fVertexLayout &= ~(kEdge_VertexLayoutBit |
+                                kCoverage_VertexLayoutBit);
+    }
+
+    bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+    bool colorIsSolidWhite = (blendOpts & kEmitCoverage_BlendOptFlag) ||
+                             (!requiresAttributeColors &&
+                              0xffffffff == drawState.getColor());
+    if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) {
+        desc.fColorInput = ProgramDesc::kTransBlack_ColorInput;
+    } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) {
+        desc.fColorInput = ProgramDesc::kSolidWhite_ColorInput;
+    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) {
+        desc.fColorInput = ProgramDesc::kUniform_ColorInput;
+    } else {
+        desc.fColorInput = ProgramDesc::kAttribute_ColorInput;
+    }
+    
+    bool covIsSolidWhite = !requiresAttributeCoverage &&
+                           0xffffffff == drawState.getCoverage();
+    
+    if (skipCoverage) {
+        desc.fCoverageInput = ProgramDesc::kTransBlack_ColorInput;
+    } else if (covIsSolidWhite) {
+        desc.fCoverageInput = ProgramDesc::kSolidWhite_ColorInput;
+    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeCoverage) {
+        desc.fCoverageInput = ProgramDesc::kUniform_ColorInput;
+    } else {
+        desc.fCoverageInput = ProgramDesc::kAttribute_ColorInput;
+    }
+
+    desc.fEdgeAANumEdges = skipCoverage ? 0 : drawState.getNumAAEdges();
+    desc.fEdgeAAConcave = desc.fEdgeAANumEdges > 0 &&
+                          drawState.isConcaveEdgeAAState();
+
+    int lastEnabledStage = -1;
+
+    if (!skipCoverage && (desc.fVertexLayout &
+                          GrDrawTarget::kEdge_VertexLayoutBit)) {
+        desc.fVertexEdgeType = drawState.getVertexEdgeType();
+    } else {
+        // use canonical value when not set to avoid cache misses
+        desc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+    }
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        StageDesc& stage = desc.fStages[s];
+
+        stage.fOptFlags = 0;
+        stage.setEnabled(this->isStageEnabled(s));
+
+        bool skip = s < drawState.getFirstCoverageStage() ? skipColor :
+                                                             skipCoverage;
+
+        if (!skip && stage.isEnabled()) {
+            lastEnabledStage = s;
+            const GrGLTexture* texture =
+                static_cast<const GrGLTexture*>(drawState.getTexture(s));
+            GrAssert(NULL != texture);
+            const GrSamplerState& sampler = drawState.getSampler(s);
+            // we matrix to invert when orientation is TopDown, so make sure
+            // we aren't in that case before flagging as identity.
+            if (TextureMatrixIsIdentity(texture, sampler)) {
+                stage.fOptFlags |= StageDesc::kIdentityMatrix_OptFlagBit;
+            } else if (!sampler.getMatrix().hasPerspective()) {
+                stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
+            }
+            switch (sampler.getSampleMode()) {
+                case GrSamplerState::kNormal_SampleMode:
+                    stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
+                    break;
+                case GrSamplerState::kRadial_SampleMode:
+                    stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping;
+                    break;
+                case GrSamplerState::kRadial2_SampleMode:
+                    if (sampler.radial2IsDegenerate()) {
+                        stage.fCoordMapping =
+                            StageDesc::kRadial2GradientDegenerate_CoordMapping;
+                    } else {
+                        stage.fCoordMapping =
+                            StageDesc::kRadial2Gradient_CoordMapping;
+                    }
+                    break;
+                case GrSamplerState::kSweep_SampleMode:
+                    stage.fCoordMapping = StageDesc::kSweepGradient_CoordMapping;
+                    break;
+                default:
+                    GrCrash("Unexpected sample mode!");
+                    break;
+            }
+
+            switch (sampler.getFilter()) {
+                // these both can use a regular texture2D()
+                case GrSamplerState::kNearest_Filter:
+                case GrSamplerState::kBilinear_Filter:
+                    stage.fFetchMode = StageDesc::kSingle_FetchMode;
+                    break;
+                // performs 4 texture2D()s
+                case GrSamplerState::k4x4Downsample_Filter:
+                    stage.fFetchMode = StageDesc::k2x2_FetchMode;
+                    break;
+                // performs fKernelWidth texture2D()s
+                case GrSamplerState::kConvolution_Filter:
+                    stage.fFetchMode = StageDesc::kConvolution_FetchMode;
+                    break;
+                default:
+                    GrCrash("Unexpected filter!");
+                    break;
+            }
+
+            if (sampler.hasTextureDomain()) {
+                GrAssert(GrSamplerState::kClamp_WrapMode ==
+                            sampler.getWrapX() &&
+                         GrSamplerState::kClamp_WrapMode ==
+                            sampler.getWrapY());
+                stage.fOptFlags |= StageDesc::kCustomTextureDomain_OptFlagBit;
+            }
+
+            stage.fInConfigFlags = 0;
+            if (!this->glCaps().fTextureSwizzleSupport) {
+                if (GrPixelConfigIsAlphaOnly(texture->config())) {
+                    // if we don't have texture swizzle support then
+                    // the shader must do an alpha smear after reading
+                    // the texture
+                    stage.fInConfigFlags |= StageDesc::kSmearAlpha_InConfigFlag;
+                } else if (sampler.swapsRAndB()) {
+                    stage.fInConfigFlags |= StageDesc::kSwapRAndB_InConfigFlag;
+                }
+            }
+            if (GrPixelConfigIsUnpremultiplied(texture->config())) {
+                stage.fInConfigFlags |= StageDesc::kMulRGBByAlpha_InConfigFlag;
+            }
+
+            if (sampler.getFilter() == GrSamplerState::kConvolution_Filter) {
+                stage.fKernelWidth = sampler.getKernelWidth();
+            } else {
+                stage.fKernelWidth = 0;
+            }
+        } else {
+            stage.fOptFlags         = 0;
+            stage.fCoordMapping     = (StageDesc::CoordMapping) 0;
+            stage.fInConfigFlags    = 0;
+            stage.fFetchMode        = (StageDesc::FetchMode) 0;
+            stage.fKernelWidth      = 0;
+        }
+    }
+
+    if (GrPixelConfigIsUnpremultiplied(drawState.getRenderTarget()->config())) {
+        desc.fOutputPM = ProgramDesc::kNo_OutputPM;
+    } else {
+        desc.fOutputPM = ProgramDesc::kYes_OutputPM;
+    }
+
+    desc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+
+    // currently the experimental GS will only work with triangle prims
+    // (and it doesn't do anything other than pass through values from
+    // the VS to the FS anyway).
+#if 0 && GR_GL_EXPERIMENTAL_GS
+    desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport;
+#endif
+
+    // we want to avoid generating programs with different "first cov stage"
+    // values when they would compute the same result.
+    // We set field in the desc to kNumStages when either there are no 
+    // coverage stages or the distinction between coverage and color is
+    // immaterial.
+    int firstCoverageStage = GrDrawState::kNumStages;
+    desc.fFirstCoverageStage = GrDrawState::kNumStages;
+    bool hasCoverage = drawState.getFirstCoverageStage() <= lastEnabledStage;
+    if (hasCoverage) {
+        firstCoverageStage = drawState.getFirstCoverageStage();
+    }
+
+    // other coverage inputs
+    if (!hasCoverage) {
+        hasCoverage =
+               desc.fEdgeAANumEdges ||
+               requiresAttributeCoverage ||
+               (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit);
+    }
+
+    if (hasCoverage) {
+        // color filter is applied between color/coverage computation
+        if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
+            desc.fFirstCoverageStage = firstCoverageStage;
+        }
+
+        if (this->getCaps().fDualSourceBlendingSupport &&
+            !(blendOpts & (kEmitCoverage_BlendOptFlag |
+                           kCoverageAsAlpha_BlendOptFlag))) {
+            if (kZero_BlendCoeff == dstCoeff) {
+                // write the coverage value to second color
+                desc.fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
+                desc.fFirstCoverageStage = firstCoverageStage;
+            } else if (kSA_BlendCoeff == dstCoeff) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
+                // cover
+                desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
+                desc.fFirstCoverageStage = firstCoverageStage;
+            } else if (kSC_BlendCoeff == dstCoeff) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
+                // cover
+                desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
+                desc.fFirstCoverageStage = firstCoverageStage;
+            }
+        }
+    }
+}
diff --git a/src/gpu/gl/GrGpuGLShaders.h b/src/gpu/gl/GrGpuGLShaders.h
new file mode 100644
index 0000000..39bc974
--- /dev/null
+++ b/src/gpu/gl/GrGpuGLShaders.h
@@ -0,0 +1,103 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGpuGLShaders_DEFINED
+#define GrGpuGLShaders_DEFINED
+
+#include "GrGpuGL.h"
+#include "GrGLProgram.h"
+
+class GrGpuGLProgram;
+
+// Programmable OpenGL or OpenGL ES 2.0
+class GrGpuGLShaders : public GrGpuGL {
+public:
+             GrGpuGLShaders(const GrGLContextInfo& ctxInfo);
+    virtual ~GrGpuGLShaders();
+
+    virtual void abandonResources();
+
+    bool programUnitTest();
+
+protected:
+    // overrides from GrGpu
+    virtual void onResetContext() SK_OVERRIDE;
+    virtual bool flushGraphicsState(GrPrimitiveType type);
+    virtual void setupGeometry(int* startVertex,
+                               int* startIndex,
+                               int vertexCount,
+                               int indexCount);
+    virtual void postDraw();
+
+private:
+
+    // for readability of function impls
+    typedef GrGLProgram::ProgramDesc ProgramDesc;
+    typedef ProgramDesc::StageDesc   StageDesc;
+    typedef GrGLProgram::CachedData  CachedData;
+
+    class ProgramCache;
+
+    // Helpers to make code more readable
+    const GrMatrix& getHWViewMatrix();
+    void recordHWViewMatrix(const GrMatrix& matrix);
+    const GrMatrix& getHWSamplerMatrix(int stage);
+    void recordHWSamplerMatrix(int stage, const GrMatrix& matrix);
+
+    // sets the texture matrix uniform for currently bound program
+    void flushTextureMatrix(int stage);
+
+    // sets the texture domain uniform for currently bound program
+    void flushTextureDomain(int stage);
+
+    // sets the color specified by GrDrawState::setColor()
+    void flushColor(GrColor color);
+
+    // sets the color specified by GrDrawState::setCoverage()
+    void flushCoverage(GrColor color);
+
+    // sets the MVP matrix uniform for currently bound program
+    void flushViewMatrix();
+
+    // flushes the parameters to two point radial gradient
+    void flushRadial2(int stage);
+
+    // flushes the parameters for convolution
+    void flushConvolution(int stage);
+
+    // flushes the normalized texel size
+    void flushTexelSize(int stage);
+
+    // flushes the edges for edge AA
+    void flushEdgeAAData();
+
+    // flushes the color matrix
+    void flushColorMatrix();
+
+    static void DeleteProgram(const GrGLInterface* gl,
+                              CachedData* programData);
+
+    void buildProgram(GrPrimitiveType typeBlend,
+                      BlendOptFlags blendOpts,
+                      GrBlendCoeff dstCoeff);
+
+    ProgramCache*               fProgramCache;
+    CachedData*                 fProgramData;
+    GrGLuint                    fHWProgramID;
+    GrGLProgram                 fCurrentProgram;
+    // If we get rid of fixed function subclass this should move
+    // to the GLCaps struct in parent class
+    GrGLint                     fMaxVertexAttribs;
+
+    typedef GrGpuGL INHERITED;
+};
+
+#endif
+
diff --git a/src/gpu/gl/SkGLContext.cpp b/src/gpu/gl/SkGLContext.cpp
new file mode 100644
index 0000000..6142d3c
--- /dev/null
+++ b/src/gpu/gl/SkGLContext.cpp
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "SkGLContext.h"
+
+SkGLContext::SkGLContext()
+    : fFBO(0)
+    , fGL(NULL) {
+}
+
+SkGLContext::~SkGLContext() {
+    SkSafeUnref(fGL);
+}
+
+bool SkGLContext::hasExtension(const char* extensionName) const {
+    return GrGLHasExtensionFromString(extensionName, fExtensionString.c_str());
+}
+
+bool SkGLContext::init(int width, int height) {
+    if (fGL) {
+        fGL->unref();
+        this->destroyGLContext();
+    }
+
+    fGL = this->createGLContext();
+    if (fGL) {
+        fExtensionString =
+            reinterpret_cast<const char*>(SK_GL(*this,
+                                                 GetString(GR_GL_EXTENSIONS)));
+        const char* versionStr =
+            reinterpret_cast<const char*>(SK_GL(*this,
+                                                GetString(GR_GL_VERSION)));
+        GrGLVersion version = GrGLGetVersionFromString(versionStr);
+
+        // clear any existing GL erorrs
+        GrGLenum error;
+        do {
+            error = SK_GL(*this, GetError());
+        } while (GR_GL_NO_ERROR != error);
+
+        GrGLuint cbID;
+        GrGLuint dsID;
+
+        GrGLBinding bindingInUse = GrGLGetBindingInUse(this->gl());
+
+        SK_GL(*this, GenFramebuffers(1, &fFBO));
+        SK_GL(*this, BindFramebuffer(GR_GL_FRAMEBUFFER, fFBO));
+        SK_GL(*this, GenRenderbuffers(1, &cbID));
+        SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, cbID));
+        if (kES2_GrGLBinding == bindingInUse) {
+            SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                             GR_GL_RGBA8,
+                                             width, height));
+        } else {
+            SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                             GR_GL_RGBA,
+                                             width, height));
+        }
+        SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                             GR_GL_COLOR_ATTACHMENT0,
+                                             GR_GL_RENDERBUFFER, 
+                                             cbID));
+        SK_GL(*this, GenRenderbuffers(1, &dsID));
+        SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, dsID));
+
+        // Some drivers that support packed depth stencil will only succeed
+        // in binding a packed format an FBO. However, we can't rely on packed
+        // depth stencil being available.
+        bool supportsPackedDepthStencil;
+        if (kES2_GrGLBinding == bindingInUse) {
+            supportsPackedDepthStencil = 
+                    this->hasExtension("GL_OES_packed_depth_stencil");
+        } else {
+            supportsPackedDepthStencil = version >= GR_GL_VER(3,0) ||
+                    this->hasExtension("GL_EXT_packed_depth_stencil") ||
+                    this->hasExtension("GL_ARB_framebuffer_object");
+        }
+
+        if (supportsPackedDepthStencil) {
+            // ES2 requires sized internal formats for RenderbufferStorage
+            // On Desktop we let the driver decide.
+            GrGLenum format = kES2_GrGLBinding == bindingInUse ? 
+                                    GR_GL_DEPTH24_STENCIL8 :
+                                    GR_GL_DEPTH_STENCIL;
+            SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                             format,
+                                             width, height));
+            SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                                 GR_GL_DEPTH_ATTACHMENT,
+                                                 GR_GL_RENDERBUFFER,
+                                                 dsID));
+        } else {
+            GrGLenum format = kES2_GrGLBinding == bindingInUse ? 
+                                    GR_GL_STENCIL_INDEX8 :
+                                    GR_GL_STENCIL_INDEX;
+            SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
+                                             format,
+                                             width, height));
+        }
+        SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
+                                             GR_GL_STENCIL_ATTACHMENT,
+                                             GR_GL_RENDERBUFFER,
+                                             dsID));
+        SK_GL(*this, Viewport(0, 0, width, height));
+        SK_GL(*this, ClearStencil(0));
+        SK_GL(*this, Clear(GR_GL_STENCIL_BUFFER_BIT));
+        
+        error = SK_GL(*this, GetError());
+        GrGLenum status =
+            SK_GL(*this, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
+
+        if (GR_GL_FRAMEBUFFER_COMPLETE != status ||
+            GR_GL_NO_ERROR != error) {
+            fFBO = 0;
+            fGL->unref();
+            fGL = NULL;
+            this->destroyGLContext();
+            return false;
+        } else {
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/src/gpu/gl/SkNullGLContext.cpp b/src/gpu/gl/SkNullGLContext.cpp
new file mode 100644
index 0000000..04e63d8
--- /dev/null
+++ b/src/gpu/gl/SkNullGLContext.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkNullGLContext.h"
+
+const GrGLInterface* SkNullGLContext::createGLContext() {
+    return GrGLCreateNullInterface();
+};