/* Copyright 2011 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "GrInOrderDrawBuffer.h" #include "GrTexture.h" #include "GrBufferAllocPool.h" #include "GrIndexBuffer.h" #include "GrVertexBuffer.h" #include "GrGpu.h" GrInOrderDrawBuffer::GrInOrderDrawBuffer(GrVertexBufferAllocPool* vertexPool, GrIndexBufferAllocPool* indexPool) : fDraws(DRAWS_BLOCK_SIZE, fDrawsStorage), fStates(STATES_BLOCK_SIZE, fStatesStorage), fClips(CLIPS_BLOCK_SIZE, fClipsStorage), fClipSet(true), fLastRectVertexLayout(0), fQuadIndexBuffer(NULL), fMaxQuads(0), fCurrQuad(0), fVertexPool(*vertexPool), fCurrPoolVertexBuffer(NULL), fCurrPoolStartVertex(0), fIndexPool(*indexPool), fCurrPoolIndexBuffer(NULL), fCurrPoolStartIndex(0), fReservedVertexBytes(0), fReservedIndexBytes(0), fUsedReservedVertexBytes(0), fUsedReservedIndexBytes(0) { GrAssert(NULL != vertexPool); GrAssert(NULL != indexPool); } GrInOrderDrawBuffer::~GrInOrderDrawBuffer() { this->reset(); GrSafeUnref(fQuadIndexBuffer); } void GrInOrderDrawBuffer::initializeDrawStateAndClip(const GrDrawTarget& target) { this->copyDrawState(target); this->setClip(target.getClip()); } void GrInOrderDrawBuffer::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) { bool newIdxBuffer = fQuadIndexBuffer != indexBuffer; if (newIdxBuffer) { GrSafeUnref(fQuadIndexBuffer); fQuadIndexBuffer = indexBuffer; GrSafeRef(fQuadIndexBuffer); fCurrQuad = 0; fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads(); } else { GrAssert((NULL == indexBuffer && 0 == fMaxQuads) || (indexBuffer->maxQuads() == fMaxQuads)); } } void GrInOrderDrawBuffer::drawRect(const GrRect& rect, const GrMatrix* matrix, StageBitfield stageEnableBitfield, const GrRect* srcRects[], const GrMatrix* srcMatrices[]) { GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad)); GrAssert(!(fDraws.empty() && fCurrQuad)); GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer)); // if we have a quad IB then either append to the previous run of // rects or start a new run if (fMaxQuads) { bool appendToPreviousDraw = false; GrVertexLayout layout = GetRectVertexLayout(stageEnableBitfield, srcRects); AutoReleaseGeometry geo(this, layout, 4, 0); AutoViewMatrixRestore avmr(this); GrMatrix combinedMatrix = this->getViewMatrix(); this->setViewMatrix(GrMatrix::I()); if (NULL != matrix) { combinedMatrix.preConcat(*matrix); } SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices, layout, geo.vertices()); // we don't want to miss an opportunity to batch rects together // simply because the clip has changed if the clip doesn't affect // the rect. bool disabledClip = false; if (this->isClipState() && fClip.isRect()) { GrRect clipRect = fClip.getRect(0); // If the clip rect touches the edge of the viewport, extended it // out (close) to infinity to avoid bogus intersections. // We might consider a more exact clip to viewport if this // conservative test fails. const GrRenderTarget* target = this->getRenderTarget(); if (0 >= clipRect.fLeft) { clipRect.fLeft = GR_ScalarMin; } if (target->width() <= clipRect.fRight) { clipRect.fRight = GR_ScalarMax; } if (0 >= clipRect.top()) { clipRect.fTop = GR_ScalarMin; } if (target->height() <= clipRect.fBottom) { clipRect.fBottom = GR_ScalarMax; } int stride = VertexSize(layout); bool insideClip = true; for (int v = 0; v < 4; ++v) { const GrPoint& p = *GetVertexPoint(geo.vertices(), v, stride); if (!clipRect.contains(p)) { insideClip = false; break; } } if (insideClip) { this->disableState(kClip_StateBit); disabledClip = true; } } if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 && fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) { int vsize = VertexSize(layout); Draw& lastDraw = fDraws.back(); GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer); GrAssert(kTriangles_PrimitiveType == lastDraw.fPrimitiveType); GrAssert(0 == lastDraw.fVertexCount % 4); GrAssert(0 == lastDraw.fIndexCount % 6); GrAssert(0 == lastDraw.fStartIndex); appendToPreviousDraw = lastDraw.fVertexBuffer == fCurrPoolVertexBuffer && (fCurrQuad * 4 + lastDraw.fStartVertex) == fCurrPoolStartVertex; if (appendToPreviousDraw) { lastDraw.fVertexCount += 4; lastDraw.fIndexCount += 6; fCurrQuad += 1; GrAssert(0 == fUsedReservedVertexBytes); fUsedReservedVertexBytes = 4 * vsize; } } if (!appendToPreviousDraw) { this->setIndexSourceToBuffer(fQuadIndexBuffer); drawIndexed(kTriangles_PrimitiveType, 0, 0, 4, 6); fCurrQuad = 1; fLastRectVertexLayout = layout; } if (disabledClip) { this->enableState(kClip_StateBit); } } else { INHERITED::drawRect(rect, matrix, stageEnableBitfield, srcRects, srcMatrices); } } void GrInOrderDrawBuffer::drawIndexed(GrPrimitiveType primitiveType, int startVertex, int startIndex, int vertexCount, int indexCount) { if (!vertexCount || !indexCount) { return; } fCurrQuad = 0; Draw& draw = fDraws.push_back(); draw.fPrimitiveType = primitiveType; draw.fStartVertex = startVertex; draw.fStartIndex = startIndex; draw.fVertexCount = vertexCount; draw.fIndexCount = indexCount; draw.fClipChanged = this->needsNewClip(); if (draw.fClipChanged) { this->pushClip(); } draw.fStateChanged = this->needsNewState(); if (draw.fStateChanged) { this->pushState(); } draw.fVertexLayout = fGeometrySrc.fVertexLayout; switch (fGeometrySrc.fVertexSrc) { case kBuffer_GeometrySrcType: draw.fVertexBuffer = fGeometrySrc.fVertexBuffer; break; case kReserved_GeometrySrcType: { size_t vertexBytes = (vertexCount + startVertex) * VertexSize(fGeometrySrc.fVertexLayout); fUsedReservedVertexBytes = GrMax(fUsedReservedVertexBytes, vertexBytes); } // fallthrough case kArray_GeometrySrcType: draw.fVertexBuffer = fCurrPoolVertexBuffer; draw.fStartVertex += fCurrPoolStartVertex; break; default: GrCrash("unknown geom src type"); } draw.fVertexBuffer->ref(); switch (fGeometrySrc.fIndexSrc) { case kBuffer_GeometrySrcType: draw.fIndexBuffer = fGeometrySrc.fIndexBuffer; break; case kReserved_GeometrySrcType: { size_t indexBytes = (indexCount + startIndex) * sizeof(uint16_t); fUsedReservedIndexBytes = GrMax(fUsedReservedIndexBytes, indexBytes); } // fallthrough case kArray_GeometrySrcType: draw.fIndexBuffer = fCurrPoolIndexBuffer; draw.fStartIndex += fCurrPoolStartVertex; break; default: GrCrash("unknown geom src type"); } draw.fIndexBuffer->ref(); } void GrInOrderDrawBuffer::drawNonIndexed(GrPrimitiveType primitiveType, int startVertex, int vertexCount) { if (!vertexCount) { return; } fCurrQuad = 0; Draw& draw = fDraws.push_back(); draw.fPrimitiveType = primitiveType; draw.fStartVertex = startVertex; draw.fStartIndex = 0; draw.fVertexCount = vertexCount; draw.fIndexCount = 0; draw.fClipChanged = this->needsNewClip(); if (draw.fClipChanged) { this->pushClip(); } draw.fStateChanged = this->needsNewState(); if (draw.fStateChanged) { this->pushState(); } draw.fVertexLayout = fGeometrySrc.fVertexLayout; switch (fGeometrySrc.fVertexSrc) { case kBuffer_GeometrySrcType: draw.fVertexBuffer = fGeometrySrc.fVertexBuffer; break; case kReserved_GeometrySrcType: { size_t vertexBytes = (vertexCount + startVertex) * VertexSize(fGeometrySrc.fVertexLayout); fUsedReservedVertexBytes = GrMax(fUsedReservedVertexBytes, vertexBytes); } // fallthrough case kArray_GeometrySrcType: draw.fVertexBuffer = fCurrPoolVertexBuffer; draw.fStartVertex += fCurrPoolStartVertex; break; default: GrCrash("unknown geom src type"); } draw.fVertexBuffer->ref(); draw.fIndexBuffer = NULL; } void GrInOrderDrawBuffer::reset() { GrAssert(!fReservedGeometry.fLocked); uint32_t numStates = fStates.count(); for (uint32_t i = 0; i < numStates; ++i) { const DrState& dstate = this->accessSavedDrawState(fStates[i]); for (int s = 0; s < kNumStages; ++s) { GrSafeUnref(dstate.fTextures[s]); } GrSafeUnref(dstate.fRenderTarget); } int numDraws = fDraws.count(); for (int d = 0; d < numDraws; ++d) { // we always have a VB, but not always an IB GrAssert(NULL != fDraws[d].fVertexBuffer); fDraws[d].fVertexBuffer->unref(); GrSafeUnref(fDraws[d].fIndexBuffer); } fDraws.reset(); fStates.reset(); fVertexPool.reset(); fIndexPool.reset(); fClips.reset(); fCurrQuad = 0; } void GrInOrderDrawBuffer::playback(GrDrawTarget* target) { GrAssert(NULL != target); GrAssert(target != this); // not considered and why? uint32_t numDraws = fDraws.count(); if (!numDraws) { return; } fVertexPool.unlock(); fIndexPool.unlock(); GrDrawTarget::AutoStateRestore asr(target); GrDrawTarget::AutoClipRestore acr(target); // important to not mess with reserve/lock geometry in the target with this // on the stack. GrDrawTarget::AutoGeometrySrcRestore agsr(target); uint32_t currState = ~0; uint32_t currClip = ~0; for (uint32_t i = 0; i < numDraws; ++i) { const Draw& draw = fDraws[i]; if (draw.fStateChanged) { ++currState; target->restoreDrawState(fStates[currState]); } if (draw.fClipChanged) { ++currClip; target->setClip(fClips[currClip]); } uint32_t vertexReserveCount = 0; uint32_t indexReserveCount = 0; target->setVertexSourceToBuffer(draw.fVertexLayout, draw.fVertexBuffer); if (draw.fIndexCount) { target->setIndexSourceToBuffer(draw.fIndexBuffer); } if (draw.fIndexCount) { target->drawIndexed(draw.fPrimitiveType, draw.fStartVertex, draw.fStartIndex, draw.fVertexCount, draw.fIndexCount); } else { target->drawNonIndexed(draw.fPrimitiveType, draw.fStartVertex, draw.fVertexCount); } if (vertexReserveCount || indexReserveCount) { target->releaseReservedGeometry(); } } } bool GrInOrderDrawBuffer::geometryHints(GrVertexLayout vertexLayout, 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 (NULL != indexCount) { int32_t currIndices = fIndexPool.currentBufferIndices(); if (*indexCount > currIndices && (!fIndexPool.preallocatedBuffersRemaining() && *indexCount <= fIndexPool.preallocatedBufferIndices())) { flush = true; } *indexCount = currIndices; } if (NULL != vertexCount) { int32_t currVertices = fVertexPool.currentBufferVertices(vertexLayout); if (*vertexCount > currVertices && (!fVertexPool.preallocatedBuffersRemaining() && *vertexCount <= fVertexPool.preallocatedBufferVertices(vertexLayout))) { flush = true; } *vertexCount = currVertices; } return flush; } bool GrInOrderDrawBuffer::acquireGeometryHelper(GrVertexLayout vertexLayout, void** vertices, void** indices) { GrAssert(!fReservedGeometry.fLocked); if (fReservedGeometry.fVertexCount) { GrAssert(NULL != vertices); GrAssert(0 == fReservedVertexBytes); GrAssert(0 == fUsedReservedVertexBytes); fReservedVertexBytes = VertexSize(vertexLayout) * fReservedGeometry.fVertexCount; *vertices = fVertexPool.makeSpace(vertexLayout, fReservedGeometry.fVertexCount, &fCurrPoolVertexBuffer, &fCurrPoolStartVertex); if (NULL == *vertices) { return false; } } if (fReservedGeometry.fIndexCount) { GrAssert(NULL != indices); GrAssert(0 == fReservedIndexBytes); GrAssert(0 == fUsedReservedIndexBytes); *indices = fIndexPool.makeSpace(fReservedGeometry.fIndexCount, &fCurrPoolIndexBuffer, &fCurrPoolStartIndex); if (NULL == *indices) { fVertexPool.putBack(fReservedVertexBytes); fReservedVertexBytes = 0; fCurrPoolVertexBuffer = NULL; return false; } } return true; } void GrInOrderDrawBuffer::releaseGeometryHelper() { GrAssert(fUsedReservedVertexBytes <= fReservedVertexBytes); GrAssert(fUsedReservedIndexBytes <= fReservedIndexBytes); size_t vertexSlack = fReservedVertexBytes - fUsedReservedVertexBytes; fVertexPool.putBack(vertexSlack); size_t indexSlack = fReservedIndexBytes - fUsedReservedIndexBytes; fIndexPool.putBack(indexSlack); fReservedVertexBytes = 0; fReservedIndexBytes = 0; fUsedReservedVertexBytes = 0; fUsedReservedIndexBytes = 0; fCurrPoolVertexBuffer = 0; fCurrPoolStartVertex = 0; } void GrInOrderDrawBuffer::setVertexSourceToArrayHelper(const void* vertexArray, int vertexCount) { GrAssert(!fReservedGeometry.fLocked || !fReservedGeometry.fVertexCount); #if GR_DEBUG bool success = #endif fVertexPool.appendVertices(fGeometrySrc.fVertexLayout, vertexCount, vertexArray, &fCurrPoolVertexBuffer, &fCurrPoolStartVertex); GR_DEBUGASSERT(success); } void GrInOrderDrawBuffer::setIndexSourceToArrayHelper(const void* indexArray, int indexCount) { GrAssert(!fReservedGeometry.fLocked || !fReservedGeometry.fIndexCount); #if GR_DEBUG bool success = #endif fIndexPool.appendIndices(indexCount, indexArray, &fCurrPoolIndexBuffer, &fCurrPoolStartIndex); GR_DEBUGASSERT(success); } bool GrInOrderDrawBuffer::needsNewState() const { if (fStates.empty()) { return true; } else { const DrState& old = this->accessSavedDrawState(fStates.back()); return old != fCurrDrawState; } } void GrInOrderDrawBuffer::pushState() { for (int s = 0; s < kNumStages; ++s) { GrSafeRef(fCurrDrawState.fTextures[s]); } GrSafeRef(fCurrDrawState.fRenderTarget); this->saveCurrentDrawState(&fStates.push_back()); } bool GrInOrderDrawBuffer::needsNewClip() const { if (fCurrDrawState.fFlagBits & kClip_StateBit) { if (fClips.empty() || (fClipSet && fClips.back() != fClip)) { return true; } } return false; } void GrInOrderDrawBuffer::pushClip() { fClips.push_back() = fClip; fClipSet = false; } void GrInOrderDrawBuffer::clipWillBeSet(const GrClip& newClip) { fClipSet = true; }