/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrTargetCommands.h" #include "GrColor.h" #include "GrDefaultGeoProcFactory.h" #include "GrInOrderDrawBuffer.h" #include "GrTemplates.h" #include "SkPoint.h" void GrTargetCommands::closeBatch() { if (fDrawBatch) { fBatchTarget.resetNumberOfDraws(); fDrawBatch->execute(NULL, fPrevState); fDrawBatch->fBatch->setNumberOfDraws(fBatchTarget.numberOfDraws()); fDrawBatch = NULL; } } static bool path_fill_type_is_winding(const GrStencilSettings& pathStencilSettings) { static const GrStencilSettings::Face pathFace = GrStencilSettings::kFront_Face; bool isWinding = kInvert_StencilOp != pathStencilSettings.passOp(pathFace); if (isWinding) { // Double check that it is in fact winding. SkASSERT(kIncClamp_StencilOp == pathStencilSettings.passOp(pathFace)); SkASSERT(kIncClamp_StencilOp == pathStencilSettings.failOp(pathFace)); SkASSERT(0x1 != pathStencilSettings.writeMask(pathFace)); SkASSERT(!pathStencilSettings.isTwoSided()); } return isWinding; } int GrTargetCommands::concatInstancedDraw(GrInOrderDrawBuffer* iodb, const GrDrawTarget::DrawInfo& info) { SkASSERT(!fCmdBuffer.empty()); SkASSERT(info.isInstanced()); const GrIndexBuffer* ib; if (!iodb->canConcatToIndexBuffer(&ib)) { 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 (Cmd::kDraw_CmdType != fCmdBuffer.back().type()) { return 0; } Draw* draw = static_cast(&fCmdBuffer.back()); if (!draw->fInfo.isInstanced() || draw->fInfo.primitiveType() != info.primitiveType() || draw->fInfo.verticesPerInstance() != info.verticesPerInstance() || draw->fInfo.indicesPerInstance() != info.indicesPerInstance() || draw->fInfo.vertexBuffer() != info.vertexBuffer() || draw->fInfo.indexBuffer() != ib) { return 0; } if (draw->fInfo.startVertex() + draw->fInfo.vertexCount() != info.startVertex()) { return 0; } // how many instances can be concat'ed onto draw given the size of the index buffer int instancesToConcat = iodb->indexCountInCurrentSource() / info.indicesPerInstance(); instancesToConcat -= draw->fInfo.instanceCount(); instancesToConcat = SkTMin(instancesToConcat, info.instanceCount()); draw->fInfo.adjustInstanceCount(instancesToConcat); // update last fGpuCmdMarkers to include any additional trace markers that have been added iodb->recordTraceMarkersIfNecessary(draw); return instancesToConcat; } GrTargetCommands::Cmd* GrTargetCommands::recordDraw( GrInOrderDrawBuffer* iodb, const GrGeometryProcessor* gp, const GrDrawTarget::DrawInfo& info, const GrDrawTarget::PipelineInfo& pipelineInfo) { SkASSERT(info.vertexBuffer() && (!info.isIndexed() || info.indexBuffer())); this->closeBatch(); if (!this->setupPipelineAndShouldDraw(iodb, gp, pipelineInfo)) { return NULL; } Draw* draw; if (info.isInstanced()) { int instancesConcated = this->concatInstancedDraw(iodb, info); if (info.instanceCount() > instancesConcated) { draw = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Draw, (info)); draw->fInfo.adjustInstanceCount(-instancesConcated); } else { return NULL; } } else { draw = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Draw, (info)); } return draw; } GrTargetCommands::Cmd* GrTargetCommands::recordDrawBatch( GrInOrderDrawBuffer* iodb, GrBatch* batch, const GrDrawTarget::PipelineInfo& pipelineInfo) { if (!this->setupPipelineAndShouldDraw(iodb, batch, pipelineInfo)) { return NULL; } // Check if there is a Batch Draw we can batch with if (Cmd::kDrawBatch_CmdType != fCmdBuffer.back().type() || !fDrawBatch) { fDrawBatch = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawBatch, (batch, &fBatchTarget)); return fDrawBatch; } SkASSERT(&fCmdBuffer.back() == fDrawBatch); if (!fDrawBatch->fBatch->combineIfPossible(batch)) { this->closeBatch(); fDrawBatch = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawBatch, (batch, &fBatchTarget)); } return fDrawBatch; } GrTargetCommands::Cmd* GrTargetCommands::recordStencilPath( GrInOrderDrawBuffer* iodb, const GrPipelineBuilder& pipelineBuilder, const GrPathProcessor* pathProc, const GrPath* path, const GrScissorState& scissorState, const GrStencilSettings& stencilSettings) { this->closeBatch(); StencilPath* sp = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, StencilPath, (path, pipelineBuilder.getRenderTarget())); sp->fScissor = scissorState; sp->fUseHWAA = pipelineBuilder.isHWAntialias(); sp->fViewMatrix = pathProc->viewMatrix(); sp->fStencil = stencilSettings; return sp; } GrTargetCommands::Cmd* GrTargetCommands::recordDrawPath( GrInOrderDrawBuffer* iodb, const GrPathProcessor* pathProc, const GrPath* path, const GrStencilSettings& stencilSettings, const GrDrawTarget::PipelineInfo& pipelineInfo) { this->closeBatch(); // TODO: Only compare the subset of GrPipelineBuilder relevant to path covering? if (!this->setupPipelineAndShouldDraw(iodb, pathProc, pipelineInfo)) { return NULL; } DrawPath* dp = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawPath, (path)); dp->fStencilSettings = stencilSettings; return dp; } GrTargetCommands::Cmd* GrTargetCommands::recordDrawPaths( GrInOrderDrawBuffer* iodb, const GrPathProcessor* pathProc, const GrPathRange* pathRange, const void* indexValues, GrDrawTarget::PathIndexType indexType, const float transformValues[], GrDrawTarget::PathTransformType transformType, int count, const GrStencilSettings& stencilSettings, const GrDrawTarget::PipelineInfo& pipelineInfo) { SkASSERT(pathRange); SkASSERT(indexValues); SkASSERT(transformValues); this->closeBatch(); if (!this->setupPipelineAndShouldDraw(iodb, pathProc, pipelineInfo)) { return NULL; } char* savedIndices; float* savedTransforms; iodb->appendIndicesAndTransforms(indexValues, indexType, transformValues, transformType, count, &savedIndices, &savedTransforms); if (Cmd::kDrawPaths_CmdType == fCmdBuffer.back().type()) { // The previous command was also DrawPaths. Try to collapse this call into the one // before. Note that stenciling all the paths at once, then covering, may not be // equivalent to two separate draw calls if there is overlap. Blending won't work, // and the combined calls may also cancel each other's winding numbers in some // places. For now the winding numbers are only an issue if the fill is even/odd, // because DrawPaths is currently only used for glyphs, and glyphs in the same // font tend to all wind in the same direction. DrawPaths* previous = static_cast(&fCmdBuffer.back()); if (pathRange == previous->pathRange() && indexType == previous->fIndexType && transformType == previous->fTransformType && stencilSettings == previous->fStencilSettings && path_fill_type_is_winding(stencilSettings) && !pipelineInfo.willBlendWithDst(pathProc)) { const int indexBytes = GrPathRange::PathIndexSizeInBytes(indexType); const int xformSize = GrPathRendering::PathTransformSize(transformType); if (&previous->fIndices[previous->fCount*indexBytes] == savedIndices && (0 == xformSize || &previous->fTransforms[previous->fCount*xformSize] == savedTransforms)) { // Fold this DrawPaths call into the one previous. previous->fCount += count; return NULL; } } } DrawPaths* dp = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, DrawPaths, (pathRange)); dp->fIndices = savedIndices; dp->fIndexType = indexType; dp->fTransforms = savedTransforms; dp->fTransformType = transformType; dp->fCount = count; dp->fStencilSettings = stencilSettings; return dp; } GrTargetCommands::Cmd* GrTargetCommands::recordClear(GrInOrderDrawBuffer* iodb, const SkIRect* rect, GrColor color, bool canIgnoreRect, GrRenderTarget* renderTarget) { SkASSERT(renderTarget); this->closeBatch(); SkIRect r; 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 = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Clear, (renderTarget)); GrColorIsPMAssert(color); clr->fColor = color; clr->fRect = *rect; clr->fCanIgnoreRect = canIgnoreRect; return clr; } GrTargetCommands::Cmd* GrTargetCommands::recordClearStencilClip(GrInOrderDrawBuffer* iodb, const SkIRect& rect, bool insideClip, GrRenderTarget* renderTarget) { SkASSERT(renderTarget); this->closeBatch(); ClearStencilClip* clr = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, ClearStencilClip, (renderTarget)); clr->fRect = rect; clr->fInsideClip = insideClip; return clr; } GrTargetCommands::Cmd* GrTargetCommands::recordDiscard(GrInOrderDrawBuffer* iodb, GrRenderTarget* renderTarget) { SkASSERT(renderTarget); this->closeBatch(); Clear* clr = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, Clear, (renderTarget)); clr->fColor = GrColor_ILLEGAL; return clr; } void GrTargetCommands::reset() { fCmdBuffer.reset(); fPrevState = NULL; fDrawBatch = NULL; } void GrTargetCommands::flush(GrInOrderDrawBuffer* iodb) { if (fCmdBuffer.empty()) { return; } // TODO this is temporary while batch is being rolled out this->closeBatch(); iodb->getVertexAllocPool()->unmap(); iodb->getIndexAllocPool()->unmap(); fBatchTarget.preFlush(); // Updated every time we find a set state cmd to reflect the current state in the playback // stream. SetState* currentState = NULL; CmdBuffer::Iter iter(fCmdBuffer); GrGpu* gpu = iodb->getGpu(); while (iter.next()) { GrGpuTraceMarker newMarker("", -1); SkString traceString; if (iter->isTraced()) { traceString = iodb->getCmdString(iter->markerID()); newMarker.fMarker = traceString.c_str(); gpu->addGpuTraceMarker(&newMarker); } // TODO temporary hack if (Cmd::kDrawBatch_CmdType == iter->type()) { DrawBatch* db = reinterpret_cast(iter.get()); fBatchTarget.flushNext(db->fBatch->numberOfDraws()); if (iter->isTraced()) { gpu->removeGpuTraceMarker(&newMarker); } continue; } if (Cmd::kSetState_CmdType == iter->type()) { SetState* ss = reinterpret_cast(iter.get()); ss->execute(gpu, currentState); currentState = ss; } else { iter->execute(gpu, currentState); } if (iter->isTraced()) { gpu->removeGpuTraceMarker(&newMarker); } } // TODO see copious notes about hack fBatchTarget.postFlush(); } void GrTargetCommands::Draw::execute(GrGpu* gpu, const SetState* state) { SkASSERT(state); DrawArgs args(state->fPrimitiveProcessor.get(), state->getPipeline(), &state->fDesc, &state->fBatchTracker); gpu->draw(args, fInfo); } void GrTargetCommands::StencilPath::execute(GrGpu* gpu, const SetState*) { GrGpu::StencilPathState state; state.fRenderTarget = fRenderTarget.get(); state.fScissor = &fScissor; state.fStencil = &fStencil; state.fUseHWAA = fUseHWAA; state.fViewMatrix = &fViewMatrix; gpu->stencilPath(this->path(), state); } void GrTargetCommands::DrawPath::execute(GrGpu* gpu, const SetState* state) { SkASSERT(state); DrawArgs args(state->fPrimitiveProcessor.get(), state->getPipeline(), &state->fDesc, &state->fBatchTracker); gpu->drawPath(args, this->path(), fStencilSettings); } void GrTargetCommands::DrawPaths::execute(GrGpu* gpu, const SetState* state) { SkASSERT(state); DrawArgs args(state->fPrimitiveProcessor.get(), state->getPipeline(), &state->fDesc, &state->fBatchTracker); gpu->drawPaths(args, this->pathRange(), fIndices, fIndexType, fTransforms, fTransformType, fCount, fStencilSettings); } void GrTargetCommands::DrawBatch::execute(GrGpu*, const SetState* state) { SkASSERT(state); fBatch->generateGeometry(fBatchTarget, state->getPipeline()); } void GrTargetCommands::SetState::execute(GrGpu* gpu, const SetState*) { // TODO sometimes we have a prim proc, othertimes we have a GrBatch. Eventually we // will only have GrBatch and we can delete this if (fPrimitiveProcessor) { gpu->buildProgramDesc(&fDesc, *fPrimitiveProcessor, *getPipeline(), fBatchTracker); } } void GrTargetCommands::Clear::execute(GrGpu* gpu, const SetState*) { if (GrColor_ILLEGAL == fColor) { gpu->discard(this->renderTarget()); } else { gpu->clear(&fRect, fColor, fCanIgnoreRect, this->renderTarget()); } } void GrTargetCommands::ClearStencilClip::execute(GrGpu* gpu, const SetState*) { gpu->clearStencilClip(fRect, fInsideClip, this->renderTarget()); } void GrTargetCommands::CopySurface::execute(GrGpu* gpu, const SetState*) { gpu->copySurface(this->dst(), this->src(), fSrcRect, fDstPoint); } GrTargetCommands::Cmd* GrTargetCommands::recordCopySurface(GrInOrderDrawBuffer* iodb, GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { if (iodb->getGpu()->canCopySurface(dst, src, srcRect, dstPoint)) { this->closeBatch(); CopySurface* cs = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, CopySurface, (dst, src)); cs->fSrcRect = srcRect; cs->fDstPoint = dstPoint; return cs; } return NULL; } bool GrTargetCommands::setupPipelineAndShouldDraw(GrInOrderDrawBuffer* iodb, const GrPrimitiveProcessor* primProc, const GrDrawTarget::PipelineInfo& pipelineInfo) { SetState* ss = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, SetState, (primProc)); iodb->setupPipeline(pipelineInfo, ss->pipelineLocation()); if (ss->getPipeline()->mustSkip()) { fCmdBuffer.pop_back(); return false; } ss->fPrimitiveProcessor->initBatchTracker(&ss->fBatchTracker, ss->getPipeline()->getInitBatchTracker()); if (fPrevState && fPrevState->fPrimitiveProcessor.get() && fPrevState->fPrimitiveProcessor->canMakeEqual(fPrevState->fBatchTracker, *ss->fPrimitiveProcessor, ss->fBatchTracker) && fPrevState->getPipeline()->isEqual(*ss->getPipeline())) { fCmdBuffer.pop_back(); } else { fPrevState = ss; iodb->recordTraceMarkersIfNecessary(ss); } return true; } bool GrTargetCommands::setupPipelineAndShouldDraw(GrInOrderDrawBuffer* iodb, GrBatch* batch, const GrDrawTarget::PipelineInfo& pipelineInfo) { SetState* ss = GrNEW_APPEND_TO_RECORDER(fCmdBuffer, SetState, ()); iodb->setupPipeline(pipelineInfo, ss->pipelineLocation()); if (ss->getPipeline()->mustSkip()) { fCmdBuffer.pop_back(); return false; } batch->initBatchTracker(ss->getPipeline()->getInitBatchTracker()); if (fPrevState && !fPrevState->fPrimitiveProcessor.get() && fPrevState->getPipeline()->isEqual(*ss->getPipeline())) { fCmdBuffer.pop_back(); } else { this->closeBatch(); fPrevState = ss; iodb->recordTraceMarkersIfNecessary(ss); } return true; }