michael@0: michael@0: /* michael@0: * Copyright 2010 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: michael@0: #include "GrGpu.h" michael@0: michael@0: #include "GrBufferAllocPool.h" michael@0: #include "GrContext.h" michael@0: #include "GrDrawTargetCaps.h" michael@0: #include "GrIndexBuffer.h" michael@0: #include "GrStencilBuffer.h" michael@0: #include "GrVertexBuffer.h" michael@0: michael@0: // probably makes no sense for this to be less than a page michael@0: static const size_t VERTEX_POOL_VB_SIZE = 1 << 18; michael@0: static const int VERTEX_POOL_VB_COUNT = 4; michael@0: static const size_t INDEX_POOL_IB_SIZE = 1 << 16; michael@0: static const int INDEX_POOL_IB_COUNT = 4; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #define DEBUG_INVAL_BUFFER 0xdeadcafe michael@0: #define DEBUG_INVAL_START_IDX -1 michael@0: michael@0: GrGpu::GrGpu(GrContext* context) michael@0: : GrDrawTarget(context) michael@0: , fResetTimestamp(kExpiredTimestamp+1) michael@0: , fResetBits(kAll_GrBackendState) michael@0: , fVertexPool(NULL) michael@0: , fIndexPool(NULL) michael@0: , fVertexPoolUseCnt(0) michael@0: , fIndexPoolUseCnt(0) michael@0: , fQuadIndexBuffer(NULL) { michael@0: michael@0: fClipMaskManager.setGpu(this); michael@0: michael@0: fGeomPoolStateStack.push_back(); michael@0: #ifdef SK_DEBUG michael@0: GeometryPoolState& poolState = fGeomPoolStateStack.back(); michael@0: poolState.fPoolVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER; michael@0: poolState.fPoolStartVertex = DEBUG_INVAL_START_IDX; michael@0: poolState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER; michael@0: poolState.fPoolStartIndex = DEBUG_INVAL_START_IDX; michael@0: #endif michael@0: } michael@0: michael@0: GrGpu::~GrGpu() { michael@0: this->releaseResources(); michael@0: } michael@0: michael@0: void GrGpu::abandonResources() { michael@0: michael@0: fClipMaskManager.releaseResources(); michael@0: michael@0: while (NULL != fResourceList.head()) { michael@0: fResourceList.head()->abandon(); michael@0: } michael@0: michael@0: SkASSERT(NULL == fQuadIndexBuffer || !fQuadIndexBuffer->isValid()); michael@0: SkSafeSetNull(fQuadIndexBuffer); michael@0: delete fVertexPool; michael@0: fVertexPool = NULL; michael@0: delete fIndexPool; michael@0: fIndexPool = NULL; michael@0: } michael@0: michael@0: void GrGpu::releaseResources() { michael@0: michael@0: fClipMaskManager.releaseResources(); michael@0: michael@0: while (NULL != fResourceList.head()) { michael@0: fResourceList.head()->release(); michael@0: } michael@0: michael@0: SkASSERT(NULL == fQuadIndexBuffer || !fQuadIndexBuffer->isValid()); michael@0: SkSafeSetNull(fQuadIndexBuffer); michael@0: delete fVertexPool; michael@0: fVertexPool = NULL; michael@0: delete fIndexPool; michael@0: fIndexPool = NULL; michael@0: } michael@0: michael@0: void GrGpu::insertResource(GrResource* resource) { michael@0: SkASSERT(NULL != resource); michael@0: SkASSERT(this == resource->getGpu()); michael@0: michael@0: fResourceList.addToHead(resource); michael@0: } michael@0: michael@0: void GrGpu::removeResource(GrResource* resource) { michael@0: SkASSERT(NULL != resource); michael@0: SkASSERT(this == resource->getGpu()); michael@0: michael@0: fResourceList.remove(resource); michael@0: } michael@0: michael@0: michael@0: void GrGpu::unimpl(const char msg[]) { michael@0: #ifdef SK_DEBUG michael@0: GrPrintf("--- GrGpu unimplemented(\"%s\")\n", msg); michael@0: #endif michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: GrTexture* GrGpu::createTexture(const GrTextureDesc& desc, michael@0: const void* srcData, size_t rowBytes) { michael@0: if (kUnknown_GrPixelConfig == desc.fConfig) { michael@0: return NULL; michael@0: } michael@0: if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) && michael@0: !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) { michael@0: return NULL; michael@0: } michael@0: michael@0: this->handleDirtyContext(); michael@0: GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes); michael@0: if (NULL != tex && michael@0: (kRenderTarget_GrTextureFlagBit & desc.fFlags) && michael@0: !(kNoStencil_GrTextureFlagBit & desc.fFlags)) { michael@0: SkASSERT(NULL != tex->asRenderTarget()); michael@0: // TODO: defer this and attach dynamically michael@0: if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) { michael@0: tex->unref(); michael@0: return NULL; michael@0: } michael@0: } michael@0: return tex; michael@0: } michael@0: michael@0: bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) { michael@0: SkASSERT(NULL == rt->getStencilBuffer()); michael@0: GrStencilBuffer* sb = michael@0: this->getContext()->findStencilBuffer(rt->width(), michael@0: rt->height(), michael@0: rt->numSamples()); michael@0: if (NULL != sb) { michael@0: rt->setStencilBuffer(sb); michael@0: bool attached = this->attachStencilBufferToRenderTarget(sb, rt); michael@0: if (!attached) { michael@0: rt->setStencilBuffer(NULL); michael@0: } michael@0: return attached; michael@0: } michael@0: if (this->createStencilBufferForRenderTarget(rt, michael@0: rt->width(), rt->height())) { michael@0: // Right now we're clearing the stencil buffer here after it is michael@0: // attached to an RT for the first time. When we start matching michael@0: // stencil buffers with smaller color targets this will no longer michael@0: // be correct because it won't be guaranteed to clear the entire michael@0: // sb. michael@0: // We used to clear down in the GL subclass using a special purpose michael@0: // FBO. But iOS doesn't allow a stencil-only FBO. It reports unsupported michael@0: // FBO status. michael@0: GrDrawState::AutoRenderTargetRestore artr(this->drawState(), rt); michael@0: this->clearStencil(); michael@0: return true; michael@0: } else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc) { michael@0: this->handleDirtyContext(); michael@0: GrTexture* tex = this->onWrapBackendTexture(desc); michael@0: if (NULL == tex) { michael@0: return NULL; michael@0: } michael@0: // TODO: defer this and attach dynamically michael@0: GrRenderTarget* tgt = tex->asRenderTarget(); michael@0: if (NULL != tgt && michael@0: !this->attachStencilBufferToRenderTarget(tgt)) { michael@0: tex->unref(); michael@0: return NULL; michael@0: } else { michael@0: return tex; michael@0: } michael@0: } michael@0: michael@0: GrRenderTarget* GrGpu::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) { michael@0: this->handleDirtyContext(); michael@0: return this->onWrapBackendRenderTarget(desc); michael@0: } michael@0: michael@0: GrVertexBuffer* GrGpu::createVertexBuffer(size_t size, bool dynamic) { michael@0: this->handleDirtyContext(); michael@0: return this->onCreateVertexBuffer(size, dynamic); michael@0: } michael@0: michael@0: GrIndexBuffer* GrGpu::createIndexBuffer(size_t size, bool dynamic) { michael@0: this->handleDirtyContext(); michael@0: return this->onCreateIndexBuffer(size, dynamic); michael@0: } michael@0: michael@0: GrPath* GrGpu::createPath(const SkPath& path, const SkStrokeRec& stroke) { michael@0: SkASSERT(this->caps()->pathRenderingSupport()); michael@0: this->handleDirtyContext(); michael@0: return this->onCreatePath(path, stroke); michael@0: } michael@0: michael@0: void GrGpu::clear(const SkIRect* rect, michael@0: GrColor color, michael@0: bool canIgnoreRect, michael@0: GrRenderTarget* renderTarget) { michael@0: GrDrawState::AutoRenderTargetRestore art; michael@0: if (NULL != renderTarget) { michael@0: art.set(this->drawState(), renderTarget); michael@0: } michael@0: if (NULL == this->getDrawState().getRenderTarget()) { michael@0: SkASSERT(0); michael@0: return; michael@0: } michael@0: this->handleDirtyContext(); michael@0: this->onClear(rect, color, canIgnoreRect); michael@0: } michael@0: michael@0: void GrGpu::forceRenderTargetFlush() { michael@0: this->handleDirtyContext(); michael@0: this->onForceRenderTargetFlush(); michael@0: } michael@0: michael@0: bool GrGpu::readPixels(GrRenderTarget* target, michael@0: int left, int top, int width, int height, michael@0: GrPixelConfig config, void* buffer, michael@0: size_t rowBytes) { michael@0: this->handleDirtyContext(); michael@0: return this->onReadPixels(target, left, top, width, height, michael@0: config, buffer, rowBytes); michael@0: } michael@0: michael@0: bool GrGpu::writeTexturePixels(GrTexture* texture, michael@0: int left, int top, int width, int height, michael@0: GrPixelConfig config, const void* buffer, michael@0: size_t rowBytes) { michael@0: this->handleDirtyContext(); michael@0: return this->onWriteTexturePixels(texture, left, top, width, height, michael@0: config, buffer, rowBytes); michael@0: } michael@0: michael@0: void GrGpu::resolveRenderTarget(GrRenderTarget* target) { michael@0: SkASSERT(target); michael@0: this->handleDirtyContext(); michael@0: this->onResolveRenderTarget(target); michael@0: } michael@0: michael@0: static const GrStencilSettings& winding_path_stencil_settings() { michael@0: GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, michael@0: kIncClamp_StencilOp, michael@0: kIncClamp_StencilOp, michael@0: kAlwaysIfInClip_StencilFunc, michael@0: 0xFFFF, 0xFFFF, 0xFFFF); michael@0: return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); michael@0: } michael@0: michael@0: static const GrStencilSettings& even_odd_path_stencil_settings() { michael@0: GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, michael@0: kInvert_StencilOp, michael@0: kInvert_StencilOp, michael@0: kAlwaysIfInClip_StencilFunc, michael@0: 0xFFFF, 0xFFFF, 0xFFFF); michael@0: return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); michael@0: } michael@0: michael@0: void GrGpu::getPathStencilSettingsForFillType(SkPath::FillType fill, GrStencilSettings* outStencilSettings) { michael@0: michael@0: switch (fill) { michael@0: default: michael@0: GrCrash("Unexpected path fill."); michael@0: /* fallthrough */; michael@0: case SkPath::kWinding_FillType: michael@0: case SkPath::kInverseWinding_FillType: michael@0: *outStencilSettings = winding_path_stencil_settings(); michael@0: break; michael@0: case SkPath::kEvenOdd_FillType: michael@0: case SkPath::kInverseEvenOdd_FillType: michael@0: *outStencilSettings = even_odd_path_stencil_settings(); michael@0: break; michael@0: } michael@0: fClipMaskManager.adjustPathStencilParams(outStencilSettings); michael@0: } michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static const int MAX_QUADS = 1 << 12; // max possible: (1 << 14) - 1; michael@0: michael@0: GR_STATIC_ASSERT(4 * MAX_QUADS <= 65535); michael@0: michael@0: static inline void fill_indices(uint16_t* indices, int quadCount) { michael@0: for (int i = 0; i < quadCount; ++i) { michael@0: indices[6 * i + 0] = 4 * i + 0; michael@0: indices[6 * i + 1] = 4 * i + 1; michael@0: indices[6 * i + 2] = 4 * i + 2; michael@0: indices[6 * i + 3] = 4 * i + 0; michael@0: indices[6 * i + 4] = 4 * i + 2; michael@0: indices[6 * i + 5] = 4 * i + 3; michael@0: } michael@0: } michael@0: michael@0: const GrIndexBuffer* GrGpu::getQuadIndexBuffer() const { michael@0: if (NULL == fQuadIndexBuffer) { michael@0: static const int SIZE = sizeof(uint16_t) * 6 * MAX_QUADS; michael@0: GrGpu* me = const_cast(this); michael@0: fQuadIndexBuffer = me->createIndexBuffer(SIZE, false); michael@0: if (NULL != fQuadIndexBuffer) { michael@0: uint16_t* indices = (uint16_t*)fQuadIndexBuffer->lock(); michael@0: if (NULL != indices) { michael@0: fill_indices(indices, MAX_QUADS); michael@0: fQuadIndexBuffer->unlock(); michael@0: } else { michael@0: indices = (uint16_t*)sk_malloc_throw(SIZE); michael@0: fill_indices(indices, MAX_QUADS); michael@0: if (!fQuadIndexBuffer->updateData(indices, SIZE)) { michael@0: fQuadIndexBuffer->unref(); michael@0: fQuadIndexBuffer = NULL; michael@0: GrCrash("Can't get indices into buffer!"); michael@0: } michael@0: sk_free(indices); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return fQuadIndexBuffer; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool GrGpu::setupClipAndFlushState(DrawType type, const GrDeviceCoordTexture* dstCopy, michael@0: GrDrawState::AutoRestoreEffects* are, michael@0: const SkRect* devBounds) { michael@0: if (!fClipMaskManager.setupClipping(this->getClip(), are, devBounds)) { michael@0: return false; michael@0: } michael@0: michael@0: if (!this->flushGraphicsState(type, dstCopy)) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void GrGpu::geometrySourceWillPush() { michael@0: const GeometrySrcState& geoSrc = this->getGeomSrc(); michael@0: if (kArray_GeometrySrcType == geoSrc.fVertexSrc || michael@0: kReserved_GeometrySrcType == geoSrc.fVertexSrc) { michael@0: this->finalizeReservedVertices(); michael@0: } michael@0: if (kArray_GeometrySrcType == geoSrc.fIndexSrc || michael@0: kReserved_GeometrySrcType == geoSrc.fIndexSrc) { michael@0: this->finalizeReservedIndices(); michael@0: } michael@0: GeometryPoolState& newState = fGeomPoolStateStack.push_back(); michael@0: #ifdef SK_DEBUG michael@0: newState.fPoolVertexBuffer = (GrVertexBuffer*)DEBUG_INVAL_BUFFER; michael@0: newState.fPoolStartVertex = DEBUG_INVAL_START_IDX; michael@0: newState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER; michael@0: newState.fPoolStartIndex = DEBUG_INVAL_START_IDX; michael@0: #else michael@0: (void) newState; // silence compiler warning michael@0: #endif michael@0: } michael@0: michael@0: void GrGpu::geometrySourceWillPop(const GeometrySrcState& restoredState) { michael@0: // if popping last entry then pops are unbalanced with pushes michael@0: SkASSERT(fGeomPoolStateStack.count() > 1); michael@0: fGeomPoolStateStack.pop_back(); michael@0: } michael@0: michael@0: void GrGpu::onDraw(const DrawInfo& info) { michael@0: this->handleDirtyContext(); michael@0: GrDrawState::AutoRestoreEffects are; michael@0: if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()), michael@0: info.getDstCopy(), &are, info.getDevBounds())) { michael@0: return; michael@0: } michael@0: this->onGpuDraw(info); michael@0: } michael@0: michael@0: void GrGpu::onStencilPath(const GrPath* path, SkPath::FillType fill) { michael@0: this->handleDirtyContext(); michael@0: michael@0: GrDrawState::AutoRestoreEffects are; michael@0: if (!this->setupClipAndFlushState(kStencilPath_DrawType, NULL, &are, NULL)) { michael@0: return; michael@0: } michael@0: michael@0: this->onGpuStencilPath(path, fill); michael@0: } michael@0: michael@0: michael@0: void GrGpu::onDrawPath(const GrPath* path, SkPath::FillType fill, michael@0: const GrDeviceCoordTexture* dstCopy) { michael@0: this->handleDirtyContext(); michael@0: michael@0: drawState()->setDefaultVertexAttribs(); michael@0: michael@0: GrDrawState::AutoRestoreEffects are; michael@0: if (!this->setupClipAndFlushState(kDrawPath_DrawType, dstCopy, &are, NULL)) { michael@0: return; michael@0: } michael@0: michael@0: this->onGpuDrawPath(path, fill); michael@0: } michael@0: michael@0: void GrGpu::finalizeReservedVertices() { michael@0: SkASSERT(NULL != fVertexPool); michael@0: fVertexPool->unlock(); michael@0: } michael@0: michael@0: void GrGpu::finalizeReservedIndices() { michael@0: SkASSERT(NULL != fIndexPool); michael@0: fIndexPool->unlock(); michael@0: } michael@0: michael@0: void GrGpu::prepareVertexPool() { michael@0: if (NULL == fVertexPool) { michael@0: SkASSERT(0 == fVertexPoolUseCnt); michael@0: fVertexPool = SkNEW_ARGS(GrVertexBufferAllocPool, (this, true, michael@0: VERTEX_POOL_VB_SIZE, michael@0: VERTEX_POOL_VB_COUNT)); michael@0: fVertexPool->releaseGpuRef(); michael@0: } else if (!fVertexPoolUseCnt) { michael@0: // the client doesn't have valid data in the pool michael@0: fVertexPool->reset(); michael@0: } michael@0: } michael@0: michael@0: void GrGpu::prepareIndexPool() { michael@0: if (NULL == fIndexPool) { michael@0: SkASSERT(0 == fIndexPoolUseCnt); michael@0: fIndexPool = SkNEW_ARGS(GrIndexBufferAllocPool, (this, true, michael@0: INDEX_POOL_IB_SIZE, michael@0: INDEX_POOL_IB_COUNT)); michael@0: fIndexPool->releaseGpuRef(); michael@0: } else if (!fIndexPoolUseCnt) { michael@0: // the client doesn't have valid data in the pool michael@0: fIndexPool->reset(); michael@0: } michael@0: } michael@0: michael@0: bool GrGpu::onReserveVertexSpace(size_t vertexSize, michael@0: int vertexCount, michael@0: void** vertices) { michael@0: GeometryPoolState& geomPoolState = fGeomPoolStateStack.back(); michael@0: michael@0: SkASSERT(vertexCount > 0); michael@0: SkASSERT(NULL != vertices); michael@0: michael@0: this->prepareVertexPool(); michael@0: michael@0: *vertices = fVertexPool->makeSpace(vertexSize, michael@0: vertexCount, michael@0: &geomPoolState.fPoolVertexBuffer, michael@0: &geomPoolState.fPoolStartVertex); michael@0: if (NULL == *vertices) { michael@0: return false; michael@0: } michael@0: ++fVertexPoolUseCnt; michael@0: return true; michael@0: } michael@0: michael@0: bool GrGpu::onReserveIndexSpace(int indexCount, void** indices) { michael@0: GeometryPoolState& geomPoolState = fGeomPoolStateStack.back(); michael@0: michael@0: SkASSERT(indexCount > 0); michael@0: SkASSERT(NULL != indices); michael@0: michael@0: this->prepareIndexPool(); michael@0: michael@0: *indices = fIndexPool->makeSpace(indexCount, michael@0: &geomPoolState.fPoolIndexBuffer, michael@0: &geomPoolState.fPoolStartIndex); michael@0: if (NULL == *indices) { michael@0: return false; michael@0: } michael@0: ++fIndexPoolUseCnt; michael@0: return true; michael@0: } michael@0: michael@0: void GrGpu::releaseReservedVertexSpace() { michael@0: const GeometrySrcState& geoSrc = this->getGeomSrc(); michael@0: SkASSERT(kReserved_GeometrySrcType == geoSrc.fVertexSrc); michael@0: size_t bytes = geoSrc.fVertexCount * geoSrc.fVertexSize; michael@0: fVertexPool->putBack(bytes); michael@0: --fVertexPoolUseCnt; michael@0: } michael@0: michael@0: void GrGpu::releaseReservedIndexSpace() { michael@0: const GeometrySrcState& geoSrc = this->getGeomSrc(); michael@0: SkASSERT(kReserved_GeometrySrcType == geoSrc.fIndexSrc); michael@0: size_t bytes = geoSrc.fIndexCount * sizeof(uint16_t); michael@0: fIndexPool->putBack(bytes); michael@0: --fIndexPoolUseCnt; michael@0: } michael@0: michael@0: void GrGpu::onSetVertexSourceToArray(const void* vertexArray, int vertexCount) { michael@0: this->prepareVertexPool(); michael@0: GeometryPoolState& geomPoolState = fGeomPoolStateStack.back(); michael@0: #ifdef SK_DEBUG michael@0: bool success = michael@0: #endif michael@0: fVertexPool->appendVertices(this->getVertexSize(), michael@0: vertexCount, michael@0: vertexArray, michael@0: &geomPoolState.fPoolVertexBuffer, michael@0: &geomPoolState.fPoolStartVertex); michael@0: ++fVertexPoolUseCnt; michael@0: GR_DEBUGASSERT(success); michael@0: } michael@0: michael@0: void GrGpu::onSetIndexSourceToArray(const void* indexArray, int indexCount) { michael@0: this->prepareIndexPool(); michael@0: GeometryPoolState& geomPoolState = fGeomPoolStateStack.back(); michael@0: #ifdef SK_DEBUG michael@0: bool success = michael@0: #endif michael@0: fIndexPool->appendIndices(indexCount, michael@0: indexArray, michael@0: &geomPoolState.fPoolIndexBuffer, michael@0: &geomPoolState.fPoolStartIndex); michael@0: ++fIndexPoolUseCnt; michael@0: GR_DEBUGASSERT(success); michael@0: } michael@0: michael@0: void GrGpu::releaseVertexArray() { michael@0: // if vertex source was array, we stowed data in the pool michael@0: const GeometrySrcState& geoSrc = this->getGeomSrc(); michael@0: SkASSERT(kArray_GeometrySrcType == geoSrc.fVertexSrc); michael@0: size_t bytes = geoSrc.fVertexCount * geoSrc.fVertexSize; michael@0: fVertexPool->putBack(bytes); michael@0: --fVertexPoolUseCnt; michael@0: } michael@0: michael@0: void GrGpu::releaseIndexArray() { michael@0: // if index source was array, we stowed data in the pool michael@0: const GeometrySrcState& geoSrc = this->getGeomSrc(); michael@0: SkASSERT(kArray_GeometrySrcType == geoSrc.fIndexSrc); michael@0: size_t bytes = geoSrc.fIndexCount * sizeof(uint16_t); michael@0: fIndexPool->putBack(bytes); michael@0: --fIndexPoolUseCnt; michael@0: }