blob: 9b968dc9e73650c0501fd9786a1e0901bcef2e18 [file] [log] [blame]
/*
* 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<Draw*>(&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<DrawPaths*>(&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<DrawBatch*>(iter.get());
fBatchTarget.flushNext(db->fBatch->numberOfDraws());
if (iter->isTraced()) {
gpu->removeGpuTraceMarker(&newMarker);
}
continue;
}
if (Cmd::kSetState_CmdType == iter->type()) {
SetState* ss = reinterpret_cast<SetState*>(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;
}