Add a base class for GrIODB that handles geometry data

Review URL: https://codereview.chromium.org/773433002
diff --git a/src/gpu/GrFlushToGpuDrawTarget.cpp b/src/gpu/GrFlushToGpuDrawTarget.cpp
new file mode 100644
index 0000000..b860468
--- /dev/null
+++ b/src/gpu/GrFlushToGpuDrawTarget.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrFlushToGpuDrawTarget.h"
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrTextStrike.h"
+#include "GrBufferAllocPool.h"
+
+GrFlushToGpuDrawTarget::GrFlushToGpuDrawTarget(GrGpu* gpu,
+                                               GrVertexBufferAllocPool* vertexPool,
+                                               GrIndexBufferAllocPool* indexPool)
+    : INHERITED(gpu->getContext())
+    , fGpu(SkRef(gpu))
+    , fVertexPool(vertexPool)
+    , fIndexPool(indexPool)
+    , fFlushing(false) {
+
+    fCaps.reset(SkRef(fGpu->caps()));
+
+    SkASSERT(vertexPool);
+    SkASSERT(indexPool);
+
+    GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
+    poolState.fUsedPoolVertexBytes = 0;
+    poolState.fUsedPoolIndexBytes = 0;
+#ifdef SK_DEBUG
+    poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
+    poolState.fPoolStartVertex = ~0;
+    poolState.fPoolIndexBuffer = (GrIndexBuffer*)~0;
+    poolState.fPoolStartIndex = ~0;
+#endif
+}
+
+GrFlushToGpuDrawTarget::~GrFlushToGpuDrawTarget() {
+    // This must be called by before the GrDrawTarget destructor
+    this->releaseGeometry();
+}
+
+void GrFlushToGpuDrawTarget::setDrawBuffers(DrawInfo* info, size_t vertexStride) {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    if (kBuffer_GeometrySrcType == this->getGeomSrc().fVertexSrc) {
+        info->setVertexBuffer(this->getGeomSrc().fVertexBuffer);
+    } else {
+        // Update the bytes used since the last reserve-geom request.
+        size_t bytes = (info->vertexCount() + info->startVertex()) * vertexStride;
+        poolState.fUsedPoolVertexBytes = SkTMax(poolState.fUsedPoolVertexBytes, bytes);
+        info->setVertexBuffer(poolState.fPoolVertexBuffer);
+        info->adjustStartVertex(poolState.fPoolStartVertex);
+    }
+
+    if (info->isIndexed()) {
+        if (kBuffer_GeometrySrcType == this->getGeomSrc().fIndexSrc) {
+            info->setIndexBuffer(this->getGeomSrc().fIndexBuffer);
+        } else {
+            // Update the bytes used since the last reserve-geom request.
+            size_t bytes = (info->indexCount() + info->startIndex()) * sizeof(uint16_t);
+            poolState.fUsedPoolIndexBytes = SkTMax(poolState.fUsedPoolIndexBytes, bytes);
+            info->setIndexBuffer(poolState.fPoolIndexBuffer);
+            info->adjustStartIndex(poolState.fPoolStartIndex);
+        }
+    }
+}
+
+void GrFlushToGpuDrawTarget::reset() {
+    SkASSERT(1 == fGeoPoolStateStack.count());
+    this->resetVertexSource();
+    this->resetIndexSource();
+
+    fVertexPool->reset();
+    fIndexPool->reset();
+
+    this->onReset();
+}
+
+void GrFlushToGpuDrawTarget::flush() {
+    SkASSERT(kReserved_GeometrySrcType != this->getGeomSrc().fVertexSrc);
+    SkASSERT(kReserved_GeometrySrcType != this->getGeomSrc().fIndexSrc);
+
+    if (fFlushing) {
+        return;
+    }
+    fFlushing = true;
+
+    fGpu->getContext()->getFontCache()->updateTextures();
+    fVertexPool->unmap();
+    fIndexPool->unmap();
+
+    fGpu->saveActiveTraceMarkers();
+
+    this->onFlush();
+
+    fGpu->restoreActiveTraceMarkers();
+
+    fFlushing = false;
+    this->reset();
+}
+
+void GrFlushToGpuDrawTarget::willReserveVertexAndIndexSpace(int vertexCount,
+                                                            size_t vertexStride,
+                                                            int indexCount) {
+    // We use geometryHints() to know whether to flush the draw buffer. We
+    // can't flush if we are inside an unbalanced pushGeometrySource.
+    // Moreover, flushing blows away vertex and index data that was
+    // previously reserved. So if the vertex or index data is pulled from
+    // reserved space and won't be released by this request then we can't
+    // flush.
+    bool insideGeoPush = fGeoPoolStateStack.count() > 1;
+
+    bool unreleasedVertexSpace =
+        !vertexCount &&
+        kReserved_GeometrySrcType == this->getGeomSrc().fVertexSrc;
+
+    bool unreleasedIndexSpace =
+        !indexCount &&
+        kReserved_GeometrySrcType == this->getGeomSrc().fIndexSrc;
+
+    int vcount = vertexCount;
+    int icount = indexCount;
+
+    if (!insideGeoPush &&
+        !unreleasedVertexSpace &&
+        !unreleasedIndexSpace &&
+        this->geometryHints(vertexStride, &vcount, &icount)) {
+        this->flush();
+    }
+}
+
+bool GrFlushToGpuDrawTarget::geometryHints(size_t vertexStride,
+                                           int* vertexCount,
+                                           int* indexCount) const {
+    // we will recommend a flush if the data could fit in a single
+    // preallocated buffer but none are left and it can't fit
+    // in the current buffer (which may not be prealloced).
+    bool flush = false;
+    if (indexCount) {
+        int32_t currIndices = fIndexPool->currentBufferIndices();
+        if (*indexCount > currIndices &&
+            (!fIndexPool->preallocatedBuffersRemaining() &&
+             *indexCount <= fIndexPool->preallocatedBufferIndices())) {
+
+            flush = true;
+        }
+        *indexCount = currIndices;
+    }
+    if (vertexCount) {
+        int32_t currVertices = fVertexPool->currentBufferVertices(vertexStride);
+        if (*vertexCount > currVertices &&
+            (!fVertexPool->preallocatedBuffersRemaining() &&
+             *vertexCount <= fVertexPool->preallocatedBufferVertices(vertexStride))) {
+
+            flush = true;
+        }
+        *vertexCount = currVertices;
+    }
+    return flush;
+}
+
+bool GrFlushToGpuDrawTarget::onReserveVertexSpace(size_t vertexSize,
+                                                  int vertexCount,
+                                                  void** vertices) {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    SkASSERT(vertexCount > 0);
+    SkASSERT(vertices);
+    SkASSERT(0 == poolState.fUsedPoolVertexBytes);
+
+    *vertices = fVertexPool->makeSpace(vertexSize,
+                                       vertexCount,
+                                       &poolState.fPoolVertexBuffer,
+                                       &poolState.fPoolStartVertex);
+    return SkToBool(*vertices);
+}
+
+bool GrFlushToGpuDrawTarget::onReserveIndexSpace(int indexCount, void** indices) {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    SkASSERT(indexCount > 0);
+    SkASSERT(indices);
+    SkASSERT(0 == poolState.fUsedPoolIndexBytes);
+
+    *indices = fIndexPool->makeSpace(indexCount,
+                                     &poolState.fPoolIndexBuffer,
+                                     &poolState.fPoolStartIndex);
+    return SkToBool(*indices);
+}
+
+void GrFlushToGpuDrawTarget::releaseReservedVertexSpace() {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+
+    // If we get a release vertex space call then our current source should either be reserved
+    // or array (which we copied into reserved space).
+    SkASSERT(kReserved_GeometrySrcType == geoSrc.fVertexSrc);
+
+    // When the caller reserved vertex buffer space we gave it back a pointer
+    // provided by the vertex buffer pool. At each draw we tracked the largest
+    // offset into the pool's pointer that was referenced. Now we return to the
+    // pool any portion at the tail of the allocation that no draw referenced.
+    size_t reservedVertexBytes = geoSrc.fVertexSize * geoSrc.fVertexCount;
+    fVertexPool->putBack(reservedVertexBytes - poolState.fUsedPoolVertexBytes);
+    poolState.fUsedPoolVertexBytes = 0;
+    poolState.fPoolVertexBuffer = NULL;
+    poolState.fPoolStartVertex = 0;
+}
+
+void GrFlushToGpuDrawTarget::releaseReservedIndexSpace() {
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
+
+    // If we get a release index space call then our current source should either be reserved
+    // or array (which we copied into reserved space).
+    SkASSERT(kReserved_GeometrySrcType == geoSrc.fIndexSrc);
+
+    // Similar to releaseReservedVertexSpace we return any unused portion at
+    // the tail
+    size_t reservedIndexBytes = sizeof(uint16_t) * geoSrc.fIndexCount;
+    fIndexPool->putBack(reservedIndexBytes - poolState.fUsedPoolIndexBytes);
+    poolState.fUsedPoolIndexBytes = 0;
+    poolState.fPoolIndexBuffer = NULL;
+    poolState.fPoolStartIndex = 0;
+}
+
+void GrFlushToGpuDrawTarget::geometrySourceWillPush() {
+    GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
+    poolState.fUsedPoolVertexBytes = 0;
+    poolState.fUsedPoolIndexBytes = 0;
+#ifdef SK_DEBUG
+    poolState.fPoolVertexBuffer = (GrVertexBuffer*)~0;
+    poolState.fPoolStartVertex = ~0;
+    poolState.fPoolIndexBuffer = (GrIndexBuffer*)~0;
+    poolState.fPoolStartIndex = ~0;
+#endif
+}
+
+void GrFlushToGpuDrawTarget::geometrySourceWillPop(const GeometrySrcState& restoredState) {
+    SkASSERT(fGeoPoolStateStack.count() > 1);
+    fGeoPoolStateStack.pop_back();
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    // we have to assume that any slack we had in our vertex/index data
+    // is now unreleasable because data may have been appended later in the
+    // pool.
+    if (kReserved_GeometrySrcType == restoredState.fVertexSrc) {
+        poolState.fUsedPoolVertexBytes = restoredState.fVertexSize * restoredState.fVertexCount;
+    }
+    if (kReserved_GeometrySrcType == restoredState.fIndexSrc) {
+        poolState.fUsedPoolIndexBytes = sizeof(uint16_t) * restoredState.fIndexCount;
+    }
+}