| /* | |
| * Copyright 2011 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "GrInOrderDrawBuffer.h" | |
| #include "GrBufferAllocPool.h" | |
| #include "GrDrawTargetCaps.h" | |
| #include "GrGpu.h" | |
| #include "GrIndexBuffer.h" | |
| #include "GrPath.h" | |
| #include "GrRenderTarget.h" | |
| #include "GrTemplates.h" | |
| #include "GrTexture.h" | |
| #include "GrVertexBuffer.h" | |
| GrInOrderDrawBuffer::GrInOrderDrawBuffer(GrGpu* gpu, | |
| GrVertexBufferAllocPool* vertexPool, | |
| GrIndexBufferAllocPool* indexPool) | |
| : GrDrawTarget(gpu->getContext()) | |
| , fDstGpu(gpu) | |
| , fClipSet(true) | |
| , fClipProxyState(kUnknown_ClipProxyState) | |
| , fVertexPool(*vertexPool) | |
| , fIndexPool(*indexPool) | |
| , fFlushing(false) | |
| , fDrawID(0) { | |
| fDstGpu->ref(); | |
| fCaps.reset(SkRef(fDstGpu->caps())); | |
| SkASSERT(NULL != vertexPool); | |
| SkASSERT(NULL != 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 | |
| this->reset(); | |
| } | |
| GrInOrderDrawBuffer::~GrInOrderDrawBuffer() { | |
| this->reset(); | |
| // This must be called by before the GrDrawTarget destructor | |
| this->releaseGeometry(); | |
| fDstGpu->unref(); | |
| } | |
| //////////////////////////////////////////////////////////////////////////////// | |
| namespace { | |
| void get_vertex_bounds(const void* vertices, | |
| size_t vertexSize, | |
| int vertexCount, | |
| SkRect* bounds) { | |
| SkASSERT(vertexSize >= sizeof(SkPoint)); | |
| SkASSERT(vertexCount > 0); | |
| const SkPoint* point = static_cast<const SkPoint*>(vertices); | |
| bounds->fLeft = bounds->fRight = point->fX; | |
| bounds->fTop = bounds->fBottom = point->fY; | |
| for (int i = 1; i < vertexCount; ++i) { | |
| point = reinterpret_cast<SkPoint*>(reinterpret_cast<intptr_t>(point) + vertexSize); | |
| bounds->growToInclude(point->fX, point->fY); | |
| } | |
| } | |
| } | |
| namespace { | |
| extern const GrVertexAttrib kRectPosColorUVAttribs[] = { | |
| {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, | |
| {kVec4ub_GrVertexAttribType, sizeof(SkPoint), kColor_GrVertexAttribBinding}, | |
| {kVec2f_GrVertexAttribType, sizeof(SkPoint)+sizeof(GrColor), | |
| kLocalCoord_GrVertexAttribBinding}, | |
| }; | |
| extern const GrVertexAttrib kRectPosUVAttribs[] = { | |
| {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, | |
| {kVec2f_GrVertexAttribType, sizeof(SkPoint), kLocalCoord_GrVertexAttribBinding}, | |
| }; | |
| static void set_vertex_attributes(GrDrawState* drawState, | |
| bool hasColor, bool hasUVs, | |
| int* colorOffset, int* localOffset) { | |
| *colorOffset = -1; | |
| *localOffset = -1; | |
| // Using per-vertex colors allows batching across colors. (A lot of rects in a row differing | |
| // only in color is a common occurrence in tables). However, having per-vertex colors disables | |
| // blending optimizations because we don't know if the color will be solid or not. These | |
| // optimizations help determine whether coverage and color can be blended correctly when | |
| // dual-source blending isn't available. This comes into play when there is coverage. If colors | |
| // were a stage it could take a hint that every vertex's color will be opaque. | |
| if (hasColor && hasUVs) { | |
| *colorOffset = sizeof(SkPoint); | |
| *localOffset = sizeof(SkPoint) + sizeof(GrColor); | |
| drawState->setVertexAttribs<kRectPosColorUVAttribs>(3); | |
| } else if (hasColor) { | |
| *colorOffset = sizeof(SkPoint); | |
| drawState->setVertexAttribs<kRectPosColorUVAttribs>(2); | |
| } else if (hasUVs) { | |
| *localOffset = sizeof(SkPoint); | |
| drawState->setVertexAttribs<kRectPosUVAttribs>(2); | |
| } else { | |
| drawState->setVertexAttribs<kRectPosUVAttribs>(1); | |
| } | |
| } | |
| }; | |
| enum { | |
| kTraceCmdBit = 0x80, | |
| kCmdMask = 0x7f, | |
| }; | |
| static uint8_t add_trace_bit(uint8_t cmd) { | |
| return cmd | kTraceCmdBit; | |
| } | |
| static uint8_t strip_trace_bit(uint8_t cmd) { | |
| return cmd & kCmdMask; | |
| } | |
| static bool cmd_has_trace_marker(uint8_t cmd) { | |
| return SkToBool(cmd & kTraceCmdBit); | |
| } | |
| void GrInOrderDrawBuffer::onDrawRect(const SkRect& rect, | |
| const SkMatrix* matrix, | |
| const SkRect* localRect, | |
| const SkMatrix* localMatrix) { | |
| GrDrawState::AutoColorRestore acr; | |
| GrDrawState* drawState = this->drawState(); | |
| GrColor color = drawState->getColor(); | |
| int colorOffset, localOffset; | |
| set_vertex_attributes(drawState, | |
| this->caps()->dualSourceBlendingSupport() || drawState->hasSolidCoverage(), | |
| NULL != localRect, | |
| &colorOffset, &localOffset); | |
| if (colorOffset >= 0) { | |
| // We set the draw state's color to white here. This is done so that any batching performed | |
| // in our subclass's onDraw() won't get a false from GrDrawState::op== due to a color | |
| // mismatch. TODO: Once vertex layout is owned by GrDrawState it should skip comparing the | |
| // constant color in its op== when the kColor layout bit is set and then we can remove | |
| // this. | |
| acr.set(drawState, 0xFFFFFFFF); | |
| } | |
| AutoReleaseGeometry geo(this, 4, 0); | |
| if (!geo.succeeded()) { | |
| GrPrintf("Failed to get space for vertices!\n"); | |
| return; | |
| } | |
| // Go to device coords to allow batching across matrix changes | |
| SkMatrix combinedMatrix; | |
| if (NULL != matrix) { | |
| combinedMatrix = *matrix; | |
| } else { | |
| combinedMatrix.reset(); | |
| } | |
| combinedMatrix.postConcat(drawState->getViewMatrix()); | |
| // When the caller has provided an explicit source rect for a stage then we don't want to | |
| // modify that stage's matrix. Otherwise if the effect is generating its source rect from | |
| // the vertex positions then we have to account for the view matrix change. | |
| GrDrawState::AutoViewMatrixRestore avmr; | |
| if (!avmr.setIdentity(drawState)) { | |
| return; | |
| } | |
| size_t vsize = drawState->getVertexSize(); | |
| geo.positions()->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize); | |
| combinedMatrix.mapPointsWithStride(geo.positions(), vsize, 4); | |
| SkRect devBounds; | |
| // since we already computed the dev verts, set the bounds hint. This will help us avoid | |
| // unnecessary clipping in our onDraw(). | |
| get_vertex_bounds(geo.vertices(), vsize, 4, &devBounds); | |
| if (localOffset >= 0) { | |
| SkPoint* coords = GrTCast<SkPoint*>(GrTCast<intptr_t>(geo.vertices()) + localOffset); | |
| coords->setRectFan(localRect->fLeft, localRect->fTop, | |
| localRect->fRight, localRect->fBottom, | |
| vsize); | |
| if (NULL != localMatrix) { | |
| localMatrix->mapPointsWithStride(coords, vsize, 4); | |
| } | |
| } | |
| if (colorOffset >= 0) { | |
| GrColor* vertColor = GrTCast<GrColor*>(GrTCast<intptr_t>(geo.vertices()) + colorOffset); | |
| for (int i = 0; i < 4; ++i) { | |
| *vertColor = color; | |
| vertColor = (GrColor*) ((intptr_t) vertColor + vsize); | |
| } | |
| } | |
| this->setIndexSourceToBuffer(this->getContext()->getQuadIndexBuffer()); | |
| this->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &devBounds); | |
| // to ensure that stashing the drawState ptr is valid | |
| SkASSERT(this->drawState() == drawState); | |
| } | |
| bool GrInOrderDrawBuffer::quickInsideClip(const SkRect& devBounds) { | |
| if (!this->getDrawState().isClipState()) { | |
| return true; | |
| } | |
| if (kUnknown_ClipProxyState == fClipProxyState) { | |
| SkIRect rect; | |
| bool iior; | |
| this->getClip()->getConservativeBounds(this->getDrawState().getRenderTarget(), &rect, &iior); | |
| if (iior) { | |
| // The clip is a rect. We will remember that in fProxyClip. It is common for an edge (or | |
| // all edges) of the clip to be at the edge of the RT. However, we get that clipping for | |
| // free via the viewport. We don't want to think that clipping must be enabled in this | |
| // case. So we extend the clip outward from the edge to avoid these false negatives. | |
| fClipProxyState = kValid_ClipProxyState; | |
| fClipProxy = SkRect::Make(rect); | |
| if (fClipProxy.fLeft <= 0) { | |
| fClipProxy.fLeft = SK_ScalarMin; | |
| } | |
| if (fClipProxy.fTop <= 0) { | |
| fClipProxy.fTop = SK_ScalarMin; | |
| } | |
| if (fClipProxy.fRight >= this->getDrawState().getRenderTarget()->width()) { | |
| fClipProxy.fRight = SK_ScalarMax; | |
| } | |
| if (fClipProxy.fBottom >= this->getDrawState().getRenderTarget()->height()) { | |
| fClipProxy.fBottom = SK_ScalarMax; | |
| } | |
| } else { | |
| fClipProxyState = kInvalid_ClipProxyState; | |
| } | |
| } | |
| if (kValid_ClipProxyState == fClipProxyState) { | |
| return fClipProxy.contains(devBounds); | |
| } | |
| SkPoint originOffset = {SkIntToScalar(this->getClip()->fOrigin.fX), | |
| SkIntToScalar(this->getClip()->fOrigin.fY)}; | |
| SkRect clipSpaceBounds = devBounds; | |
| clipSpaceBounds.offset(originOffset); | |
| return this->getClip()->fClipStack->quickContains(clipSpaceBounds); | |
| } | |
| int GrInOrderDrawBuffer::concatInstancedDraw(const DrawInfo& info) { | |
| SkASSERT(info.isInstanced()); | |
| const GeometrySrcState& geomSrc = this->getGeomSrc(); | |
| const GrDrawState& drawState = this->getDrawState(); | |
| // we only attempt to concat the case when reserved verts are used with a client-specified index | |
| // buffer. To make this work with client-specified VBs we'd need to know if the VB was updated | |
| // between draws. | |
| if (kReserved_GeometrySrcType != geomSrc.fVertexSrc || | |
| kBuffer_GeometrySrcType != geomSrc.fIndexSrc) { | |
| return 0; | |
| } | |
| // Check if there is a draw info that is compatible that uses the same VB from the pool and | |
| // the same IB | |
| if (kDraw_Cmd != strip_trace_bit(fCmds.back())) { | |
| return 0; | |
| } | |
| DrawRecord* draw = &fDraws.back(); | |
| GeometryPoolState& poolState = fGeoPoolStateStack.back(); | |
| const GrVertexBuffer* vertexBuffer = poolState.fPoolVertexBuffer; | |
| if (!draw->isInstanced() || | |
| draw->verticesPerInstance() != info.verticesPerInstance() || | |
| draw->indicesPerInstance() != info.indicesPerInstance() || | |
| draw->fVertexBuffer != vertexBuffer || | |
| draw->fIndexBuffer != geomSrc.fIndexBuffer) { | |
| return 0; | |
| } | |
| // info does not yet account for the offset from the start of the pool's VB while the previous | |
| // draw record does. | |
| int adjustedStartVertex = poolState.fPoolStartVertex + info.startVertex(); | |
| if (draw->startVertex() + draw->vertexCount() != adjustedStartVertex) { | |
| return 0; | |
| } | |
| SkASSERT(poolState.fPoolStartVertex == draw->startVertex() + draw->vertexCount()); | |
| // how many instances can be concat'ed onto draw given the size of the index buffer | |
| int instancesToConcat = this->indexCountInCurrentSource() / info.indicesPerInstance(); | |
| instancesToConcat -= draw->instanceCount(); | |
| instancesToConcat = SkTMin(instancesToConcat, info.instanceCount()); | |
| // update the amount of reserved vertex data actually referenced in draws | |
| size_t vertexBytes = instancesToConcat * info.verticesPerInstance() * | |
| drawState.getVertexSize(); | |
| poolState.fUsedPoolVertexBytes = SkTMax(poolState.fUsedPoolVertexBytes, vertexBytes); | |
| draw->adjustInstanceCount(instancesToConcat); | |
| // update last fGpuCmdMarkers to include any additional trace markers that have been added | |
| if (this->getActiveTraceMarkers().count() > 0) { | |
| if (cmd_has_trace_marker(fCmds.back())) { | |
| fGpuCmdMarkers.back().addSet(this->getActiveTraceMarkers()); | |
| } else { | |
| fGpuCmdMarkers.push_back(this->getActiveTraceMarkers()); | |
| fCmds.back() = add_trace_bit(fCmds.back()); | |
| } | |
| } | |
| return instancesToConcat; | |
| } | |
| class AutoClipReenable { | |
| public: | |
| AutoClipReenable() : fDrawState(NULL) {} | |
| ~AutoClipReenable() { | |
| if (NULL != fDrawState) { | |
| fDrawState->enableState(GrDrawState::kClip_StateBit); | |
| } | |
| } | |
| void set(GrDrawState* drawState) { | |
| if (drawState->isClipState()) { | |
| fDrawState = drawState; | |
| drawState->disableState(GrDrawState::kClip_StateBit); | |
| } | |
| } | |
| private: | |
| GrDrawState* fDrawState; | |
| }; | |
| void GrInOrderDrawBuffer::onDraw(const DrawInfo& info) { | |
| GeometryPoolState& poolState = fGeoPoolStateStack.back(); | |
| const GrDrawState& drawState = this->getDrawState(); | |
| AutoClipReenable acr; | |
| if (drawState.isClipState() && | |
| NULL != info.getDevBounds() && | |
| this->quickInsideClip(*info.getDevBounds())) { | |
| acr.set(this->drawState()); | |
| } | |
| if (this->needsNewClip()) { | |
| this->recordClip(); | |
| } | |
| if (this->needsNewState()) { | |
| this->recordState(); | |
| } | |
| DrawRecord* draw; | |
| if (info.isInstanced()) { | |
| int instancesConcated = this->concatInstancedDraw(info); | |
| if (info.instanceCount() > instancesConcated) { | |
| draw = this->recordDraw(info); | |
| draw->adjustInstanceCount(-instancesConcated); | |
| } else { | |
| return; | |
| } | |
| } else { | |
| draw = this->recordDraw(info); | |
| } | |
| switch (this->getGeomSrc().fVertexSrc) { | |
| case kBuffer_GeometrySrcType: | |
| draw->fVertexBuffer = this->getGeomSrc().fVertexBuffer; | |
| break; | |
| case kReserved_GeometrySrcType: // fallthrough | |
| case kArray_GeometrySrcType: { | |
| size_t vertexBytes = (info.vertexCount() + info.startVertex()) * | |
| drawState.getVertexSize(); | |
| poolState.fUsedPoolVertexBytes = SkTMax(poolState.fUsedPoolVertexBytes, vertexBytes); | |
| draw->fVertexBuffer = poolState.fPoolVertexBuffer; | |
| draw->adjustStartVertex(poolState.fPoolStartVertex); | |
| break; | |
| } | |
| default: | |
| SkFAIL("unknown geom src type"); | |
| } | |
| draw->fVertexBuffer->ref(); | |
| if (info.isIndexed()) { | |
| switch (this->getGeomSrc().fIndexSrc) { | |
| case kBuffer_GeometrySrcType: | |
| draw->fIndexBuffer = this->getGeomSrc().fIndexBuffer; | |
| break; | |
| case kReserved_GeometrySrcType: // fallthrough | |
| case kArray_GeometrySrcType: { | |
| size_t indexBytes = (info.indexCount() + info.startIndex()) * sizeof(uint16_t); | |
| poolState.fUsedPoolIndexBytes = SkTMax(poolState.fUsedPoolIndexBytes, indexBytes); | |
| draw->fIndexBuffer = poolState.fPoolIndexBuffer; | |
| draw->adjustStartIndex(poolState.fPoolStartIndex); | |
| break; | |
| } | |
| default: | |
| SkFAIL("unknown geom src type"); | |
| } | |
| draw->fIndexBuffer->ref(); | |
| } else { | |
| draw->fIndexBuffer = NULL; | |
| } | |
| } | |
| GrInOrderDrawBuffer::StencilPath::StencilPath() {} | |
| GrInOrderDrawBuffer::DrawPath::DrawPath() {} | |
| GrInOrderDrawBuffer::DrawPaths::DrawPaths() {} | |
| GrInOrderDrawBuffer::DrawPaths::~DrawPaths() { | |
| if (fTransforms) { | |
| SkDELETE_ARRAY(fTransforms); | |
| } | |
| for (int i = 0; i < fPathCount; ++i) { | |
| fPaths[i]->unref(); | |
| } | |
| SkDELETE_ARRAY(fPaths); | |
| } | |
| void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, SkPath::FillType fill) { | |
| if (this->needsNewClip()) { | |
| this->recordClip(); | |
| } | |
| // Only compare the subset of GrDrawState relevant to path stenciling? | |
| if (this->needsNewState()) { | |
| this->recordState(); | |
| } | |
| StencilPath* sp = this->recordStencilPath(); | |
| sp->fPath.reset(path); | |
| path->ref(); | |
| sp->fFill = fill; | |
| } | |
| void GrInOrderDrawBuffer::onDrawPath(const GrPath* path, | |
| SkPath::FillType fill, const GrDeviceCoordTexture* dstCopy) { | |
| if (this->needsNewClip()) { | |
| this->recordClip(); | |
| } | |
| // TODO: Only compare the subset of GrDrawState relevant to path covering? | |
| if (this->needsNewState()) { | |
| this->recordState(); | |
| } | |
| DrawPath* cp = this->recordDrawPath(); | |
| cp->fPath.reset(path); | |
| path->ref(); | |
| cp->fFill = fill; | |
| if (NULL != dstCopy) { | |
| cp->fDstCopy = *dstCopy; | |
| } | |
| } | |
| void GrInOrderDrawBuffer::onDrawPaths(int pathCount, const GrPath** paths, | |
| const SkMatrix* transforms, | |
| SkPath::FillType fill, | |
| SkStrokeRec::Style stroke, | |
| const GrDeviceCoordTexture* dstCopy) { | |
| SkASSERT(pathCount); | |
| if (this->needsNewClip()) { | |
| this->recordClip(); | |
| } | |
| if (this->needsNewState()) { | |
| this->recordState(); | |
| } | |
| DrawPaths* dp = this->recordDrawPaths(); | |
| dp->fPathCount = pathCount; | |
| dp->fPaths = SkNEW_ARRAY(const GrPath*, pathCount); | |
| memcpy(dp->fPaths, paths, sizeof(GrPath*) * pathCount); | |
| for (int i = 0; i < pathCount; ++i) { | |
| dp->fPaths[i]->ref(); | |
| } | |
| dp->fTransforms = SkNEW_ARRAY(SkMatrix, pathCount); | |
| memcpy(dp->fTransforms, transforms, sizeof(SkMatrix) * pathCount); | |
| dp->fFill = fill; | |
| dp->fStroke = stroke; | |
| if (NULL != dstCopy) { | |
| dp->fDstCopy = *dstCopy; | |
| } | |
| } | |
| void GrInOrderDrawBuffer::clear(const SkIRect* rect, GrColor color, | |
| bool canIgnoreRect, GrRenderTarget* renderTarget) { | |
| SkIRect r; | |
| if (NULL == renderTarget) { | |
| renderTarget = this->drawState()->getRenderTarget(); | |
| SkASSERT(NULL != renderTarget); | |
| } | |
| if (NULL == rect) { | |
| // We could do something smart and remove previous draws and clears to | |
| // the current render target. If we get that smart we have to make sure | |
| // those draws aren't read before this clear (render-to-texture). | |
| r.setLTRB(0, 0, renderTarget->width(), renderTarget->height()); | |
| rect = &r; | |
| } | |
| Clear* clr = this->recordClear(); | |
| GrColorIsPMAssert(color); | |
| clr->fColor = color; | |
| clr->fRect = *rect; | |
| clr->fCanIgnoreRect = canIgnoreRect; | |
| clr->fRenderTarget = renderTarget; | |
| renderTarget->ref(); | |
| } | |
| void GrInOrderDrawBuffer::discard(GrRenderTarget* renderTarget) { | |
| if (!this->caps()->discardRenderTargetSupport()) { | |
| return; | |
| } | |
| if (NULL == renderTarget) { | |
| renderTarget = this->drawState()->getRenderTarget(); | |
| SkASSERT(NULL != renderTarget); | |
| } | |
| Clear* clr = this->recordClear(); | |
| clr->fColor = GrColor_ILLEGAL; | |
| clr->fRenderTarget = renderTarget; | |
| renderTarget->ref(); | |
| } | |
| void GrInOrderDrawBuffer::reset() { | |
| SkASSERT(1 == fGeoPoolStateStack.count()); | |
| this->resetVertexSource(); | |
| this->resetIndexSource(); | |
| int numDraws = fDraws.count(); | |
| for (int d = 0; d < numDraws; ++d) { | |
| // we always have a VB, but not always an IB | |
| SkASSERT(NULL != fDraws[d].fVertexBuffer); | |
| fDraws[d].fVertexBuffer->unref(); | |
| SkSafeUnref(fDraws[d].fIndexBuffer); | |
| } | |
| fCmds.reset(); | |
| fDraws.reset(); | |
| fStencilPaths.reset(); | |
| fDrawPath.reset(); | |
| fDrawPaths.reset(); | |
| fStates.reset(); | |
| fClears.reset(); | |
| fVertexPool.reset(); | |
| fIndexPool.reset(); | |
| fClips.reset(); | |
| fClipOrigins.reset(); | |
| fCopySurfaces.reset(); | |
| fGpuCmdMarkers.reset(); | |
| fClipSet = true; | |
| } | |
| void GrInOrderDrawBuffer::flush() { | |
| if (fFlushing) { | |
| return; | |
| } | |
| SkASSERT(kReserved_GeometrySrcType != this->getGeomSrc().fVertexSrc); | |
| SkASSERT(kReserved_GeometrySrcType != this->getGeomSrc().fIndexSrc); | |
| int numCmds = fCmds.count(); | |
| if (0 == numCmds) { | |
| return; | |
| } | |
| GrAutoTRestore<bool> flushRestore(&fFlushing); | |
| fFlushing = true; | |
| fVertexPool.unmap(); | |
| fIndexPool.unmap(); | |
| GrDrawTarget::AutoClipRestore acr(fDstGpu); | |
| AutoGeometryAndStatePush agasp(fDstGpu, kPreserve_ASRInit); | |
| GrDrawState playbackState; | |
| GrDrawState* prevDrawState = fDstGpu->drawState(); | |
| prevDrawState->ref(); | |
| fDstGpu->setDrawState(&playbackState); | |
| GrClipData clipData; | |
| int currState = 0; | |
| int currClip = 0; | |
| int currClear = 0; | |
| int currDraw = 0; | |
| int currStencilPath = 0; | |
| int currDrawPath = 0; | |
| int currDrawPaths = 0; | |
| int currCopySurface = 0; | |
| int currCmdMarker = 0; | |
| for (int c = 0; c < numCmds; ++c) { | |
| GrGpuTraceMarker newMarker("", -1); | |
| if (cmd_has_trace_marker(fCmds[c])) { | |
| SkString traceString = fGpuCmdMarkers[currCmdMarker].toString(); | |
| newMarker.fMarker = traceString.c_str(); | |
| fDstGpu->addGpuTraceMarker(&newMarker); | |
| ++currCmdMarker; | |
| } | |
| switch (strip_trace_bit(fCmds[c])) { | |
| case kDraw_Cmd: { | |
| const DrawRecord& draw = fDraws[currDraw]; | |
| fDstGpu->setVertexSourceToBuffer(draw.fVertexBuffer); | |
| if (draw.isIndexed()) { | |
| fDstGpu->setIndexSourceToBuffer(draw.fIndexBuffer); | |
| } | |
| fDstGpu->executeDraw(draw); | |
| ++currDraw; | |
| break; | |
| } | |
| case kStencilPath_Cmd: { | |
| const StencilPath& sp = fStencilPaths[currStencilPath]; | |
| fDstGpu->stencilPath(sp.fPath.get(), sp.fFill); | |
| ++currStencilPath; | |
| break; | |
| } | |
| case kDrawPath_Cmd: { | |
| const DrawPath& cp = fDrawPath[currDrawPath]; | |
| fDstGpu->executeDrawPath(cp.fPath.get(), cp.fFill, | |
| NULL != cp.fDstCopy.texture() ? &cp.fDstCopy : NULL); | |
| ++currDrawPath; | |
| break; | |
| } | |
| case kDrawPaths_Cmd: { | |
| DrawPaths& dp = fDrawPaths[currDrawPaths]; | |
| const GrDeviceCoordTexture* dstCopy = | |
| NULL != dp.fDstCopy.texture() ? &dp.fDstCopy : NULL; | |
| fDstGpu->executeDrawPaths(dp.fPathCount, dp.fPaths, | |
| dp.fTransforms, dp.fFill, dp.fStroke, | |
| dstCopy); | |
| ++currDrawPaths; | |
| break; | |
| } | |
| case kSetState_Cmd: | |
| fStates[currState].restoreTo(&playbackState); | |
| ++currState; | |
| break; | |
| case kSetClip_Cmd: | |
| clipData.fClipStack = &fClips[currClip]; | |
| clipData.fOrigin = fClipOrigins[currClip]; | |
| fDstGpu->setClip(&clipData); | |
| ++currClip; | |
| break; | |
| case kClear_Cmd: | |
| if (GrColor_ILLEGAL == fClears[currClear].fColor) { | |
| fDstGpu->discard(fClears[currClear].fRenderTarget); | |
| } else { | |
| fDstGpu->clear(&fClears[currClear].fRect, | |
| fClears[currClear].fColor, | |
| fClears[currClear].fCanIgnoreRect, | |
| fClears[currClear].fRenderTarget); | |
| } | |
| ++currClear; | |
| break; | |
| case kCopySurface_Cmd: | |
| fDstGpu->copySurface(fCopySurfaces[currCopySurface].fDst.get(), | |
| fCopySurfaces[currCopySurface].fSrc.get(), | |
| fCopySurfaces[currCopySurface].fSrcRect, | |
| fCopySurfaces[currCopySurface].fDstPoint); | |
| ++currCopySurface; | |
| break; | |
| } | |
| if (cmd_has_trace_marker(fCmds[c])) { | |
| fDstGpu->removeGpuTraceMarker(&newMarker); | |
| } | |
| } | |
| // we should have consumed all the states, clips, etc. | |
| SkASSERT(fStates.count() == currState); | |
| SkASSERT(fClips.count() == currClip); | |
| SkASSERT(fClipOrigins.count() == currClip); | |
| SkASSERT(fClears.count() == currClear); | |
| SkASSERT(fDraws.count() == currDraw); | |
| SkASSERT(fCopySurfaces.count() == currCopySurface); | |
| SkASSERT(fGpuCmdMarkers.count() == currCmdMarker); | |
| fDstGpu->setDrawState(prevDrawState); | |
| prevDrawState->unref(); | |
| this->reset(); | |
| ++fDrawID; | |
| } | |
| bool GrInOrderDrawBuffer::onCopySurface(GrSurface* dst, | |
| GrSurface* src, | |
| const SkIRect& srcRect, | |
| const SkIPoint& dstPoint) { | |
| if (fDstGpu->canCopySurface(dst, src, srcRect, dstPoint)) { | |
| CopySurface* cs = this->recordCopySurface(); | |
| cs->fDst.reset(SkRef(dst)); | |
| cs->fSrc.reset(SkRef(src)); | |
| cs->fSrcRect = srcRect; | |
| cs->fDstPoint = dstPoint; | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| bool GrInOrderDrawBuffer::onCanCopySurface(GrSurface* dst, | |
| GrSurface* src, | |
| const SkIRect& srcRect, | |
| const SkIPoint& dstPoint) { | |
| return fDstGpu->canCopySurface(dst, src, srcRect, dstPoint); | |
| } | |
| void GrInOrderDrawBuffer::initCopySurfaceDstDesc(const GrSurface* src, GrTextureDesc* desc) { | |
| fDstGpu->initCopySurfaceDstDesc(src, desc); | |
| } | |
| void GrInOrderDrawBuffer::willReserveVertexAndIndexSpace(int vertexCount, | |
| 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; | |
| // we don't want to finalize any reserved geom on the target since | |
| // we don't know that the client has finished writing to it. | |
| bool targetHasReservedGeom = fDstGpu->hasReservedVerticesOrIndices(); | |
| int vcount = vertexCount; | |
| int icount = indexCount; | |
| if (!insideGeoPush && | |
| !unreleasedVertexSpace && | |
| !unreleasedIndexSpace && | |
| !targetHasReservedGeom && | |
| this->geometryHints(&vcount, &icount)) { | |
| this->flush(); | |
| } | |
| } | |
| bool GrInOrderDrawBuffer::geometryHints(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) { | |
| size_t vertexSize = this->getDrawState().getVertexSize(); | |
| int32_t currVertices = fVertexPool.currentBufferVertices(vertexSize); | |
| if (*vertexCount > currVertices && | |
| (!fVertexPool.preallocatedBuffersRemaining() && | |
| *vertexCount <= fVertexPool.preallocatedBufferVertices(vertexSize))) { | |
| flush = true; | |
| } | |
| *vertexCount = currVertices; | |
| } | |
| return flush; | |
| } | |
| bool GrInOrderDrawBuffer::onReserveVertexSpace(size_t vertexSize, | |
| int vertexCount, | |
| void** vertices) { | |
| GeometryPoolState& poolState = fGeoPoolStateStack.back(); | |
| SkASSERT(vertexCount > 0); | |
| SkASSERT(NULL != vertices); | |
| SkASSERT(0 == poolState.fUsedPoolVertexBytes); | |
| *vertices = fVertexPool.makeSpace(vertexSize, | |
| vertexCount, | |
| &poolState.fPoolVertexBuffer, | |
| &poolState.fPoolStartVertex); | |
| return NULL != *vertices; | |
| } | |
| bool GrInOrderDrawBuffer::onReserveIndexSpace(int indexCount, void** indices) { | |
| GeometryPoolState& poolState = fGeoPoolStateStack.back(); | |
| SkASSERT(indexCount > 0); | |
| SkASSERT(NULL != indices); | |
| SkASSERT(0 == poolState.fUsedPoolIndexBytes); | |
| *indices = fIndexPool.makeSpace(indexCount, | |
| &poolState.fPoolIndexBuffer, | |
| &poolState.fPoolStartIndex); | |
| return NULL != *indices; | |
| } | |
| void GrInOrderDrawBuffer::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 || | |
| kArray_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 GrInOrderDrawBuffer::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 || | |
| kArray_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 GrInOrderDrawBuffer::onSetVertexSourceToArray(const void* vertexArray, | |
| int vertexCount) { | |
| GeometryPoolState& poolState = fGeoPoolStateStack.back(); | |
| SkASSERT(0 == poolState.fUsedPoolVertexBytes); | |
| #ifdef SK_DEBUG | |
| bool success = | |
| #endif | |
| fVertexPool.appendVertices(this->getVertexSize(), | |
| vertexCount, | |
| vertexArray, | |
| &poolState.fPoolVertexBuffer, | |
| &poolState.fPoolStartVertex); | |
| GR_DEBUGASSERT(success); | |
| } | |
| void GrInOrderDrawBuffer::onSetIndexSourceToArray(const void* indexArray, | |
| int indexCount) { | |
| GeometryPoolState& poolState = fGeoPoolStateStack.back(); | |
| SkASSERT(0 == poolState.fUsedPoolIndexBytes); | |
| #ifdef SK_DEBUG | |
| bool success = | |
| #endif | |
| fIndexPool.appendIndices(indexCount, | |
| indexArray, | |
| &poolState.fPoolIndexBuffer, | |
| &poolState.fPoolStartIndex); | |
| GR_DEBUGASSERT(success); | |
| } | |
| void GrInOrderDrawBuffer::releaseVertexArray() { | |
| // When the client provides an array as the vertex source we handled it | |
| // by copying their array into reserved space. | |
| this->GrInOrderDrawBuffer::releaseReservedVertexSpace(); | |
| } | |
| void GrInOrderDrawBuffer::releaseIndexArray() { | |
| // When the client provides an array as the index source we handled it | |
| // by copying their array into reserved space. | |
| this->GrInOrderDrawBuffer::releaseReservedIndexSpace(); | |
| } | |
| void GrInOrderDrawBuffer::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 GrInOrderDrawBuffer::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 || | |
| kArray_GeometrySrcType == restoredState.fVertexSrc) { | |
| poolState.fUsedPoolVertexBytes = restoredState.fVertexSize * restoredState.fVertexCount; | |
| } | |
| if (kReserved_GeometrySrcType == restoredState.fIndexSrc || | |
| kArray_GeometrySrcType == restoredState.fIndexSrc) { | |
| poolState.fUsedPoolIndexBytes = sizeof(uint16_t) * | |
| restoredState.fIndexCount; | |
| } | |
| } | |
| bool GrInOrderDrawBuffer::needsNewState() const { | |
| return fStates.empty() || !fStates.back().isEqual(this->getDrawState()); | |
| } | |
| bool GrInOrderDrawBuffer::needsNewClip() const { | |
| SkASSERT(fClips.count() == fClipOrigins.count()); | |
| if (this->getDrawState().isClipState()) { | |
| if (fClipSet && | |
| (fClips.empty() || | |
| fClips.back() != *this->getClip()->fClipStack || | |
| fClipOrigins.back() != this->getClip()->fOrigin)) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| void GrInOrderDrawBuffer::addToCmdBuffer(uint8_t cmd) { | |
| SkASSERT(!cmd_has_trace_marker(cmd)); | |
| const GrTraceMarkerSet& activeTraceMarkers = this->getActiveTraceMarkers(); | |
| if (activeTraceMarkers.count() > 0) { | |
| fCmds.push_back(add_trace_bit(cmd)); | |
| fGpuCmdMarkers.push_back(activeTraceMarkers); | |
| } else { | |
| fCmds.push_back(cmd); | |
| } | |
| } | |
| void GrInOrderDrawBuffer::recordClip() { | |
| fClips.push_back(*this->getClip()->fClipStack); | |
| fClipOrigins.push_back() = this->getClip()->fOrigin; | |
| fClipSet = false; | |
| this->addToCmdBuffer(kSetClip_Cmd); | |
| } | |
| void GrInOrderDrawBuffer::recordState() { | |
| fStates.push_back().saveFrom(this->getDrawState()); | |
| this->addToCmdBuffer(kSetState_Cmd); | |
| } | |
| GrInOrderDrawBuffer::DrawRecord* GrInOrderDrawBuffer::recordDraw(const DrawInfo& info) { | |
| this->addToCmdBuffer(kDraw_Cmd); | |
| return &fDraws.push_back(info); | |
| } | |
| GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() { | |
| this->addToCmdBuffer(kStencilPath_Cmd); | |
| return &fStencilPaths.push_back(); | |
| } | |
| GrInOrderDrawBuffer::DrawPath* GrInOrderDrawBuffer::recordDrawPath() { | |
| this->addToCmdBuffer(kDrawPath_Cmd); | |
| return &fDrawPath.push_back(); | |
| } | |
| GrInOrderDrawBuffer::DrawPaths* GrInOrderDrawBuffer::recordDrawPaths() { | |
| this->addToCmdBuffer(kDrawPaths_Cmd); | |
| return &fDrawPaths.push_back(); | |
| } | |
| GrInOrderDrawBuffer::Clear* GrInOrderDrawBuffer::recordClear() { | |
| this->addToCmdBuffer(kClear_Cmd); | |
| return &fClears.push_back(); | |
| } | |
| GrInOrderDrawBuffer::CopySurface* GrInOrderDrawBuffer::recordCopySurface() { | |
| this->addToCmdBuffer(kCopySurface_Cmd); | |
| return &fCopySurfaces.push_back(); | |
| } | |
| void GrInOrderDrawBuffer::clipWillBeSet(const GrClipData* newClipData) { | |
| INHERITED::clipWillBeSet(newClipData); | |
| fClipSet = true; | |
| fClipProxyState = kUnknown_ClipProxyState; | |
| } |