Add support for glMapBufferRange. Use glMapBufferRange and glMapBufferSubData.

BUG=skia:2402

Committed: http://code.google.com/p/skia/source/detail?r=14533

R=robertphillips@google.com, djsollen@google.com

Author: bsalomon@google.com

Review URL: https://codereview.chromium.org/243413002

git-svn-id: http://skia.googlecode.com/svn/trunk@14564 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/gpu/gl/GrGLAssembleInterface.cpp b/src/gpu/gl/GrGLAssembleInterface.cpp
index aed11e5..e433725 100644
--- a/src/gpu/gl/GrGLAssembleInterface.cpp
+++ b/src/gpu/gl/GrGLAssembleInterface.cpp
@@ -173,6 +173,11 @@
         GET_PROC(DeleteVertexArrays);
     }
 
+    if (glVer >= GR_GL_VER(3,0) || extensions.has("GL_ARB_map_buffer_range")) {
+        GET_PROC(MapBufferRange);
+        GET_PROC(FlushMappedBufferRange);
+    }
+
     // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
     // GL_ARB_framebuffer_object doesn't use ARB suffix.)
     if (glVer >= GR_GL_VER(3,0) || extensions.has("GL_ARB_framebuffer_object")) {
diff --git a/src/gpu/gl/GrGLBufferImpl.cpp b/src/gpu/gl/GrGLBufferImpl.cpp
index 3c75b9f..46e1f79 100644
--- a/src/gpu/gl/GrGLBufferImpl.cpp
+++ b/src/gpu/gl/GrGLBufferImpl.cpp
@@ -26,20 +26,22 @@
     , fLockPtr(NULL) {
     if (0 == desc.fID) {
         fCPUData = sk_malloc_flags(desc.fSizeInBytes, SK_MALLOC_THROW);
+        fGLSizeInBytes = 0;
     } else {
         fCPUData = NULL;
+        // We assume that the GL buffer was created at the desc's size initially.
+        fGLSizeInBytes = fDesc.fSizeInBytes;
     }
     VALIDATE();
 }
 
 void GrGLBufferImpl::release(GrGpuGL* gpu) {
+    VALIDATE();
     // make sure we've not been abandoned or already released
     if (NULL != fCPUData) {
-        VALIDATE();
         sk_free(fCPUData);
         fCPUData = NULL;
     } else if (fDesc.fID && !fDesc.fIsWrapped) {
-        VALIDATE();
         GL_CALL(gpu, DeleteBuffers(1, &fDesc.fID));
         if (GR_GL_ARRAY_BUFFER == fBufferType) {
             gpu->notifyVertexBufferDelete(fDesc.fID);
@@ -48,15 +50,19 @@
             gpu->notifyIndexBufferDelete(fDesc.fID);
         }
         fDesc.fID = 0;
+        fGLSizeInBytes = 0;
     }
     fLockPtr = NULL;
+    VALIDATE();
 }
 
 void GrGLBufferImpl::abandon() {
     fDesc.fID = 0;
+    fGLSizeInBytes = 0;
     fLockPtr = NULL;
     sk_free(fCPUData);
     fCPUData = NULL;
+    VALIDATE();
 }
 
 void GrGLBufferImpl::bind(GrGpuGL* gpu) const {
@@ -67,6 +73,7 @@
         SkASSERT(GR_GL_ELEMENT_ARRAY_BUFFER == fBufferType);
         gpu->bindIndexBufferAndDefaultVertexArray(fDesc.fID);
     }
+    VALIDATE();
 }
 
 void* GrGLBufferImpl::lock(GrGpuGL* gpu) {
@@ -74,17 +81,55 @@
     SkASSERT(!this->isLocked());
     if (0 == fDesc.fID) {
         fLockPtr = fCPUData;
-    } else if (gpu->caps()->bufferLockSupport()) {
-        this->bind(gpu);
-        // Let driver know it can discard the old data
-        GL_CALL(gpu, BufferData(fBufferType,
-                                (GrGLsizeiptr) fDesc.fSizeInBytes,
-                                NULL,
-                                fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW));
-        GR_GL_CALL_RET(gpu->glInterface(),
-                       fLockPtr,
-                       MapBuffer(fBufferType, GR_GL_WRITE_ONLY));
+    } else {
+        switch (gpu->glCaps().mapBufferType()) {
+            case GrGLCaps::kNone_MapBufferType:
+                VALIDATE();
+                return NULL;
+            case GrGLCaps::kMapBuffer_MapBufferType:
+                this->bind(gpu);
+                // Let driver know it can discard the old data
+                if (GR_GL_USE_BUFFER_DATA_NULL_HINT || fDesc.fSizeInBytes != fGLSizeInBytes) {
+                    fGLSizeInBytes = fDesc.fSizeInBytes;
+                    GL_CALL(gpu,
+                            BufferData(fBufferType, fGLSizeInBytes, NULL,
+                                       fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW));
+                }
+                GR_GL_CALL_RET(gpu->glInterface(), fLockPtr,
+                               MapBuffer(fBufferType, GR_GL_WRITE_ONLY));
+                break;
+            case GrGLCaps::kMapBufferRange_MapBufferType: {
+                this->bind(gpu);
+                // Make sure the GL buffer size agrees with fDesc before mapping.
+                if (fDesc.fSizeInBytes != fGLSizeInBytes) {
+                    fGLSizeInBytes = fDesc.fSizeInBytes;
+                    GL_CALL(gpu,
+                            BufferData(fBufferType, fGLSizeInBytes, NULL,
+                                       fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW));
+                }
+                static const GrGLbitfield kAccess = GR_GL_MAP_INVALIDATE_BUFFER_BIT |
+                                                    GR_GL_MAP_WRITE_BIT;
+                GR_GL_CALL_RET(gpu->glInterface(),
+                               fLockPtr,
+                               MapBufferRange(fBufferType, 0, fGLSizeInBytes, kAccess));
+                break;
+            }
+            case GrGLCaps::kChromium_MapBufferType:
+                this->bind(gpu);
+                // Make sure the GL buffer size agrees with fDesc before mapping.
+                if (fDesc.fSizeInBytes != fGLSizeInBytes) {
+                    fGLSizeInBytes = fDesc.fSizeInBytes;
+                    GL_CALL(gpu,
+                            BufferData(fBufferType, fGLSizeInBytes, NULL,
+                                       fDesc.fDynamic ? DYNAMIC_USAGE_PARAM : GR_GL_STATIC_DRAW));
+                }
+                GR_GL_CALL_RET(gpu->glInterface(),
+                               fLockPtr,
+                               MapBufferSubData(fBufferType, 0, fGLSizeInBytes, GR_GL_WRITE_ONLY));
+                break;
+        }
     }
+    VALIDATE();
     return fLockPtr;
 }
 
