| /* | |
| * 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 "GrFontCache.h" | |
| #include "GrGpu.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; | |
| } | |
| } | |
| bool GrFlushToGpuDrawTarget::onCanCopySurface(const GrSurface* dst, | |
| const GrSurface* src, | |
| const SkIRect& srcRect, | |
| const SkIPoint& dstPoint) { | |
| return getGpu()->canCopySurface(dst, src, srcRect, dstPoint); | |
| } | |
| bool GrFlushToGpuDrawTarget::onInitCopySurfaceDstDesc(const GrSurface* src, GrSurfaceDesc* desc) { | |
| return getGpu()->initCopySurfaceDstDesc(src, desc); | |
| } |