Make use of the buffer data null hint a GrContextOption

TBR=bsalomon@google.com
Change-Id: I5a3fd18479ca8c95e1bc8c087c28346264049eb0
Reviewed-on: https://skia-review.googlesource.com/111604
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index 74ec7fe..69ea181 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -132,6 +132,19 @@
     bool fAvoidStencilBuffers = false;
 
     /**
+     * When specifing new data for a vertex/index buffer that replaces old data Ganesh can give
+     * a hint to the driver that the previous data will not be used in future draws like this:
+     *  glBufferData(GL_..._BUFFER, size, NULL, usage);       //<--hint, NULL means
+     *  glBufferSubData(GL_..._BUFFER, 0, lessThanSize, data) //   old data can't be
+     *                                                        //   used again.
+     * However, this can be an unoptimization on some platforms, esp. Chrome.
+     * Chrome's cmd buffer will create a new allocation and memset the whole thing
+     * to zero (for security reasons).
+     * Defaults to the value of GR_GL_USE_BUFFER_DATA_NULL_HINT #define (which is, by default, 1).
+     */
+    Enable fUseGLBufferDataNullHint = Enable::kDefault;
+
+    /**
      * If true, texture fetches from mip-mapped textures will be biased to read larger MIP levels.
      * This has the effect of sharpening those textures, at the cost of some aliasing, and possible
      * performance impact.
diff --git a/src/gpu/GrCaps.cpp b/src/gpu/GrCaps.cpp
index 8cfbc5f..e8e098a 100644
--- a/src/gpu/GrCaps.cpp
+++ b/src/gpu/GrCaps.cpp
@@ -172,6 +172,7 @@
     writer->appendBool("Blacklist Coverage Counting Path Renderer [workaround]",
                        fBlacklistCoverageCounting);
     writer->appendBool("Prefer VRAM Use over flushes [workaround]", fPreferVRAMUseOverFlushes);
+    writer->appendBool("Avoid stencil buffers [workaround]", fAvoidStencilBuffers);
 
     if (this->advancedBlendEquationSupport()) {
         writer->appendHexU32("Advanced Blend Equation Blacklist", fAdvBlendEqBlacklist);
diff --git a/src/gpu/gl/GrGLBuffer.cpp b/src/gpu/gl/GrGLBuffer.cpp
index 180dc39..074dc66 100644
--- a/src/gpu/gl/GrGLBuffer.cpp
+++ b/src/gpu/gl/GrGLBuffer.cpp
@@ -175,7 +175,7 @@
         case GrGLCaps::kMapBuffer_MapBufferType: {
             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
             // Let driver know it can discard the old data
-            if (GR_GL_USE_BUFFER_DATA_NULL_HINT || fGLSizeInBytes != this->sizeInBytes()) {
+            if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->sizeInBytes()) {
                 GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
             }
             GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
@@ -257,28 +257,28 @@
     // bindbuffer handles dirty context
     GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
 
-#if GR_GL_USE_BUFFER_DATA_NULL_HINT
-    if (this->sizeInBytes() == srcSizeInBytes) {
-        GL_CALL(BufferData(target, (GrGLsizeiptr) srcSizeInBytes, src, fUsage));
+    if (this->glCaps().useBufferDataNullHint()) {
+        if (this->sizeInBytes() == srcSizeInBytes) {
+            GL_CALL(BufferData(target, (GrGLsizeiptr) srcSizeInBytes, src, fUsage));
+        } else {
+            // Before we call glBufferSubData we give the driver a hint using
+            // glBufferData with nullptr. 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.
+            // TODO I think we actually want to try calling bufferData here
+            GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
+            GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src));
+        }
+        fGLSizeInBytes = this->sizeInBytes();
     } else {
-        // Before we call glBufferSubData we give the driver a hint using
-        // glBufferData with nullptr. 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.
-        // TODO I think we actually want to try calling bufferData here
-        GL_CALL(BufferData(target, this->sizeInBytes(), nullptr, fUsage));
-        GL_CALL(BufferSubData(target, 0, (GrGLsizeiptr) srcSizeInBytes, src));
+        // 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 (map() does a glBufferData(..size, nullptr..))
+        GL_CALL(BufferData(target, srcSizeInBytes, src, fUsage));
+        fGLSizeInBytes = srcSizeInBytes;
     }
-    fGLSizeInBytes = this->sizeInBytes();
-#else
-    // 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 (map() does a glBufferData(..size, nullptr..))
-    GL_CALL(BufferData(target, srcSizeInBytes, src, fUsage));
-    fGLSizeInBytes = srcSizeInBytes;
-#endif
     VALIDATE();
     return true;
 }
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index 9068525..e70cb9d 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -48,6 +48,7 @@
     fPartialFBOReadIsSlow = false;
     fMipMapLevelAndLodControlSupport = false;
     fRGBAToBGRAReadbackConversionsAreSlow = false;
+    fUseBufferDataNullHint = SkToBool(GR_GL_USE_BUFFER_DATA_NULL_HINT);
     fDoManualMipmapping = false;
     fSRGBDecodeDisableAffectsMipmaps = false;
     fClearToBoundaryValuesIsBroken = false;
@@ -260,6 +261,12 @@
     // vis-versa.
     fRGBAToBGRAReadbackConversionsAreSlow = isMESA || isMAC;
 
+    if (GrContextOptions::Enable::kNo == contextOptions.fUseGLBufferDataNullHint) {
+        fUseBufferDataNullHint = false;
+    } else if (GrContextOptions::Enable::kYes == contextOptions.fUseGLBufferDataNullHint) {
+        fUseBufferDataNullHint = true;
+    }
+
     if (kGL_GrGLStandard == standard) {
         if (version >= GR_GL_VER(4,4) || ctxInfo.hasExtension("GL_ARB_clear_texture")) {
             fClearTextureSupport = true;
@@ -1145,6 +1152,7 @@
     writer->appendBool("Texture swizzle support", fTextureSwizzleSupport);
     writer->appendBool("BGRA to RGBA readback conversions are slow",
                        fRGBAToBGRAReadbackConversionsAreSlow);
+    writer->appendBool("Use buffer data null hint", fUseBufferDataNullHint);
     writer->appendBool("Draw To clear color", fUseDrawToClearColor);
     writer->appendBool("Draw To clear stencil clip", fUseDrawToClearStencilClip);
     writer->appendBool("Intermediate texture for partial updates of unorm textures ever bound to FBOs",
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 770ebca..c4a10b3 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -351,6 +351,8 @@
         return fRGBAToBGRAReadbackConversionsAreSlow;
     }
 
+    bool useBufferDataNullHint() const { return fUseBufferDataNullHint; }
+
     // Certain Intel GPUs on Mac fail to clear if the glClearColor is made up of only 1s and 0s.
     bool clearToBoundaryValuesIsBroken() const { return fClearToBoundaryValuesIsBroken; }
 
@@ -480,6 +482,7 @@
     bool fTextureSwizzleSupport : 1;
     bool fMipMapLevelAndLodControlSupport : 1;
     bool fRGBAToBGRAReadbackConversionsAreSlow : 1;
+    bool fUseBufferDataNullHint                : 1;
     bool fClearTextureSupport : 1;
     bool fProgramBinarySupport : 1;