@@ -92,9 +137,20 @@
     VALIDATE();
     SkASSERT(this->isLocked());
     if (0 != fDesc.fID) {
-        SkASSERT(gpu->caps()->bufferLockSupport());
-        this->bind(gpu);
-        GL_CALL(gpu, UnmapBuffer(fBufferType));
+        switch (gpu->glCaps().mapBufferType()) {
+            case GrGLCaps::kNone_MapBufferType:
+                SkDEBUGFAIL("Shouldn't get here.");
+                return;
+            case GrGLCaps::kMapBuffer_MapBufferType: // fall through
+            case GrGLCaps::kMapBufferRange_MapBufferType:
+                this->bind(gpu);
+                GL_CALL(gpu, UnmapBuffer(fBufferType));
+                break;
+            case GrGLCaps::kChromium_MapBufferType:
+                this->bind(gpu);
+                GR_GL_CALL(gpu->glInterface(), UnmapBufferSubData(fLockPtr));
+                break;
+        }
     }
     fLockPtr = NULL;
 }
@@ -127,7 +183,8 @@
         // 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(gpu, BufferData(fBufferType, (GrGLsizeiptr) fDesc.fSizeInBytes, NULL, usage));
+        fGLSizeInBytes = fDesc.fSizeInBytes;
+        GL_CALL(gpu, BufferData(fBufferType, fGLSizeInBytes, NULL, usage));
         GL_CALL(gpu, BufferSubData(fBufferType, 0, (GrGLsizeiptr) srcSizeInBytes, src));
     }
 #else
@@ -147,10 +204,12 @@
         // Chromium's command buffer may turn a glBufferSubData where the size
         // exactly matches the buffer size into a glBufferData. So we tack 1
         // extra byte onto the glBufferData.
-        GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes + 1, NULL, usage));
+        fGLSizeInBytes = srcSizeInBytes + 1;
+        GL_CALL(gpu, BufferData(fBufferType, fGLSizeInBytes, NULL, usage));
         GL_CALL(gpu, BufferSubData(fBufferType, 0, srcSizeInBytes, src));
     } else {
-        GL_CALL(gpu, BufferData(fBufferType, srcSizeInBytes, src, usage));
+        fGLSizeInBytes = srcSizeInBytes;
+        GL_CALL(gpu, BufferData(fBufferType, fGLSizeInBytes, src, usage));
     }
 #endif
     return true;
