Clarify that buffers of type GrGpuBufferType::kXferGpuToCpu are mapped for reading.

Don't allow GrGpuBuffer::updateData on kXferGpuToCpu (in GL this can resize the buffer
to a smaller size).

Don't call glBufferData with NULL prior to mapping for reading.

Don't set GL_MAP_WRITE_BIT when using glMapBufferRange for reading only.

Bug: skia:8962
Change-Id: I605d83fa40c7b170082c48a456436d97cf3b70a5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/206707
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrGpuBuffer.cpp b/src/gpu/GrGpuBuffer.cpp
index b679b47..03f351b 100644
--- a/src/gpu/GrGpuBuffer.cpp
+++ b/src/gpu/GrGpuBuffer.cpp
@@ -17,6 +17,36 @@
         , fAccessPattern(pattern)
         , fIntendedType(type) {}
 
+void* GrGpuBuffer::map() {
+    if (this->wasDestroyed()) {
+        return nullptr;
+    }
+    if (!fMapPtr) {
+        this->onMap();
+    }
+    return fMapPtr;
+}
+
+void GrGpuBuffer::unmap() {
+    if (this->wasDestroyed()) {
+        return;
+    }
+    SkASSERT(fMapPtr);
+    this->onUnmap();
+    fMapPtr = nullptr;
+}
+
+bool GrGpuBuffer::isMapped() const { return SkToBool(fMapPtr); }
+
+bool GrGpuBuffer::updateData(const void* src, size_t srcSizeInBytes) {
+    SkASSERT(!this->isMapped());
+    SkASSERT(srcSizeInBytes <= fSizeInBytes);
+    if (this->intendedType() == GrGpuBufferType::kXferGpuToCpu) {
+        return false;
+    }
+    return this->onUpdateData(src, srcSizeInBytes);
+}
+
 void GrGpuBuffer::ComputeScratchKeyForDynamicVBO(size_t size, GrGpuBufferType intendedType,
                                                  GrScratchKey* key) {
     static const GrScratchKey::ResourceType kType = GrScratchKey::GenerateResourceType();
diff --git a/src/gpu/GrGpuBuffer.h b/src/gpu/GrGpuBuffer.h
index 5bc6ed1..e8c0141 100644
--- a/src/gpu/GrGpuBuffer.h
+++ b/src/gpu/GrGpuBuffer.h
@@ -30,43 +30,35 @@
     void unref() const final { GrGpuResource::unref(); }
 
     /**
-     * Maps the buffer to be written by the CPU.
+     * Maps the buffer to be read or written by the CPU.
      *
-     * The previous content of the buffer is invalidated. It is an error
-     * to draw from the buffer while it is mapped. It may fail if the backend
-     * doesn't support mapping the buffer. If the buffer is CPU backed then
-     * it will always succeed and is a free operation. Once a buffer is mapped,
-     * subsequent calls to map() are ignored.
+     * It is an error to draw from the buffer while it is mapped or transfer to/from the buffer. It
+     * may fail if the backend doesn't support mapping the buffer. Once a buffer is mapped,
+     * subsequent calls to map() trivially succeed. No matter how many times map() is called,
+     * umap() will unmap the buffer on the first call if it is mapped.
      *
-     * Note that buffer mapping does not go through GrContext and therefore is
-     * not serialized with other operations.
+     * If the buffer is of type GrGpuBufferType::kXferGpuToCpu then it is mapped for reading only.
+     * Otherwise it is mapped writing only. Writing to a buffer that is mapped for reading or vice
+     * versa produces undefined results. If the buffer is mapped for writing then then the buffer's
+     * previous contents are invalidated.
      *
      * @return a pointer to the data or nullptr if the map fails.
      */
-    void* map() {
-        if (!fMapPtr) {
-            this->onMap();
-        }
-        return fMapPtr;
-    }
+    void* map();
 
     /**
-     * Unmaps the buffer.
+     * Unmaps the buffer if it is mapped.
      *
      * The pointer returned by the previous map call will no longer be valid.
      */
-    void unmap() {
-        SkASSERT(fMapPtr);
-        this->onUnmap();
-        fMapPtr = nullptr;
-    }
+    void unmap();
 
     /**
-     Queries whether the buffer has been mapped.
-
-     @return true if the buffer is mapped, false otherwise.
+     * Queries whether the buffer has been mapped.
+     *
+     * @return true if the buffer is mapped, false otherwise.
      */
-    bool isMapped() const { return SkToBool(fMapPtr); }
+    bool isMapped() const;
 
     bool isCpuBuffer() const final { return false; }
 
@@ -79,16 +71,14 @@
      *
      * The buffer must not be mapped.
      *
+     * Fails for GrGpuBufferType::kXferGpuToCpu.
+     *
      * Note that buffer updates do not go through GrContext and therefore are
      * not serialized with other operations.
      *
      * @return returns true if the update succeeds, false otherwise.
      */
-    bool updateData(const void* src, size_t srcSizeInBytes) {
-        SkASSERT(!this->isMapped());
-        SkASSERT(srcSizeInBytes <= fSizeInBytes);
-        return this->onUpdateData(src, srcSizeInBytes);
-    }
+    bool updateData(const void* src, size_t srcSizeInBytes);
 
 protected:
     GrGpuBuffer(GrGpu*, size_t sizeInBytes, GrGpuBufferType, GrAccessPattern);
diff --git a/src/gpu/gl/GrGLBuffer.cpp b/src/gpu/gl/GrGLBuffer.cpp
index 4335409..f7eaa6f 100644
--- a/src/gpu/gl/GrGLBuffer.cpp
+++ b/src/gpu/gl/GrGLBuffer.cpp
@@ -159,10 +159,7 @@
 
 void GrGLBuffer::onMap() {
     SkASSERT(fBufferID);
-    if (this->wasDestroyed()) {
-        return;
-    }
-
+    SkASSERT(!this->wasDestroyed());
     VALIDATE();
     SkASSERT(!this->isMapped());
 
@@ -172,12 +169,14 @@
     // Handling dirty context is done in the bindBuffer call
     switch (this->glCaps().mapBufferType()) {
         case GrGLCaps::kNone_MapBufferType:
-            break;
+            return;
         case GrGLCaps::kMapBuffer_MapBufferType: {
             GrGLenum target = this->glGpu()->bindBuffer(fIntendedType, this);
-            // Let driver know it can discard the old data
-            if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) {
-                GL_CALL(BufferData(target, this->size(), nullptr, fUsage));
+            if (!readOnly) {
+                // Let driver know it can discard the old data
+                if (this->glCaps().useBufferDataNullHint() || fGLSizeInBytes != this->size()) {
+                    GL_CALL(BufferData(target, this->size(), nullptr, fUsage));
+                }
             }
             GL_CALL_RET(fMapPtr, MapBuffer(target, readOnly ? GR_GL_READ_ONLY : GR_GL_WRITE_ONLY));
             break;
@@ -188,13 +187,17 @@
             if (fGLSizeInBytes != this->size()) {
                 GL_CALL(BufferData(target, this->size(), nullptr, fUsage));
             }
-            GrGLbitfield writeAccess = GR_GL_MAP_WRITE_BIT;
-            if (GrGpuBufferType::kXferCpuToGpu != fIntendedType) {
-                // TODO: Make this a function parameter.
-                writeAccess |= GR_GL_MAP_INVALIDATE_BUFFER_BIT;
+            GrGLbitfield access;
+            if (readOnly) {
+                access = GR_GL_MAP_READ_BIT;
+            } else {
+                access = GR_GL_MAP_WRITE_BIT;
+                if (GrGpuBufferType::kXferCpuToGpu != fIntendedType) {
+                    // TODO: Make this a function parameter.
+                    access |= GR_GL_MAP_INVALIDATE_BUFFER_BIT;
+                }
             }
-            GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, this->size(),
-                                                readOnly ? GR_GL_MAP_READ_BIT : writeAccess));
+            GL_CALL_RET(fMapPtr, MapBufferRange(target, 0, this->size(), access));
             break;
         }
         case GrGLCaps::kChromium_MapBufferType: {
@@ -214,10 +217,6 @@
 
 void GrGLBuffer::onUnmap() {
     SkASSERT(fBufferID);
-    if (this->wasDestroyed()) {
-        return;
-    }
-
     VALIDATE();
     SkASSERT(this->isMapped());
     if (0 == fBufferID) {
diff --git a/src/gpu/vk/GrVkIndexBuffer.cpp b/src/gpu/vk/GrVkIndexBuffer.cpp
index f5a7bbc..342262d 100644
--- a/src/gpu/vk/GrVkIndexBuffer.cpp
+++ b/src/gpu/vk/GrVkIndexBuffer.cpp
@@ -48,24 +48,12 @@
     INHERITED::onAbandon();
 }
 
-void GrVkIndexBuffer::onMap() {
-    if (!this->wasDestroyed()) {
-        this->GrGpuBuffer::fMapPtr = this->vkMap(this->getVkGpu());
-    }
-}
+void GrVkIndexBuffer::onMap() { this->GrGpuBuffer::fMapPtr = this->vkMap(this->getVkGpu()); }
 
-void GrVkIndexBuffer::onUnmap() {
-    if (!this->wasDestroyed()) {
-        this->vkUnmap(this->getVkGpu());
-    }
-}
+void GrVkIndexBuffer::onUnmap() { this->vkUnmap(this->getVkGpu()); }
 
 bool GrVkIndexBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
-    if (!this->wasDestroyed()) {
-        return this->vkUpdateData(this->getVkGpu(), src, srcSizeInBytes);
-    } else {
-        return false;
-    }
+    return this->vkUpdateData(this->getVkGpu(), src, srcSizeInBytes);
 }
 
 GrVkGpu* GrVkIndexBuffer::getVkGpu() const {
diff --git a/src/gpu/vk/GrVkTransferBuffer.h b/src/gpu/vk/GrVkTransferBuffer.h
index 22f7286..3d46a2f 100644
--- a/src/gpu/vk/GrVkTransferBuffer.h
+++ b/src/gpu/vk/GrVkTransferBuffer.h
@@ -28,17 +28,9 @@
     void setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
                           const SkString& dumpName) const override;
 
-    void onMap() override {
-        if (!this->wasDestroyed()) {
-            this->GrGpuBuffer::fMapPtr = this->vkMap(this->getVkGpu());
-        }
-    }
+    void onMap() override { this->GrGpuBuffer::fMapPtr = this->vkMap(this->getVkGpu()); }
 
-    void onUnmap() override {
-        if (!this->wasDestroyed()) {
-            this->vkUnmap(this->getVkGpu());
-        }
-    }
+    void onUnmap() override { this->vkUnmap(this->getVkGpu()); }
 
     bool onUpdateData(const void* src, size_t srcSizeInBytes) override {
         SK_ABORT("Not implemented for transfer buffers.");