blob: 80304857d0f23fd2933ec01a7afb04a6de13df44 [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrCCDrawPathsOp.h"
#include "GrGpuCommandBuffer.h"
#include "GrOpFlushState.h"
#include "ccpr/GrCCPerFlushResources.h"
#include "ccpr/GrCoverageCountingPathRenderer.h"
static bool has_coord_transforms(const GrPaint& paint) {
GrFragmentProcessor::Iter iter(paint);
while (const GrFragmentProcessor* fp = iter.next()) {
if (!fp->coordTransforms().empty()) {
return true;
}
}
return false;
}
GrCCDrawPathsOp::GrCCDrawPathsOp(GrCoverageCountingPathRenderer* ccpr, GrPaint&& paint,
const SkIRect& clipIBounds, const SkMatrix& viewMatrix,
const SkPath& path, const SkRect& devBounds)
: GrDrawOp(ClassID())
, fCCPR(ccpr)
, fSRGBFlags(GrPipeline::SRGBFlagsFromPaint(paint))
, fViewMatrixIfUsingLocalCoords(has_coord_transforms(paint) ? viewMatrix : SkMatrix::I())
, fDraws({clipIBounds, viewMatrix, path, paint.getColor(), nullptr})
, fProcessors(std::move(paint)) {
SkDEBUGCODE(fCCPR->incrDrawOpCount_debugOnly());
SkDEBUGCODE(fBaseInstance = -1);
// FIXME: intersect with clip bounds to (hopefully) improve batching.
// (This is nontrivial due to assumptions in generating the octagon cover geometry.)
this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsZeroArea::kNo);
}
GrCCDrawPathsOp::~GrCCDrawPathsOp() {
if (fOwningRTPendingPaths) {
// Remove CCPR's dangling pointer to this Op before deleting it.
fOwningRTPendingPaths->fDrawOps.remove(this);
}
SkDEBUGCODE(fCCPR->decrDrawOpCount_debugOnly());
}
GrDrawOp::RequiresDstTexture GrCCDrawPathsOp::finalize(const GrCaps& caps,
const GrAppliedClip* clip,
GrPixelConfigIsClamped dstIsClamped) {
SkASSERT(!fCCPR->isFlushing_debugOnly());
// There should only be one single path draw in this Op right now.
SkASSERT(1 == fNumDraws);
GrProcessorSet::Analysis analysis =
fProcessors.finalize(fDraws.head().fColor, GrProcessorAnalysisCoverage::kSingleChannel,
clip, false, caps, dstIsClamped, &fDraws.head().fColor);
return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
}
bool GrCCDrawPathsOp::onCombineIfPossible(GrOp* op, const GrCaps& caps) {
GrCCDrawPathsOp* that = op->cast<GrCCDrawPathsOp>();
SkASSERT(fCCPR == that->fCCPR);
SkASSERT(!fCCPR->isFlushing_debugOnly());
SkASSERT(fOwningRTPendingPaths);
SkASSERT(fNumDraws);
SkASSERT(!that->fOwningRTPendingPaths || that->fOwningRTPendingPaths == fOwningRTPendingPaths);
SkASSERT(that->fNumDraws);
if (this->getFillType() != that->getFillType() || fSRGBFlags != that->fSRGBFlags ||
fProcessors != that->fProcessors ||
fViewMatrixIfUsingLocalCoords != that->fViewMatrixIfUsingLocalCoords) {
return false;
}
fDraws.append(std::move(that->fDraws), &fOwningRTPendingPaths->fAllocator);
this->joinBounds(*that);
SkDEBUGCODE(fNumDraws += that->fNumDraws);
SkDEBUGCODE(that->fNumDraws = 0);
return true;
}
void GrCCDrawPathsOp::wasRecorded(GrRenderTargetOpList* opList) {
SkASSERT(!fOwningRTPendingPaths);
fOwningRTPendingPaths = fCCPR->lookupRTPendingPaths(opList);
fOwningRTPendingPaths->fDrawOps.addToTail(this);
}
int GrCCDrawPathsOp::countPaths(GrCCPathParser::PathStats* stats) const {
int numPaths = 0;
for (const GrCCDrawPathsOp::SingleDraw& draw : fDraws) {
stats->statPath(draw.fPath);
++numPaths;
}
return numPaths;
}
void GrCCDrawPathsOp::setupResources(GrCCPerFlushResources* resources,
GrOnFlushResourceProvider* onFlushRP) {
const GrCCAtlas* currentAtlas = nullptr;
SkASSERT(fNumDraws > 0);
SkASSERT(-1 == fBaseInstance);
fBaseInstance = resources->pathInstanceCount();
for (const SingleDraw& draw : fDraws) {
// addPathToAtlas gives us two tight bounding boxes: one in device space, as well as a
// second one rotated an additional 45 degrees. The path vertex shader uses these two
// bounding boxes to generate an octagon that circumscribes the path.
SkRect devBounds, devBounds45;
int16_t atlasOffsetX, atlasOffsetY;
GrCCAtlas* atlas = resources->addPathToAtlas(*onFlushRP->caps(), draw.fClipIBounds,
draw.fMatrix, draw.fPath, &devBounds,
&devBounds45, &atlasOffsetX, &atlasOffsetY);
if (!atlas) {
SkDEBUGCODE(++fNumSkippedInstances);
continue;
}
if (currentAtlas != atlas) {
if (currentAtlas) {
this->addAtlasBatch(currentAtlas, resources->pathInstanceCount());
}
currentAtlas = atlas;
}
resources->appendDrawPathInstance() =
{devBounds, devBounds45, {{atlasOffsetX, atlasOffsetY}}, draw.fColor};
}
SkASSERT(resources->pathInstanceCount() == fBaseInstance + fNumDraws - fNumSkippedInstances);
if (currentAtlas) {
this->addAtlasBatch(currentAtlas, resources->pathInstanceCount());
}
}
void GrCCDrawPathsOp::onExecute(GrOpFlushState* flushState) {
const GrCCPerFlushResources* resources = fCCPR->getPerFlushResources();
if (!resources) {
return; // Setup failed.
}
SkASSERT(fBaseInstance >= 0); // Make sure setupResources has been called.
GrPipeline::InitArgs initArgs;
initArgs.fFlags = fSRGBFlags;
initArgs.fProxy = flushState->drawOpArgs().fProxy;
initArgs.fCaps = &flushState->caps();
initArgs.fResourceProvider = flushState->resourceProvider();
initArgs.fDstProxy = flushState->drawOpArgs().fDstProxy;
GrPipeline pipeline(initArgs, std::move(fProcessors), flushState->detachAppliedClip());
int baseInstance = fBaseInstance;
for (int i = 0; i < fAtlasBatches.count(); baseInstance = fAtlasBatches[i++].fEndInstanceIdx) {
const AtlasBatch& batch = fAtlasBatches[i];
SkASSERT(batch.fEndInstanceIdx > baseInstance);
if (!batch.fAtlas->textureProxy()) {
continue; // Atlas failed to allocate.
}
GrCCPathProcessor pathProc(flushState->resourceProvider(),
sk_ref_sp(batch.fAtlas->textureProxy()), this->getFillType(),
fViewMatrixIfUsingLocalCoords);
pathProc.drawPaths(flushState, pipeline, resources->indexBuffer(),
resources->vertexBuffer(), resources->instanceBuffer(),
baseInstance, batch.fEndInstanceIdx, this->bounds());
}
SkASSERT(baseInstance == fBaseInstance + fNumDraws - fNumSkippedInstances);
}