@@ -161,5 +220,7 @@
     // The following assert isn't valid when the buffer has been abandoned:
     // SkASSERT((0 == fDesc.fID) == (NULL != fCPUData));
     SkASSERT(0 != fDesc.fID || !fDesc.fIsWrapped);
+    SkASSERT(NULL == fCPUData || 0 == fGLSizeInBytes);
+    SkASSERT(NULL == fLockPtr || NULL != fCPUData || fGLSizeInBytes == fDesc.fSizeInBytes);
     SkASSERT(NULL == fCPUData || NULL == fLockPtr || fCPUData == fLockPtr);
 }
diff --git a/src/gpu/gl/GrGLBufferImpl.h b/src/gpu/gl/GrGLBufferImpl.h
index 148ca1b..19d23e0 100644
--- a/src/gpu/gl/GrGLBufferImpl.h
+++ b/src/gpu/gl/GrGLBufferImpl.h
@@ -53,6 +53,8 @@
     GrGLenum     fBufferType; // GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER
     void*        fCPUData;
     void*        fLockPtr;
+    size_t       fGLSizeInBytes;     // In certain cases we make the size of the GL buffer object
+                                     // smaller or larger than the size in fDesc.
 
     typedef SkNoncopyable INHERITED;
 };
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 890b816..f577e9d 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -24,6 +24,7 @@
     fMSFBOType = kNone_MSFBOType;
     fFBFetchType = kNone_FBFetchType;
     fInvalidateFBType = kNone_InvalidateFBType;
+    fMapBufferType = kNone_MapBufferType;
     fMaxFragmentUniformVectors = 0;
     fMaxVertexAttributes = 0;
     fMaxFragmentTextureUnits = 0;
@@ -47,7 +48,6 @@
     fIsCoreProfile = false;
     fFullClearIsFree = false;
     fDropsTileOnZeroDivide = false;
-    fMapSubSupport = false;
 }
 
 GrGLCaps::GrGLCaps(const GrGLCaps& caps) : GrDrawTargetCaps() {
@@ -66,6 +66,7 @@
     fMSFBOType = caps.fMSFBOType;
     fFBFetchType = caps.fFBFetchType;
     fInvalidateFBType = caps.fInvalidateFBType;
+    fMapBufferType = caps.fMapBufferType;
     fRGBA8RenderbufferSupport = caps.fRGBA8RenderbufferSupport;
     fBGRAFormatSupport = caps.fBGRAFormatSupport;
     fBGRAIsInternalFormat = caps.fBGRAIsInternalFormat;
@@ -85,7 +86,6 @@
     fIsCoreProfile = caps.fIsCoreProfile;
     fFullClearIsFree = caps.fFullClearIsFree;
     fDropsTileOnZeroDivide = caps.fDropsTileOnZeroDivide;
-    fMapSubSupport = caps.fMapSubSupport;
 
     return *this;
 }
