Home | History | Annotate | Download | only in ccpr
      1 /*
      2  * Copyright 2017 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "GrCoverageCountingPathRenderer.h"
      9 
     10 #include "GrCaps.h"
     11 #include "GrClip.h"
     12 #include "GrGpu.h"
     13 #include "GrGpuCommandBuffer.h"
     14 #include "SkMakeUnique.h"
     15 #include "SkMatrix.h"
     16 #include "GrOpFlushState.h"
     17 #include "GrRenderTargetOpList.h"
     18 #include "GrStyle.h"
     19 #include "ccpr/GrCCPRPathProcessor.h"
     20 
     21 using DrawPathsOp = GrCoverageCountingPathRenderer::DrawPathsOp;
     22 using ScissorMode = GrCCPRCoverageOpsBuilder::ScissorMode;
     23 
     24 bool GrCoverageCountingPathRenderer::IsSupported(const GrCaps& caps) {
     25     const GrShaderCaps& shaderCaps = *caps.shaderCaps();
     26     return shaderCaps.geometryShaderSupport() &&
     27            shaderCaps.texelBufferSupport() &&
     28            shaderCaps.integerSupport() &&
     29            shaderCaps.flatInterpolationSupport() &&
     30            shaderCaps.maxVertexSamplers() >= 1 &&
     31            caps.instanceAttribSupport() &&
     32            caps.isConfigTexturable(kAlpha_half_GrPixelConfig) &&
     33            caps.isConfigRenderable(kAlpha_half_GrPixelConfig, /*withMSAA=*/false);
     34 }
     35 
     36 sk_sp<GrCoverageCountingPathRenderer>
     37 GrCoverageCountingPathRenderer::CreateIfSupported(const GrCaps& caps) {
     38     return sk_sp<GrCoverageCountingPathRenderer>(IsSupported(caps) ?
     39                                                  new GrCoverageCountingPathRenderer : nullptr);
     40 }
     41 
     42 bool GrCoverageCountingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
     43     if (!args.fShape->style().isSimpleFill() ||
     44         args.fShape->inverseFilled() ||
     45         args.fViewMatrix->hasPerspective() ||
     46         GrAAType::kCoverage != args.fAAType) {
     47         return false;
     48     }
     49 
     50     SkPath path;
     51     args.fShape->asPath(&path);
     52     return !SkPathPriv::ConicWeightCnt(path);
     53 }
     54 
     55 bool GrCoverageCountingPathRenderer::onDrawPath(const DrawPathArgs& args) {
     56     SkASSERT(!fFlushing);
     57     SkASSERT(!args.fShape->isEmpty());
     58 
     59     auto op = skstd::make_unique<DrawPathsOp>(this, args, args.fPaint.getColor());
     60     args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
     61 
     62     return true;
     63 }
     64 
     65 GrCoverageCountingPathRenderer::DrawPathsOp::DrawPathsOp(GrCoverageCountingPathRenderer* ccpr,
     66                                                          const DrawPathArgs& args, GrColor color)
     67         : INHERITED(ClassID())
     68         , fCCPR(ccpr)
     69         , fSRGBFlags(GrPipeline::SRGBFlagsFromPaint(args.fPaint))
     70         , fProcessors(std::move(args.fPaint))
     71         , fTailDraw(&fHeadDraw)
     72         , fOwningRTPendingOps(nullptr) {
     73     SkDEBUGCODE(fBaseInstance = -1);
     74     SkDEBUGCODE(fDebugInstanceCount = 1;)
     75 
     76     GrRenderTargetContext* const rtc = args.fRenderTargetContext;
     77 
     78     SkRect devBounds;
     79     args.fViewMatrix->mapRect(&devBounds, args.fShape->bounds());
     80 
     81     args.fClip->getConservativeBounds(rtc->width(), rtc->height(), &fHeadDraw.fClipBounds, nullptr);
     82     fHeadDraw.fScissorMode = fHeadDraw.fClipBounds.contains(devBounds) ?
     83                              ScissorMode::kNonScissored : ScissorMode::kScissored;
     84     fHeadDraw.fMatrix = *args.fViewMatrix;
     85     args.fShape->asPath(&fHeadDraw.fPath);
     86     fHeadDraw.fColor = color; // Can't call args.fPaint.getColor() because it has been std::move'd.
     87 
     88     // FIXME: intersect with clip bounds to (hopefully) improve batching.
     89     // (This is nontrivial due to assumptions in generating the octagon cover geometry.)
     90     this->setBounds(devBounds, GrOp::HasAABloat::kYes, GrOp::IsZeroArea::kNo);
     91 }
     92 
     93 GrDrawOp::RequiresDstTexture DrawPathsOp::finalize(const GrCaps& caps, const GrAppliedClip* clip) {
     94     SingleDraw& onlyDraw = this->getOnlyPathDraw();
     95     GrProcessorSet::Analysis analysis = fProcessors.finalize(onlyDraw.fColor,
     96                                                         GrProcessorAnalysisCoverage::kSingleChannel,
     97                                                         clip, false, caps, &onlyDraw.fColor);
     98     return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
     99 }
    100 
    101 bool DrawPathsOp::onCombineIfPossible(GrOp* op, const GrCaps& caps) {
    102     DrawPathsOp* that = op->cast<DrawPathsOp>();
    103     SkASSERT(fCCPR == that->fCCPR);
    104     SkASSERT(fOwningRTPendingOps);
    105     SkASSERT(fDebugInstanceCount);
    106     SkASSERT(that->fDebugInstanceCount);
    107 
    108     if (this->getFillType() != that->getFillType() ||
    109         fSRGBFlags != that->fSRGBFlags ||
    110         fProcessors != that->fProcessors) {
    111         return false;
    112     }
    113 
    114     if (RTPendingOps* owningRTPendingOps = that->fOwningRTPendingOps) {
    115         SkASSERT(owningRTPendingOps == fOwningRTPendingOps);
    116         owningRTPendingOps->fOpList.remove(that);
    117     } else {
    118         // wasRecorded is not called when the op gets combined first. Count path items here instead.
    119         SingleDraw& onlyDraw = that->getOnlyPathDraw();
    120         fOwningRTPendingOps->fMaxBufferItems.countPathItems(onlyDraw.fScissorMode, onlyDraw.fPath);
    121     }
    122 
    123     fTailDraw->fNext = &fOwningRTPendingOps->fDrawsAllocator.push_back(that->fHeadDraw);
    124     fTailDraw = that->fTailDraw == &that->fHeadDraw ? fTailDraw->fNext : that->fTailDraw;
    125 
    126     this->joinBounds(*that);
    127 
    128     SkDEBUGCODE(fDebugInstanceCount += that->fDebugInstanceCount;)
    129     SkDEBUGCODE(that->fDebugInstanceCount = 0);
    130     return true;
    131 }
    132 
    133 void DrawPathsOp::wasRecorded(GrRenderTargetOpList* opList) {
    134     SkASSERT(!fOwningRTPendingOps);
    135     SingleDraw& onlyDraw = this->getOnlyPathDraw();
    136     fOwningRTPendingOps = &fCCPR->fRTPendingOpsMap[opList->uniqueID()];
    137     fOwningRTPendingOps->fOpList.addToTail(this);
    138     fOwningRTPendingOps->fMaxBufferItems.countPathItems(onlyDraw.fScissorMode, onlyDraw.fPath);
    139 }
    140 
    141 void GrCoverageCountingPathRenderer::preFlush(GrOnFlushResourceProvider* onFlushRP,
    142                                               const uint32_t* opListIDs, int numOpListIDs,
    143                                               SkTArray<sk_sp<GrRenderTargetContext>>* results) {
    144     using PathInstance = GrCCPRPathProcessor::Instance;
    145 
    146     SkASSERT(!fPerFlushIndexBuffer);
    147     SkASSERT(!fPerFlushVertexBuffer);
    148     SkASSERT(!fPerFlushInstanceBuffer);
    149     SkASSERT(fPerFlushAtlases.empty());
    150     SkASSERT(!fFlushing);
    151     SkDEBUGCODE(fFlushing = true;)
    152 
    153     if (fRTPendingOpsMap.empty()) {
    154         return; // Nothing to draw.
    155     }
    156 
    157     SkTInternalLList<DrawPathsOp> flushingOps;
    158     GrCCPRCoverageOpsBuilder::MaxBufferItems maxBufferItems;
    159 
    160     for (int i = 0; i < numOpListIDs; ++i) {
    161         auto it = fRTPendingOpsMap.find(opListIDs[i]);
    162         if (fRTPendingOpsMap.end() != it) {
    163             RTPendingOps& rtPendingOps = it->second;
    164             SkASSERT(!rtPendingOps.fOpList.isEmpty());
    165             flushingOps.concat(std::move(rtPendingOps.fOpList));
    166             maxBufferItems += rtPendingOps.fMaxBufferItems;
    167         }
    168     }
    169 
    170     SkASSERT(flushingOps.isEmpty() == !maxBufferItems.fMaxPaths);
    171     if (flushingOps.isEmpty()) {
    172         return; // Still nothing to draw.
    173     }
    174 
    175     fPerFlushIndexBuffer = GrCCPRPathProcessor::FindOrMakeIndexBuffer(onFlushRP);
    176     if (!fPerFlushIndexBuffer) {
    177         SkDebugf("WARNING: failed to allocate ccpr path index buffer.\n");
    178         return;
    179     }
    180 
    181     fPerFlushVertexBuffer = GrCCPRPathProcessor::FindOrMakeVertexBuffer(onFlushRP);
    182     if (!fPerFlushVertexBuffer) {
    183         SkDebugf("WARNING: failed to allocate ccpr path vertex buffer.\n");
    184         return;
    185     }
    186 
    187     GrCCPRCoverageOpsBuilder atlasOpsBuilder;
    188     if (!atlasOpsBuilder.init(onFlushRP, maxBufferItems)) {
    189         SkDebugf("WARNING: failed to allocate buffers for coverage ops. No paths will be drawn.\n");
    190         return;
    191     }
    192 
    193     fPerFlushInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
    194                                                    maxBufferItems.fMaxPaths * sizeof(PathInstance));
    195     if (!fPerFlushInstanceBuffer) {
    196         SkDebugf("WARNING: failed to allocate path instance buffer. No paths will be drawn.\n");
    197         return;
    198     }
    199 
    200     PathInstance* pathInstanceData = static_cast<PathInstance*>(fPerFlushInstanceBuffer->map());
    201     SkASSERT(pathInstanceData);
    202     int pathInstanceIdx = 0;
    203 
    204     GrCCPRAtlas* atlas = nullptr;
    205     SkDEBUGCODE(int skippedPaths = 0;)
    206 
    207     SkTInternalLList<DrawPathsOp>::Iter iter;
    208     iter.init(flushingOps, SkTInternalLList<DrawPathsOp>::Iter::kHead_IterStart);
    209     while (DrawPathsOp* op = iter.get()) {
    210         SkASSERT(op->fDebugInstanceCount > 0);
    211         SkASSERT(-1 == op->fBaseInstance);
    212         op->fBaseInstance = pathInstanceIdx;
    213 
    214         for (const DrawPathsOp::SingleDraw* draw = &op->fHeadDraw; draw; draw = draw->fNext) {
    215             // parsePath gives us two tight bounding boxes: one in device space, as well as a second
    216             // one rotated an additional 45 degrees. The path vertex shader uses these two bounding
    217             // boxes to generate an octagon that circumscribes the path.
    218             SkRect devBounds, devBounds45;
    219             atlasOpsBuilder.parsePath(draw->fScissorMode, draw->fMatrix, draw->fPath, &devBounds,
    220                                       &devBounds45);
    221 
    222             SkRect clippedDevBounds = devBounds;
    223             if (ScissorMode::kScissored == draw->fScissorMode &&
    224                 !clippedDevBounds.intersect(devBounds, SkRect::Make(draw->fClipBounds))) {
    225                 SkDEBUGCODE(--op->fDebugInstanceCount);
    226                 SkDEBUGCODE(++skippedPaths;)
    227                 continue;
    228             }
    229 
    230             SkIRect clippedDevIBounds;
    231             clippedDevBounds.roundOut(&clippedDevIBounds);
    232             const int h = clippedDevIBounds.height(), w = clippedDevIBounds.width();
    233 
    234             SkIPoint16 atlasLocation;
    235             if (atlas && !atlas->addRect(w, h, &atlasLocation)) {
    236                 // The atlas is out of room and can't grow any bigger.
    237                 auto atlasOp = atlasOpsBuilder.createIntermediateOp(atlas->drawBounds());
    238                 if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
    239                     results->push_back(std::move(rtc));
    240                 }
    241                 if (pathInstanceIdx > op->fBaseInstance) {
    242                     op->addAtlasBatch(atlas, pathInstanceIdx);
    243                 }
    244                 atlas = nullptr;
    245             }
    246 
    247             if (!atlas) {
    248                 atlas = &fPerFlushAtlases.emplace_back(*onFlushRP->caps(), w, h);
    249                 SkAssertResult(atlas->addRect(w, h, &atlasLocation));
    250             }
    251 
    252             const SkMatrix& m = draw->fMatrix;
    253             const int16_t offsetX = atlasLocation.x() - static_cast<int16_t>(clippedDevIBounds.x()),
    254                           offsetY = atlasLocation.y() - static_cast<int16_t>(clippedDevIBounds.y());
    255 
    256             pathInstanceData[pathInstanceIdx++] = {
    257                 devBounds,
    258                 devBounds45,
    259                 {{m.getScaleX(), m.getSkewY(), m.getSkewX(), m.getScaleY()}},
    260                 {{m.getTranslateX(), m.getTranslateY()}},
    261                 {{offsetX, offsetY}},
    262                 draw->fColor
    263             };
    264 
    265             atlasOpsBuilder.saveParsedPath(clippedDevIBounds, offsetX, offsetY);
    266         }
    267 
    268         SkASSERT(pathInstanceIdx == op->fBaseInstance + op->fDebugInstanceCount);
    269         op->addAtlasBatch(atlas, pathInstanceIdx);
    270 
    271         iter.next();
    272     }
    273 
    274     SkASSERT(pathInstanceIdx == maxBufferItems.fMaxPaths - skippedPaths);
    275     fPerFlushInstanceBuffer->unmap();
    276 
    277     std::unique_ptr<GrDrawOp> atlasOp = atlasOpsBuilder.finalize(atlas->drawBounds());
    278     if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
    279         results->push_back(std::move(rtc));
    280     }
    281 
    282     // Erase these last, once we are done accessing data from the SingleDraw allocators.
    283     for (int i = 0; i < numOpListIDs; ++i) {
    284         fRTPendingOpsMap.erase(opListIDs[i]);
    285     }
    286 }
    287 
    288 void DrawPathsOp::onExecute(GrOpFlushState* flushState) {
    289     SkASSERT(fCCPR->fFlushing);
    290 
    291     if (!fCCPR->fPerFlushInstanceBuffer) {
    292         return; // Setup failed.
    293     }
    294 
    295     GrPipeline pipeline;
    296     GrPipeline::InitArgs args;
    297     args.fAppliedClip = flushState->drawOpArgs().fAppliedClip;
    298     args.fCaps = &flushState->caps();
    299     args.fProcessors = &fProcessors;
    300     args.fFlags = fSRGBFlags;
    301     args.fRenderTarget = flushState->drawOpArgs().fRenderTarget;
    302     args.fDstProxy = flushState->drawOpArgs().fDstProxy;
    303     pipeline.init(args);
    304 
    305     int baseInstance = fBaseInstance;
    306 
    307     for (int i = 0; i < fAtlasBatches.count(); baseInstance = fAtlasBatches[i++].fEndInstanceIdx) {
    308         const AtlasBatch& batch = fAtlasBatches[i];
    309         SkASSERT(batch.fEndInstanceIdx > baseInstance);
    310 
    311         if (!batch.fAtlas->textureProxy()) {
    312             continue; // Atlas failed to allocate.
    313         }
    314 
    315         GrCCPRPathProcessor coverProc(flushState->resourceProvider(), batch.fAtlas->textureProxy(),
    316                                      this->getFillType(), *flushState->gpu()->caps()->shaderCaps());
    317 
    318         GrMesh mesh(GrPrimitiveType::kTriangles);
    319         mesh.setIndexedInstanced(fCCPR->fPerFlushIndexBuffer.get(),
    320                                  GrCCPRPathProcessor::kPerInstanceIndexCount,
    321                                  fCCPR->fPerFlushInstanceBuffer.get(),
    322                                  batch.fEndInstanceIdx - baseInstance, baseInstance);
    323         mesh.setVertexData(fCCPR->fPerFlushVertexBuffer.get());
    324 
    325         flushState->commandBuffer()->draw(pipeline, coverProc, &mesh, nullptr, 1, this->bounds());
    326     }
    327 
    328     SkASSERT(baseInstance == fBaseInstance + fDebugInstanceCount);
    329 }
    330 
    331 void GrCoverageCountingPathRenderer::postFlush() {
    332     SkASSERT(fFlushing);
    333     fPerFlushAtlases.reset();
    334     fPerFlushInstanceBuffer.reset();
    335     fPerFlushVertexBuffer.reset();
    336     fPerFlushIndexBuffer.reset();
    337     SkDEBUGCODE(fFlushing = false;)
    338 }
    339