Add NULL GL context implementation. Use in bench (-nullgl) and SampleApp (backspace key)

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


git-svn-id: http://skia.googlecode.com/svn/trunk@2545 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 0a5ac71..5debfee 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -15,10 +15,11 @@
 #include "SkBenchmark.h"
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
-#include "SkNativeGLContext.h"
 #include "SkGpuDevice.h"
 #include "SkGraphics.h"
 #include "SkImageEncoder.h"
+#include "SkNativeGLContext.h"
+#include "SkNullGLContext.h"
 #include "SkNWayCanvas.h"
 #include "SkPicture.h"
 #include "SkString.h"
@@ -233,6 +234,7 @@
     const char* matchStr = NULL;
     bool hasStrokeWidth = false;
     float strokeWidth;
+    bool useNullGL = false;
     
     SkString outDir;
     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
@@ -356,6 +358,8 @@
                 log_error("incomplete '-Dfoo bar' definition\n");
                 return -1;
             }
+        } else if (strcmp(*argv, "-nullgl") == 0) {
+            useNullGL = true;
         } else {
             SkString str;
             str.printf("unrecognized arg %s\n", *argv);
@@ -415,11 +419,16 @@
     GrContext* context = NULL;
     GrRenderTarget* rt = NULL;
     //Don't do GL when fixed.
+    SkAutoTUnref<SkGLContext> glctx;
 #if !defined(SK_SCALAR_IS_FIXED)
-    SkNativeGLContext glContext;
-    if (glContext.init(1024, 1024)) {
+    if (useNullGL) {
+        glctx.reset(new SkNullGLContext);
+    } else {
+        glctx.reset(new SkNativeGLContext);
+    }
+    if (glctx.get()->init(1024, 1024)) {
         GrPlatform3DContext ctx =
-            reinterpret_cast<GrPlatform3DContext>(glContext.gl());
+            reinterpret_cast<GrPlatform3DContext>(glctx.get()->gl());
         context = GrContext::Create(kOpenGL_Shaders_GrEngine, ctx);
         if (NULL != context) {
             GrPlatformSurfaceDesc desc;
@@ -428,7 +437,7 @@
             desc.fWidth = 1024;
             desc.fHeight = 1024;
             desc.fStencilBits = 8;
-            desc.fPlatformRenderTarget = glContext.getFBOID();
+            desc.fPlatformRenderTarget = glctx.get()->getFBOID();
             desc.fSurfaceType = kRenderTarget_GrPlatformSurfaceType;
             rt = static_cast<GrRenderTarget*>(context->createPlatformSurface(desc));
             if (NULL == rt) {
@@ -437,12 +446,9 @@
             }
         }
     }
-    BenchTimer timer = BenchTimer(context ?&glContext : NULL);
-#else
-    BenchTimer timer = BenchTimer();
 #endif
-    
-    
+    BenchTimer timer = BenchTimer(context ? glctx.get() : NULL);
+
     Iter iter(&defineDict);
     SkBenchmark* bench;
     while ((bench = iter.next()) != NULL) {
@@ -505,7 +511,7 @@
 #if !defined(SK_SCALAR_IS_FIXED)
                 if (gpu) {
                     context->flush();
-                    SK_GL(glContext, Finish());
+                    SK_GL(*glctx.get(), Finish());
                 }
 #endif
             }
@@ -520,7 +526,7 @@
             }
  #if !defined(SK_SCALAR_IS_FIXED)
            if (gpu) {
-                SK_GL(glContext, Finish());
+                SK_GL(*glctx.get(), Finish());
            }
  #endif
            timer.end();
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index b66bfc6..a919de7 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -99,6 +99,7 @@
         '../include/gpu/SkGLContext.h',
         '../include/gpu/SkMesaGLContext.h',
         '../include/gpu/SkNativeGLContext.h',
+        '../include/gpu/SkNullGLContext.h',
         '../include/gpu/SkGpuCanvas.h',
         '../include/gpu/SkGpuDevice.h',
         '../include/gpu/SkGr.h',
@@ -111,6 +112,7 @@
         '../src/gpu/SkGr.cpp',
         '../src/gpu/SkGrFontScaler.cpp',
         '../src/gpu/SkGrTexturePixelRef.cpp',
+        '../src/gpu/SkNullGLContext.cpp',
 
         '../src/gpu/mac/SkNativeGLContext_mac.cpp',
 
@@ -193,6 +195,7 @@
         '../src/gpu/GrDrawTarget.h',
         '../src/gpu/GrGeometryBuffer.h',
         '../src/gpu/GrGLCreateNativeInterface_none.cpp',
+        '../src/gpu/GrGLCreateNullInterface.cpp',
         '../src/gpu/GrGLDefaultInterface_none.cpp',
         '../src/gpu/GrGLDefaultInterface_native.cpp',
         '../src/gpu/GrGLIndexBuffer.cpp',
diff --git a/include/gpu/GrGLInterface.h b/include/gpu/GrGLInterface.h
index 20b74ab..5d22f95 100644
--- a/include/gpu/GrGLInterface.h
+++ b/include/gpu/GrGLInterface.h
@@ -82,6 +82,12 @@
  */
 const GrGLInterface* GrGLCreateMesaInterface();
 
+/**
+ * Creates a null GrGLInterface that doesn't draw anything. Used for measuring
+ * CPU overhead.
+ */
+const GrGLInterface* GrGLCreateNullInterface();
+
 typedef unsigned int GrGLenum;
 typedef unsigned char GrGLboolean;
 typedef unsigned int GrGLbitfield;
diff --git a/include/gpu/SkMesaGLContext.h b/include/gpu/SkMesaGLContext.h
index b7578f3..5c329ff 100644
--- a/include/gpu/SkMesaGLContext.h
+++ b/include/gpu/SkMesaGLContext.h
@@ -38,7 +38,7 @@
 
 protected:
     virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
-    void destroyGLContext() SK_OVERRIDE;
+    virtual void destroyGLContext() SK_OVERRIDE;
 
 private:
     Context fContext;
diff --git a/include/gpu/SkNativeGLContext.h b/include/gpu/SkNativeGLContext.h
index f2037cf..4ab58fb 100644
--- a/include/gpu/SkNativeGLContext.h
+++ b/include/gpu/SkNativeGLContext.h
@@ -48,7 +48,7 @@
 
 protected:
     virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
-    void destroyGLContext() SK_OVERRIDE;
+    virtual void destroyGLContext() SK_OVERRIDE;
 
 private:
 #if defined(SK_BUILD_FOR_MAC)
diff --git a/include/gpu/SkNullGLContext.h b/include/gpu/SkNullGLContext.h
new file mode 100644
index 0000000..9e16cee
--- /dev/null
+++ b/include/gpu/SkNullGLContext.h
@@ -0,0 +1,27 @@
+
+/*
+ * 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 SkNullGLContext_DEFINED
+#define SkNullGLContext_DEFINED
+
+#include "SkGLContext.h"
+
+class SkNullGLContext : public SkGLContext {
+
+public:
+    SkNullGLContext() {};
+
+    virtual void makeCurrent() const SK_OVERRIDE {};
+
+protected:
+    virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
+
+    virtual void destroyGLContext() SK_OVERRIDE {};
+};
+
+#endif
+
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
index 441d2fa..b0d471d 100644
--- a/samplecode/SampleApp.cpp
+++ b/samplecode/SampleApp.cpp
@@ -82,26 +82,40 @@
         fGrRenderTarget = NULL;
         fGrContext = NULL;
         fGL = NULL;
+        fNullGrContext = NULL;
+        fNullGrRenderTarget = NULL;
     }
 
     virtual ~DefaultDeviceManager() {
         SkSafeUnref(fGrRenderTarget);
         SkSafeUnref(fGrContext);
         SkSafeUnref(fGL);
+        SkSafeUnref(fNullGrContext);
+        SkSafeUnref(fNullGrRenderTarget);
     }
 
     virtual void init(SampleWindow* win) {
-        win->attachGL();
-        if (NULL == fGrContext) {
-            fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, NULL);
+        if (!win->attachGL()) {
+            SkDebugf("Failed to initialize GL");
         }
         if (NULL == fGL) {
-            fGL = GrGLDefaultInterface();
+            fGL = GrGLCreateNativeInterface();
+            GrAssert(NULL == fGrContext);
+            fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine,
+                                           (GrPlatform3DContext) fGL);
         }
         if (NULL == fGrContext || NULL == fGL) {
+            SkSafeUnref(fGrContext);
+            SkSafeUnref(fGL);
             SkDebugf("Failed to setup 3D");
             win->detachGL();
         }
+        if (NULL == fNullGrContext) {
+            const GrGLInterface* nullGL = GrGLCreateNullInterface();
+            fNullGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine,
+                                               (GrPlatform3DContext) nullGL);
+            nullGL->unref();
+        }
     }
 
     virtual bool supportsDeviceType(SampleWindow::DeviceType dType) {
@@ -111,6 +125,8 @@
                 return true;
             case kGPU_DeviceType:
                 return NULL != fGrContext && NULL != fGrRenderTarget;
+            case kNullGPU_DeviceType:
+                return NULL != fNullGrContext && NULL != fNullGrRenderTarget;
             default:
                 return false;
         }
@@ -119,13 +135,23 @@
     virtual bool prepareCanvas(SampleWindow::DeviceType dType,
                                SkCanvas* canvas,
                                SampleWindow* win) {
-        if (kGPU_DeviceType == dType) {
-            if (fGrContext) {
-                canvas->setDevice(new SkGpuDevice(fGrContext,
-                                                fGrRenderTarget))->unref();
-            } else {
-                return false;
-            }
+        switch (dType) {
+            case kGPU_DeviceType:
+                if (fGrContext) {
+                    canvas->setDevice(new SkGpuDevice(fGrContext,
+                                                    fGrRenderTarget))->unref();
+                } else {
+                    return false;
+                }
+                break;
+            case kNullGPU_DeviceType:
+                if (fNullGrContext) {
+                    canvas->setDevice(new SkGpuDevice(fNullGrContext,
+                                                      fNullGrRenderTarget))->unref();
+                } else {
+                    return false;
+                }
+                break;
         }
         return true;
     }
@@ -136,7 +162,11 @@
         if (fGrContext) {
             // in case we have queued drawing calls
             fGrContext->flush();
-            if (dType != kGPU_DeviceType) {
+            if (NULL != fNullGrContext) {
+                fNullGrContext->flush();
+            }
+            if (dType != kGPU_DeviceType &&
+                dType != kNullGPU_DeviceType) {
                 // need to send the raster bits to the (gpu) window
                 fGrContext->setRenderTarget(fGrRenderTarget);
                 const SkBitmap& bm = win->getBitmap();
@@ -168,15 +198,34 @@
             fGrRenderTarget = static_cast<GrRenderTarget*>(
                                             fGrContext->createPlatformSurface(desc));
         }
+        if (NULL != fNullGrContext) {
+            GrPlatformSurfaceDesc desc;
+            desc.reset();
+            desc.fSurfaceType = kRenderTarget_GrPlatformSurfaceType;
+            desc.fWidth = SkScalarRound(win->width());
+            desc.fHeight = SkScalarRound(win->height());
+            desc.fConfig = kRGBA_8888_GrPixelConfig;
+            desc.fStencilBits = 8;
+            desc.fSampleCnt = 0;
+            desc.fPlatformRenderTarget = 0;
+            fNullGrRenderTarget = static_cast<GrRenderTarget*>(
+                                            fNullGrContext->createPlatformSurface(desc));
+        }
     }
 
-    virtual GrContext* getGrContext() {
-        return fGrContext;
+    virtual GrContext* getGrContext(SampleWindow::DeviceType dType) {
+        if (kNullGPU_DeviceType == dType) {
+            return fNullGrContext;
+        } else {
+            return fGrContext;
+        }
     }
 private:
     GrContext* fGrContext;
     const GrGLInterface* fGL;
     GrRenderTarget* fGrRenderTarget;
+    GrContext* fNullGrContext;
+    GrRenderTarget* fNullGrRenderTarget;
 };
 
 ///////////////
@@ -410,6 +459,7 @@
     static const SampleWindow::DeviceType gCT[] = {
         SampleWindow::kPicture_DeviceType,
         SampleWindow::kGPU_DeviceType,
+        SampleWindow::kRaster_DeviceType, // skip the null gpu device in normal cycling
         SampleWindow::kRaster_DeviceType
     };
     return gCT[ct];
@@ -1340,6 +1390,13 @@
             this->inval(NULL);
             this->updateTitle();
             return true;
+        case '\\':
+            if (fDevManager->supportsDeviceType(kNullGPU_DeviceType)) {
+                fDeviceType=  kNullGPU_DeviceType;
+                this->inval(NULL);
+                this->updateTitle();
+            }
+            return true;
         case 's':
             fScale = !fScale;
             this->inval(NULL);
@@ -1550,7 +1607,8 @@
 static const char* gDeviceTypePrefix[] = {
     "raster: ",
     "picture: ",
-    "opengl: "
+    "opengl: ",
+    "null-gl: "
 };
 
 static const char* trystate_str(SkOSMenu::TriState state,
diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h
index 1d7e66b..dba022a 100644
--- a/samplecode/SampleApp.h
+++ b/samplecode/SampleApp.h
@@ -35,7 +35,8 @@
     enum DeviceType {
         kRaster_DeviceType,
         kPicture_DeviceType,
-        kGPU_DeviceType
+        kGPU_DeviceType,
+        kNullGPU_DeviceType
     };
     /**
      * SampleApp ports can subclass this manager class if they want to:
@@ -70,7 +71,7 @@
         virtual void windowSizeChanged(SampleWindow* win) = 0;
 
         // return the GrContext backing gpu devices
-        virtual GrContext* getGrContext() = 0;
+        virtual GrContext* getGrContext(DeviceType dType) = 0;
     };
 
     SampleWindow(void* hwnd, int argc, char** argv, DeviceManager*);
@@ -84,7 +85,7 @@
     void toggleFPS();
     void showOverview();
 
-    GrContext* getGrContext() const { return fDevManager->getGrContext(); }
+    GrContext* getGrContext() const { return fDevManager->getGrContext(fDeviceType); }
 
     void setZoomCenter(float x, float y);
     void changeZoomLevel(float delta);
diff --git a/src/gpu/GrGLCreateNullInterface.cpp b/src/gpu/GrGLCreateNullInterface.cpp
new file mode 100644
index 0000000..b2b3720
--- /dev/null
+++ b/src/gpu/GrGLCreateNullInterface.cpp
@@ -0,0 +1,528 @@
+
+/*
+ * 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"
+
+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 nullGLClientActiveTexture(GrGLenum texture) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLColor4ub(GrGLubyte red, GrGLubyte green, GrGLubyte blue, GrGLubyte alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLColorMask(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLColorPointer(GrGLint size, GrGLenum type, GrGLsizei stride, const GrGLvoid* pointer) {}
+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 nullGLDisableClientState(GrGLenum array) {}
+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 nullGLEnableClientState(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 nullGLLoadMatrixf(const GrGLfloat* m) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLMatrixMode(GrGLenum mode) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLPixelStorei(GrGLenum pname, GrGLint param) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLPointSize(GrGLfloat size) {}
+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 nullGLShadeModel(GrGLenum mode) {}
+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 nullGLTexCoordPointer(GrGLint size, GrGLenum type, GrGLsizei stride, const GrGLvoid* pointer) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexEnvi(GrGLenum target, GrGLenum pname, GrGLint param) {}
+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 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 nullGLVertexPointer(GrGLint size, GrGLenum type, GrGLsizei stride, const GrGLvoid* pointer) {}
+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.
+#include "GrTDArray.h"
+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->fClientActiveTexture = nullGLClientActiveTexture;
+        interface->fColor4ub = nullGLColor4ub;
+        interface->fColorMask = nullGLColorMask;
+        interface->fColorPointer = nullGLColorPointer;
+        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->fDisableClientState = nullGLDisableClientState;
+        interface->fDisableVertexAttribArray = nullGLDisableVertexAttribArray;
+        interface->fDrawArrays = nullGLDrawArrays;
+        interface->fDrawBuffer = nullGLDrawBuffer;
+        interface->fDrawBuffers = nullGLDrawBuffers;
+        interface->fDrawElements = nullGLDrawElements;
+        interface->fEnable = nullGLEnable;
+        interface->fEnableClientState = nullGLEnableClientState;
+        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->fLoadMatrixf = nullGLLoadMatrixf;
+        interface->fMatrixMode = nullGLMatrixMode;
+        interface->fPixelStorei = nullGLPixelStorei;
+        interface->fPointSize = nullGLPointSize;
+        interface->fQueryCounter = nullGLQueryCounter;
+        interface->fReadBuffer = nullGLReadBuffer;
+        interface->fReadPixels = nullGLReadPixels;
+        interface->fScissor = nullGLScissor;
+        interface->fShadeModel = nullGLShadeModel;
+        interface->fShaderSource = nullGLShaderSource;
+        interface->fStencilFunc = nullGLStencilFunc;
+        interface->fStencilFuncSeparate = nullGLStencilFuncSeparate;
+        interface->fStencilMask = nullGLStencilMask;
+        interface->fStencilMaskSeparate = nullGLStencilMaskSeparate;
+        interface->fStencilOp = nullGLStencilOp;
+        interface->fStencilOpSeparate = nullGLStencilOpSeparate;
+        interface->fTexCoordPointer = nullGLTexCoordPointer;
+        interface->fTexEnvi = nullGLTexEnvi;
+        interface->fTexImage2D = nullGLTexImage2D;
+        interface->fTexParameteri = nullGLTexParameteri;
+        interface->fTexSubImage2D = nullGLTexSubImage2D;
+        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->fVertexPointer = nullGLVertexPointer;
+        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/SkNullGLContext.cpp b/src/gpu/SkNullGLContext.cpp
new file mode 100644
index 0000000..04e63d8
--- /dev/null
+++ b/src/gpu/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();
+};
diff --git a/tests/GLInterfaceValidation.cpp b/tests/GLInterfaceValidation.cpp
index 758f303..1308120 100755
--- a/tests/GLInterfaceValidation.cpp
+++ b/tests/GLInterfaceValidation.cpp
@@ -20,6 +20,7 @@
 #if SK_MESA
         {GrGLCreateMesaInterface, "Mesa"},
 #endif
+        {GrGLCreateNullInterface, "Null"},
     };
 
     // On some platforms GrGLCreateNativeInterface will fail unless an OpenGL