@@ -290,12 +290,27 @@
     }
 
     if (kGL_GrGLStandard == standard) {
-        fBufferLockSupport = true; // we require VBO support and the desktop VBO extension includes
-                                   // glMapBuffer.
-        fMapSubSupport = false;
+        fMapBufferFlags = kCanMap_MapFlag; // we require VBO support and the desktop VBO
+                                            // extension includes glMapBuffer.
+        if (version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_ARB_map_buffer_range")) {
+            fMapBufferFlags |= kSubset_MapFlag;
+            fMapBufferType = kMapBufferRange_MapBufferType;
+        } else {
+            fMapBufferType = kMapBuffer_MapBufferType;
+        }
     } else {
-        fBufferLockSupport = ctxInfo.hasExtension("GL_OES_mapbuffer");
-        fMapSubSupport = ctxInfo.hasExtension("GL_CHROMIUM_map_sub");
+        // Unextended GLES2 doesn't have any buffer mapping.
+        fMapBufferFlags = kNone_MapBufferType;
+        if (ctxInfo.hasExtension("GL_CHROMIUM_map_sub")) {
+            fMapBufferFlags = kCanMap_MapFlag | kSubset_MapFlag;
+            fMapBufferType = kChromium_MapBufferType;
+        } else if (version >= GR_GL_VER(3, 0) || ctxInfo.hasExtension("GL_EXT_map_buffer_range")) {
+            fMapBufferFlags = kCanMap_MapFlag | kSubset_MapFlag;
+            fMapBufferType = kMapBufferRange_MapBufferType;
+        } else if (ctxInfo.hasExtension("GL_OES_mapbuffer")) {
+            fMapBufferFlags = kCanMap_MapFlag;
+            fMapBufferType = kMapBuffer_MapBufferType;
+        }
     }
 
     if (kGL_GrGLStandard == standard) {
@@ -655,10 +670,23 @@
     GR_STATIC_ASSERT(2 == kInvalidate_InvalidateFBType);
     GR_STATIC_ASSERT(SK_ARRAY_COUNT(kInvalidateFBTypeStr) == kLast_InvalidateFBType + 1);
 
+    static const char* kMapBufferTypeStr[] = {
+        "None",
+        "MapBuffer",
+        "MapBufferRange",
+        "Chromium",
+    };
+    GR_STATIC_ASSERT(0 == kNone_MapBufferType);
+    GR_STATIC_ASSERT(1 == kMapBuffer_MapBufferType);
+    GR_STATIC_ASSERT(2 == kMapBufferRange_MapBufferType);
+    GR_STATIC_ASSERT(3 == kChromium_MapBufferType);
+    GR_STATIC_ASSERT(SK_ARRAY_COUNT(kMapBufferTypeStr) == kLast_MapBufferType + 1);
+
     r.appendf("Core Profile: %s\n", (fIsCoreProfile ? "YES" : "NO"));
     r.appendf("MSAA Type: %s\n", kMSFBOExtStr[fMSFBOType]);
     r.appendf("FB Fetch Type: %s\n", kFBFetchTypeStr[fFBFetchType]);
     r.appendf("Invalidate FB Type: %s\n", kInvalidateFBTypeStr[fInvalidateFBType]);
+    r.appendf("Map Buffer Type: %s\n", kMapBufferTypeStr[fMapBufferType]);
     r.appendf("Max FS Uniform Vectors: %d\n", fMaxFragmentUniformVectors);
     r.appendf("Max FS Texture Units: %d\n", fMaxFragmentTextureUnits);
     if (!fIsCoreProfile) {
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 48925d4..ea0f412 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -86,6 +86,15 @@
         kLast_InvalidateFBType = kInvalidate_InvalidateFBType
     };
 
+    enum MapBufferType {
+        kNone_MapBufferType,
+        kMapBuffer_MapBufferType,         // glMapBuffer()
+        kMapBufferRange_MapBufferType,    // glMapBufferRange()
+        kChromium_MapBufferType,          // GL_CHROMIUM_map_sub
+
+        kLast_MapBufferType = kChromium_MapBufferType,
+    };
+
     /**
      * Creates a GrGLCaps that advertises no support for any extensions,
      * formats, etc. Call init to initialize from a GrGLContextInfo.
@@ -169,10 +178,8 @@
 
     InvalidateFBType invalidateFBType() const { return fInvalidateFBType; }
 
-    /**
-     * Returs a string containeng the caps info.
-     */
-    virtual SkString dump() const SK_OVERRIDE;
+    /// What type of buffer mapping is supported?
+    MapBufferType mapBufferType() const { return fMapBufferType; }
 
     /**
      * Gets an array of legal stencil formats. These formats are not guaranteed
@@ -258,8 +265,10 @@
 
     bool dropsTileOnZeroDivide() const { return fDropsTileOnZeroDivide; }
 
-    /// Is GL_CHROMIUM_map_sub supported?
-    bool mapSubSupport() const { return fMapSubSupport; }
+    /**
+     * Returns a string containing the caps info.
+     */
+    virtual SkString dump() const SK_OVERRIDE;
 
 private:
     /**
@@ -322,6 +331,7 @@
     MSFBOType           fMSFBOType;
     FBFetchType         fFBFetchType;
     InvalidateFBType    fInvalidateFBType;
+    MapBufferType       fMapBufferType;
 
     bool fRGBA8RenderbufferSupport : 1;
     bool fBGRAFormatSupport : 1;
@@ -342,7 +352,6 @@
     bool fIsCoreProfile : 1;
     bool fFullClearIsFree : 1;
     bool fDropsTileOnZeroDivide : 1;
-    bool fMapSubSupport : 1;
 
     typedef GrDrawTargetCaps INHERITED;
 };
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
index 2ef7659..6cfa8c2 100644
--- a/src/gpu/gl/GrGLCreateNullInterface.cpp
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -186,8 +186,29 @@
     }
 }
 
-GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
+GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBufferRange(GrGLenum target, GrGLintptr offset,
+                                                   GrGLsizeiptr length, GrGLbitfield access) {
+    GrGLuint id = 0;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            id = gCurrArrayBuffer;
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            id = gCurrElementArrayBuffer;
+            break;
+    }
 
+    if (id > 0) {
+        // We just ignore the offset and length here.
+        GrBufferObj* buffer = look_up(id);
+        SkASSERT(!buffer->mapped());
+        buffer->setMapped(true);
+        return buffer->dataPtr();
+    }
+    return NULL;
+}
+
+GrGLvoid* GR_GL_FUNCTION_TYPE nullGLMapBuffer(GrGLenum target, GrGLenum access) {
     GrGLuint id = 0;
     switch (target) {
         case GR_GL_ARRAY_BUFFER:
@@ -209,6 +230,11 @@
     return NULL;            // no buffer bound to target
 }
 
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLFlushMappedBufferRange(GrGLenum target,
+                                                          GrGLintptr offset,
+                                                          GrGLsizeiptr length) {}
+
+
 GrGLboolean GR_GL_FUNCTION_TYPE nullGLUnmapBuffer(GrGLenum target) {
     GrGLuint id = 0;
     switch (target) {
@@ -304,6 +330,7 @@
     functions->fEndQuery = noOpGLEndQuery;
     functions->fFinish = noOpGLFinish;
     functions->fFlush = noOpGLFlush;
+    functions->fFlushMappedBufferRange = nullGLFlushMappedBufferRange;
     functions->fFrontFace = noOpGLFrontFace;
     functions->fGenBuffers = nullGLGenBuffers;
     functions->fGenerateMipmap = nullGLGenerateMipmap;
@@ -329,6 +356,8 @@
     functions->fInsertEventMarker = noOpGLInsertEventMarker;
     functions->fLineWidth = noOpGLLineWidth;
     functions->fLinkProgram = noOpGLLinkProgram;
+    functions->fMapBuffer = nullGLMapBuffer;
+    functions->fMapBufferRange = nullGLMapBufferRange;
     functions->fPixelStorei = nullGLPixelStorei;
     functions->fPopGroupMarker = noOpGLPopGroupMarker;
     functions->fPushGroupMarker = noOpGLPushGroupMarker;
@@ -368,6 +397,7 @@
     functions->fUniformMatrix2fv = noOpGLUniformMatrix2fv;
     functions->fUniformMatrix3fv = noOpGLUniformMatrix3fv;
     functions->fUniformMatrix4fv = noOpGLUniformMatrix4fv;
+    functions->fUnmapBuffer = nullGLUnmapBuffer;
     functions->fUseProgram = nullGLUseProgram;
     functions->fVertexAttrib4fv = noOpGLVertexAttrib4fv;
     functions->fVertexAttribPointer = noOpGLVertexAttribPointer;
@@ -387,10 +417,8 @@
     functions->fRenderbufferStorageMultisample = noOpGLRenderbufferStorageMultisample;
     functions->fBlitFramebuffer = noOpGLBlitFramebuffer;
     functions->fResolveMultisampleFramebuffer = noOpGLResolveMultisampleFramebuffer;
-    functions->fMapBuffer = nullGLMapBuffer;
     functions->fMatrixLoadf = noOpGLMatrixLoadf;
     functions->fMatrixLoadIdentity = noOpGLMatrixLoadIdentity;
-    functions->fUnmapBuffer = nullGLUnmapBuffer;
     functions->fBindFragDataLocationIndexed = noOpGLBindFragDataLocationIndexed;
 
     interface->fExtensions.init(kGL_GrGLStandard, functions->fGetString, functions->fGetStringi,
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
index a4dc2f7..73f3d2e 100644
--- a/src/gpu/gl/GrGLDefines.h
+++ b/src/gpu/gl/GrGLDefines.h
@@ -601,6 +601,14 @@
 /* Vertex Buffer Object */
 #define GR_GL_WRITE_ONLY                         0x88B9
 #define GR_GL_BUFFER_MAPPED                      0x88BC
+
+#define GR_GL_MAP_READ_BIT                       0x0001
+#define GR_GL_MAP_WRITE_BIT                      0x0002
+#define GR_GL_MAP_INVALIDATE_RANGE_BIT           0x0004
+#define GR_GL_MAP_INVALIDATE_BUFFER_BIT          0x0008
+#define GR_GL_MAP_FLUSH_EXPLICIT_BIT             0x0010
+#define GR_GL_MAP_UNSYNCHRONIZED_BIT             0x0020
+
 /* Read Format */
 #define GR_GL_IMPLEMENTATION_COLOR_READ_TYPE   0x8B9A
 #define GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index 056a601..ee184d0 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -486,8 +486,8 @@
         }
     }
 
-#if 0 // This can be enabled once Chromium is updated to set these functions pointers.
-    if ((kGL_GrGLStandard == fStandard) || fExtensions.has("GL_ARB_invalidate_subdata")) {
+    if ((kGL_GrGLStandard == fStandard && glVer >= GR_GL_VER(4,3)) ||
+        fExtensions.has("GL_ARB_invalidate_subdata")) {
         if (NULL == fFunctions.fInvalidateBufferData ||
             NULL == fFunctions.fInvalidateBufferSubData ||
             NULL == fFunctions.fInvalidateFramebuffer ||
@@ -496,7 +496,7 @@
             NULL == fFunctions.fInvalidateTexSubImage) {
             RETURN_FALSE_INTERFACE;
         }
-    } else if (glVer >= GR_GL_VER(3,0)) {
+    } else if (kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0)) {
         // ES 3.0 adds the framebuffer functions but not the others.
         if (NULL == fFunctions.fInvalidateFramebuffer ||
             NULL == fFunctions.fInvalidateSubFramebuffer) {
@@ -512,7 +512,15 @@
             RETURN_FALSE_INTERFACE;
         }
     }
-#endif
 
+    // These functions are added to the 3.0 version of both GLES and GL.
+    if (glVer >= GR_GL_VER(3,0) ||
+        (kGLES_GrGLStandard == fStandard && fExtensions.has("GL_EXT_map_buffer_range")) ||
+        (kGL_GrGLStandard == fStandard && fExtensions.has("GL_ARB_map_buffer_range"))) {
+        if (NULL == fFunctions.fMapBufferRange ||
+            NULL == fFunctions.fFlushMappedBufferRange) {
+            RETURN_FALSE_INTERFACE;
+        }
+    }
     return true;
 }
diff --git a/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
index b50063f..312299a 100644
--- a/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
+++ b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
@@ -75,7 +75,7 @@
     functions->fGetShaderInfoLog = glGetShaderInfoLog;
     functions->fGetShaderiv = glGetShaderiv;
     functions->fGetString = glGetString;
-#if GL_ES_VERSION_30
+#if GL_ES_VERSION_3_0
     functions->fGetStringi = glGetStringi;
 #else
     functions->fGetStringi = (GrGLGetStringiProc) eglGetProcAddress("glGetStringi");
@@ -183,12 +183,24 @@
     functions->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
     functions->fGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
     functions->fRenderbufferStorage = glRenderbufferStorage;
+
 #if GL_OES_mapbuffer
     functions->fMapBuffer = glMapBufferOES;
     functions->fUnmapBuffer = glUnmapBufferOES;
 #else
     functions->fMapBuffer = (GrGLMapBufferProc) eglGetProcAddress("glMapBufferOES");
     functions->fUnmapBuffer = (GrGLUnmapBufferProc) eglGetProcAddress("glUnmapBufferOES");
+
+#endif
+
+#if GL_ES_VERSION_3_0 || GL_EXT_map_buffer_range
+    functions->fMapBufferRange = glMapBufferRange;
+    functions->fFlushMappedBufferRange = glFlushMappedBufferRange;
+#else
+    if (version >= GR_GL_VER(3,0) || extensions->has("GL_EXT_map_buffer_range")) {
+        functions->fMapBufferRange = (GrGLMapBufferRangeProc) eglGetProcAddress("glMapBufferRange");
+        functions->fFlushMappedBufferRange = (GrGLFlushMappedBufferRangeProc) eglGetProcAddress("glFlushMappedBufferRange");
+    }
 #endif
 
     if (extensions->has("GL_EXT_debug_marker")) {
diff --git a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
index a316ff1..cb2fc95 100644
--- a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
+++ b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
@@ -154,6 +154,14 @@
     functions->fMapBuffer = (GrGLMapBufferProc) eglGetProcAddress("glMapBufferOES");
     functions->fUnmapBuffer = (GrGLUnmapBufferProc) eglGetProcAddress("glUnmapBufferOES");
 
+#if GL_ES_VERSION_3_0
+    functions->fMapBufferRange = GET_PROC(glMapBufferRange);
+    functions->fFlushMappedBufferRange = GET_PROC(glFlushMappedBufferRange);
+#else
+    functions->fMapBufferRange = (GrGLMapBufferRangeProc) eglGetProcAddress("glMapBufferRange");
+    functions->fFlushMappedBufferRange = (GrGLFlushMappedBufferRangeProc) eglGetProcAddress("glFlushMappedBufferRange");
+#endif
+
     functions->fInsertEventMarker = (GrGLInsertEventMarkerProc) eglGetProcAddress("glInsertEventMarkerEXT");
     functions->fPushGroupMarker = (GrGLInsertEventMarkerProc) eglGetProcAddress("glPushGroupMarkerEXT");
     functions->fPopGroupMarker = (GrGLPopGroupMarkerProc) eglGetProcAddress("glPopGroupMarkerEXT");
diff --git a/src/gpu/gl/debug/GrBufferObj.h b/src/gpu/gl/debug/GrBufferObj.h
index fecfeb5..05d3cfdd 100644
--- a/src/gpu/gl/debug/GrBufferObj.h
+++ b/src/gpu/gl/debug/GrBufferObj.h
@@ -34,9 +34,15 @@
         GrAlwaysAssert(!fMapped);
     }
 
-    void setMapped()             { fMapped = true; }
+    void setMapped(GrGLintptr offset, GrGLsizeiptr length) {
+        fMapped = true;
+        fMappedOffset = offset;
+        fMappedLength = length;
+    }
     void resetMapped()           { fMapped = false; }
     bool getMapped() const       { return fMapped; }
+    GrGLsizei getMappedOffset() const { return fMappedOffset; }
+    GrGLsizei getMappedLength() const { return fMappedLength; }
 
     void setBound()              { fBound = true; }
     void resetBound()            { fBound = false; }
@@ -55,7 +61,9 @@
 private:
 
     GrGLchar*    fDataPtr;
-    bool         fMapped;       // is the buffer object mapped via "glMapBuffer"?
+    bool         fMapped;       // is the buffer object mapped via "glMapBuffer[Range]"?
+    GrGLintptr   fMappedOffset; // the offset of the buffer range that is mapped
+    GrGLsizeiptr fMappedLength; // the size of the buffer range that is mapped
     bool         fBound;        // is the buffer object bound via "glBindBuffer"?
     GrGLsizeiptr fSize;         // size in bytes
     GrGLint      fUsage;        // one of: GL_STREAM_DRAW,
diff --git a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
index 087bd47..7c430b4 100644
--- a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
+++ b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
@@ -622,11 +622,40 @@
 }
 
 // map a buffer to the caller's address space
-GrGLvoid* GR_GL_FUNCTION_TYPE debugGLMapBuffer(GrGLenum target, GrGLenum access) {
-
+GrGLvoid* GR_GL_FUNCTION_TYPE debugGLMapBufferRange(GrGLenum target, GrGLintptr offset,
+                                                    GrGLsizeiptr length, GrGLbitfield access) {
     GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
                    GR_GL_ELEMENT_ARRAY_BUFFER == target);
-    // GR_GL_READ_ONLY == access ||  || GR_GL_READ_WRIT == access);
+
+    // We only expect read access and we expect that the buffer or range is always invalidated.
+    GrAlwaysAssert(!SkToBool(GR_GL_MAP_READ_BIT & access));
+    GrAlwaysAssert((GR_GL_MAP_INVALIDATE_BUFFER_BIT | GR_GL_MAP_INVALIDATE_RANGE_BIT) & access);
+
+    GrBufferObj *buffer = NULL;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getArrayBuffer();
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getElementArrayBuffer();
+            break;
+        default:
+            SkFAIL("Unexpected target to glMapBufferRange");
+            break;
+    }
+
+    if (NULL != buffer) {
+        GrAlwaysAssert(offset >= 0 && offset + length <= buffer->getSize());
+        GrAlwaysAssert(!buffer->getMapped());
+        buffer->setMapped(offset, length);
+        return buffer->getDataPtr() + offset;
+    }
+
+    GrAlwaysAssert(false);
+    return NULL;        // no buffer bound to the target
+}
+
+GrGLvoid* GR_GL_FUNCTION_TYPE debugGLMapBuffer(GrGLenum target, GrGLenum access) {
     GrAlwaysAssert(GR_GL_WRITE_ONLY == access);
 
     GrBufferObj *buffer = NULL;
@@ -642,14 +671,8 @@
             break;
     }
 
-    if (buffer) {
-        GrAlwaysAssert(!buffer->getMapped());
-        buffer->setMapped();
-        return buffer->getDataPtr();
-    }
-
-    GrAlwaysAssert(false);
-    return NULL;        // no buffer bound to the target
+    return debugGLMapBufferRange(target, 0, buffer->getSize(),
+                                 GR_GL_MAP_WRITE_BIT | GR_GL_MAP_INVALIDATE_BUFFER_BIT);
 }
 
 // remove a buffer from the caller's address space
@@ -672,7 +695,7 @@
             break;
     }
 
-    if (buffer) {
+    if (NULL != buffer) {
         GrAlwaysAssert(buffer->getMapped());
         buffer->resetMapped();
         return GR_GL_TRUE;
@@ -682,6 +705,34 @@
     return GR_GL_FALSE; // GR_GL_INVALID_OPERATION;
 }
 
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLFlushMappedBufferRange(GrGLenum target,
+                                                           GrGLintptr offset,
+                                                           GrGLsizeiptr length) {
+    GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+                   GR_GL_ELEMENT_ARRAY_BUFFER == target);
+
+    GrBufferObj *buffer = NULL;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getArrayBuffer();
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getElementArrayBuffer();
+            break;
+        default:
+            SkFAIL("Unexpected target to glUnmapBuffer");
+            break;
+    }
+
+    if (NULL != buffer) {
+        GrAlwaysAssert(buffer->getMapped());
+        GrAlwaysAssert(offset >= 0 && (offset + length) <= buffer->getMappedLength());
+    } else {
+        GrAlwaysAssert(false);
+    }
+}
+
+
 GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetBufferParameteriv(GrGLenum target,
                                                          GrGLenum value,
                                                          GrGLint* params) {
@@ -706,17 +757,17 @@
     switch (value) {
         case GR_GL_BUFFER_MAPPED:
             *params = GR_GL_FALSE;
-            if (buffer)
+            if (NULL != buffer)
                 *params = buffer->getMapped() ? GR_GL_TRUE : GR_GL_FALSE;
             break;
         case GR_GL_BUFFER_SIZE:
             *params = 0;
-            if (buffer)
+            if (NULL != buffer)
                 *params = SkToInt(buffer->getSize());
             break;
         case GR_GL_BUFFER_USAGE:
             *params = GR_GL_STATIC_DRAW;
-            if (buffer)
+            if (NULL != buffer)
                 *params = buffer->getUsage();
             break;
         default:
@@ -826,6 +877,7 @@
     functions->fEndQuery = noOpGLEndQuery;
     functions->fFinish = noOpGLFinish;
     functions->fFlush = noOpGLFlush;
+    functions->fFlushMappedBufferRange = debugGLFlushMappedBufferRange;
     functions->fFrontFace = noOpGLFrontFace;
     functions->fGenerateMipmap = debugGLGenerateMipmap;
     functions->fGenBuffers = debugGLGenBuffers;
@@ -850,6 +902,8 @@
     functions->fGenVertexArrays = debugGLGenVertexArrays;
     functions->fLineWidth = noOpGLLineWidth;
     functions->fLinkProgram = noOpGLLinkProgram;
+    functions->fMapBuffer = debugGLMapBuffer;
+    functions->fMapBufferRange = debugGLMapBufferRange;
     functions->fPixelStorei = debugGLPixelStorei;
     functions->fQueryCounter = noOpGLQueryCounter;
     functions->fReadBuffer = noOpGLReadBuffer;
@@ -887,6 +941,7 @@
     functions->fUniformMatrix2fv = noOpGLUniformMatrix2fv;
     functions->fUniformMatrix3fv = noOpGLUniformMatrix3fv;
     functions->fUniformMatrix4fv = noOpGLUniformMatrix4fv;
+    functions->fUnmapBuffer = debugGLUnmapBuffer;
     functions->fUseProgram = debugGLUseProgram;
     functions->fVertexAttrib4fv = noOpGLVertexAttrib4fv;
     functions->fVertexAttribPointer = noOpGLVertexAttribPointer;
@@ -909,10 +964,9 @@
     functions->fBlitFramebuffer = noOpGLBlitFramebuffer;
     functions->fResolveMultisampleFramebuffer =
                                     noOpGLResolveMultisampleFramebuffer;
-    functions->fMapBuffer = debugGLMapBuffer;
     functions->fMatrixLoadf = noOpGLMatrixLoadf;
     functions->fMatrixLoadIdentity = noOpGLMatrixLoadIdentity;
-    functions->fUnmapBuffer = debugGLUnmapBuffer;
+
     functions->fBindFragDataLocationIndexed =
                                     noOpGLBindFragDataLocationIndexed;
 
diff --git a/src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp b/src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp
index 6af0471..08e7ac8 100644
--- a/src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp
+++ b/src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp
@@ -132,6 +132,11 @@
     functions->fUnmapBuffer = glUnmapBufferOES;
 #endif
 
+#if GL_EXT_map_buffer_range || GL_ES_VERSION_3_0
+    functions->fMapBufferRange = glMapBufferRangeEXT;
+    functions->fFlushMappedBufferRange = glFlushMappedBufferRangeEXT;
+#endif
+
 #if GL_APPLE_framebuffer_multisample
     functions->fRenderbufferStorageMultisample = glRenderbufferStorageMultisampleAPPLE;
     functions->fResolveMultisampleFramebuffer = glResolveMultisampleFramebufferAPPLE;