void GrPipeline::adjustProgramFromOptimizations(const GrPipelineBuilder& pipelineBuilder, GrXferProcessor::OptFlags flags, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, int* firstColorStageIdx, int* firstCoverageStageIdx) { fReadsFragPosition = fXferProcessor->willReadFragmentPosition(); if ((flags & GrXferProcessor::kIgnoreColor_OptFlag) || (flags & GrXferProcessor::kOverrideColor_OptFlag)) { *firstColorStageIdx = pipelineBuilder.numColorFragmentStages(); } else { if (coveragePOI.readsFragPosition()) { fReadsFragPosition = true; } } if (flags & GrXferProcessor::kIgnoreCoverage_OptFlag) { *firstCoverageStageIdx = pipelineBuilder.numCoverageFragmentStages(); } else { if (coveragePOI.readsFragPosition()) { fReadsFragPosition = true; } } }
void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder, const SkMatrix& viewMatrix, const GrPath* path, GrPathRendering::FillType fill) { // TODO: extract portions of checkDraw that are relevant to path stenciling. SkASSERT(path); SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); // Setup clip GrPipelineBuilder::AutoRestoreStencil ars; GrAppliedClip clip; if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, nullptr, &clip)) { return; } GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; if (clip.clipCoverageFragmentProcessor()) { arfps.set(&pipelineBuilder); arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor()); } // set stencil settings for path GrStencilSettings stencilSettings; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt); this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); GrBatch* batch = GrStencilPathBatch::Create(viewMatrix, pipelineBuilder.isHWAntialias(), stencilSettings, clip.scissorState(), pipelineBuilder.getRenderTarget(), path); this->recordBatch(batch); batch->unref(); }
void GrDrawTarget::clear(const SkIRect* rect, GrColor color, bool canIgnoreRect, GrRenderTarget* renderTarget) { SkIRect rtRect = SkIRect::MakeWH(renderTarget->width(), renderTarget->height()); SkIRect clippedRect; if (!rect || (canIgnoreRect && this->caps()->fullClearIsFree()) || rect->contains(rtRect)) { rect = &rtRect; } else { clippedRect = *rect; if (!clippedRect.intersect(rtRect)) { return; } rect = &clippedRect; } if (fCaps->useDrawInsteadOfClear()) { // This works around a driver bug with clear by drawing a rect instead. // The driver will ignore a clear if it is the only thing rendered to a // target before the target is read. if (rect == &rtRect) { this->discard(renderTarget); } GrPipelineBuilder pipelineBuilder; pipelineBuilder.setRenderTarget(renderTarget); this->drawSimpleRect(pipelineBuilder, color, SkMatrix::I(), *rect); } else { this->onClear(*rect, color, renderTarget); } }
void GrDistanceFieldTextContext::flush() { if (NULL == fDrawTarget) { return; } if (fCurrVertex > 0) { GrPipelineBuilder pipelineBuilder; pipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip); // setup our sampler state for our text texture/atlas SkASSERT(SkIsAlign4(fCurrVertex)); // get our current color SkColor filteredColor; SkColorFilter* colorFilter = fSkPaint.getColorFilter(); if (colorFilter) { filteredColor = colorFilter->filterColor(fSkPaint.getColor()); } else { filteredColor = fSkPaint.getColor(); } this->setupCoverageEffect(filteredColor); // Set draw state if (fUseLCDText) { // TODO: move supportsRGBCoverage check to setupCoverageEffect and only add LCD // processor if the xp can support it. For now we will simply assume that if // fUseLCDText is true, then we have a known color output. const GrXPFactory* xpFactory = pipelineBuilder.getXPFactory(); if (!xpFactory->supportsRGBCoverage(0, kRGBA_GrColorComponentFlags)) { SkDebugf("LCD Text will not draw correctly.\n"); } SkASSERT(!fCachedGeometryProcessor->hasVertexColor()); } else { // We're using per-vertex color. SkASSERT(fCachedGeometryProcessor->hasVertexColor()); } int nGlyphs = fCurrVertex / kVerticesPerGlyph; fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer()); fDrawTarget->drawIndexedInstances(&pipelineBuilder, fCachedGeometryProcessor.get(), kTriangles_GrPrimitiveType, nGlyphs, kVerticesPerGlyph, kIndicesPerGlyph, &fVertexBounds); fDrawTarget->resetVertexSource(); fVertices = NULL; fTotalVertexCount -= fCurrVertex; fCurrVertex = 0; SkSafeSetNull(fCurrTexture); fVertexBounds.setLargestInverted(); } }
DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(VertexAttributeCount, reporter, ctxInfo) { GrContext* context = ctxInfo.fGrContext; GrTextureDesc desc; desc.fHeight = 1; desc.fWidth = 1; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fConfig = kRGBA_8888_GrPixelConfig; SkAutoTUnref<GrTexture> target(context->textureProvider()->createTexture(desc, SkBudgeted::kYes)); if (!target) { ERRORF(reporter, "Could not create render target."); return; } SkAutoTUnref<GrDrawContext> dc(context->drawContext(target->asRenderTarget())); if (!dc) { ERRORF(reporter, "Could not create draw context."); return; } int attribCnt = context->caps()->maxVertexAttributes(); if (!attribCnt) { ERRORF(reporter, "No attributes allowed?!"); return; } context->flush(); context->resetGpuStats(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif SkAutoTUnref<GrDrawBatch> batch; GrPipelineBuilder pb; pb.setRenderTarget(target->asRenderTarget()); // This one should succeed. batch.reset(new Batch(attribCnt)); dc->drawContextPriv().testingOnly_drawBatch(pb, batch); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 1); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 0); #endif context->resetGpuStats(); // This one should fail. batch.reset(new Batch(attribCnt+1)); dc->drawContextPriv().testingOnly_drawBatch(pb, batch); context->flush(); #if GR_GPU_STATS REPORTER_ASSERT(reporter, context->getGpu()->stats()->numDraws() == 0); REPORTER_ASSERT(reporter, context->getGpu()->stats()->numFailedDraws() == 1); #endif }
bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, GrDeviceCoordTexture* dstCopy, const SkRect* drawBounds) { if (!pipelineBuilder.willXPNeedDstCopy(*this->caps(), colorPOI, coveragePOI)) { return true; } SkIRect copyRect; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); pipelineBuilder.clip().getConservativeBounds(rt, ©Rect); if (drawBounds) { SkIRect drawIBounds; drawBounds->roundOut(&drawIBounds); if (!copyRect.intersect(drawIBounds)) { #ifdef SK_DEBUG SkDebugf("Missed an early reject. Bailing on draw from setupDstReadIfNecessary.\n"); #endif return false; } } else { #ifdef SK_DEBUG //SkDebugf("No dev bounds when dst copy is made.\n"); #endif } // MSAA consideration: When there is support for reading MSAA samples in the shader we could // have per-sample dst values by making the copy multisampled. GrSurfaceDesc desc; this->initCopySurfaceDstDesc(rt, &desc); desc.fWidth = copyRect.width(); desc.fHeight = copyRect.height(); SkAutoTUnref<GrTexture> copy( fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch)); if (!copy) { SkDebugf("Failed to create temporary copy of destination texture.\n"); return false; } SkIPoint dstPoint = {0, 0}; if (this->copySurface(copy, rt, copyRect, dstPoint)) { dstCopy->setTexture(copy); dstCopy->setOffset(copyRect.fLeft, copyRect.fTop); return true; } else { return false; } }
/* * This method traverses the clip stack to see if the GrSoftwarePathRenderer * will be used on any element. If so, it returns true to indicate that the * entire clip should be rendered in SW and then uploaded en masse to the gpu. */ bool GrClipMaskManager::UseSWOnlyPath(GrContext* context, const GrPipelineBuilder& pipelineBuilder, const GrDrawContext* drawContext, const SkVector& clipToMaskOffset, const GrReducedClip::ElementList& elements) { // TODO: generalize this function so that when // a clip gets complex enough it can just be done in SW regardless // of whether it would invoke the GrSoftwarePathRenderer. // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY); for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); bool invert = element->isInverseFilled(); bool needsStencil = invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op; if (PathNeedsSWRenderer(context, pipelineBuilder.hasUserStencilSettings(), drawContext, translate, element, nullptr, needsStencil)) { return true; } } return false; }
bool GrClipMaskManager::setupScissorClip(const GrPipelineBuilder& pipelineBuilder, GrPipelineBuilder::AutoRestoreStencil* ars, const SkIRect& clipScissor, const SkRect* devBounds, GrAppliedClip* out) { if (kRespectClip_StencilClipMode == fClipMode) { fClipMode = kIgnoreClip_StencilClipMode; } GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height()); SkIRect devBoundsScissor; const SkIRect* scissor = &clipScissor; bool doDevBoundsClip = fDebugClipBatchToBounds && devBounds; if (doDevBoundsClip) { devBounds->roundOut(&devBoundsScissor); if (devBoundsScissor.intersect(clipScissor)) { scissor = &devBoundsScissor; } } if (scissor->contains(clipSpaceRTIBounds)) { // This counts as wide open this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; } if (clipSpaceRTIBounds.intersect(*scissor)) { out->fScissorState.set(clipSpaceRTIBounds); this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; } return false; }
void GrDrawTarget::drawPath(const GrPipelineBuilder& pipelineBuilder, const GrPathProcessor* pathProc, const GrPath* path, GrPathRendering::FillType fill) { // TODO: extract portions of checkDraw that are relevant to path rendering. SkASSERT(path); SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); SkRect devBounds = path->getBounds(); pathProc->viewMatrix().mapRect(&devBounds); // Setup clip GrScissorState scissorState; GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; GrPipelineBuilder::AutoRestoreStencil ars; if (!this->setupClip(pipelineBuilder, &arfps, &ars, &scissorState, &devBounds)) { return; } // set stencil settings for path GrStencilSettings stencilSettings; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment(); this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, pathProc, &devBounds, this); if (pipelineInfo.mustSkipDraw()) { return; } this->onDrawPath(pathProc, path, stencilSettings, pipelineInfo); }
void GrDrawTarget::drawPathBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawPathBatchBase* batch) { // This looks like drawBatch() but there is an added wrinkle that stencil settings get inserted // after setting up clipping but before onDrawBatch(). TODO: Figure out a better model for // handling stencil settings WRT interactions between pipeline(builder), clipmaskmanager, and // batches. SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); GrPipelineBuilder::AutoRestoreStencil ars; GrAppliedClip clip; if (!fClipMaskManager->setupClipping(pipelineBuilder, &ars, &batch->bounds(), &clip)) { return; } GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; if (clip.clipCoverageFragmentProcessor()) { arfps.set(&pipelineBuilder); arfps.addCoverageFragmentProcessor(clip.clipCoverageFragmentProcessor()); } // Ensure the render target has a stencil buffer and get the stencil settings. GrStencilSettings stencilSettings; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* sb = fResourceProvider->attachStencilAttachment(rt); this->getPathStencilSettingsForFilltype(batch->fillType(), sb, &stencilSettings); batch->setStencilSettings(stencilSettings); GrPipeline::CreateArgs args; if (!this->installPipelineInDrawBatch(&pipelineBuilder, &clip.scissorState(), batch)) { return; } this->recordBatch(batch); }
void GrPipeline::adjustProgramFromOptimizations(const GrPipelineBuilder& pipelineBuilder, GrXferProcessor::OptFlags flags, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, int* firstColorProcessorIdx, int* firstCoverageProcessorIdx) { fIgnoresCoverage = SkToBool(flags & GrXferProcessor::kIgnoreCoverage_OptFlag); if ((flags & GrXferProcessor::kIgnoreColor_OptFlag) || (flags & GrXferProcessor::kOverrideColor_OptFlag)) { *firstColorProcessorIdx = pipelineBuilder.numColorFragmentProcessors(); } if (flags & GrXferProcessor::kIgnoreCoverage_OptFlag) { *firstCoverageProcessorIdx = pipelineBuilder.numCoverageFragmentProcessors(); } }
static void test_path(GrDrawTarget* dt, GrRenderTarget* rt, GrResourceProvider* rp, const SkPath& path) { GrTessellatingPathRenderer tess; GrPipelineBuilder pipelineBuilder; pipelineBuilder.setRenderTarget(rt); GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle); GrPathRenderer::DrawPathArgs args; args.fTarget = dt; args.fPipelineBuilder = &pipelineBuilder; args.fResourceProvider = rp; args.fColor = GrColor_WHITE; args.fViewMatrix = &SkMatrix::I(); args.fPath = &path; args.fStroke = &stroke; args.fAntiAlias = false; tess.drawPath(args); }
bool GrDrawTarget::copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint) { SkASSERT(dst); SkASSERT(src); SkIRect clippedSrcRect; SkIPoint clippedDstPoint; // If the rect is outside the src or dst then we've already succeeded. if (!clip_srcrect_and_dstpoint(dst, src, srcRect, dstPoint, &clippedSrcRect, &clippedDstPoint)) { return true; } if (this->onCopySurface(dst, src, clippedSrcRect, clippedDstPoint)) { return true; } GrRenderTarget* rt = dst->asRenderTarget(); GrTexture* tex = src->asTexture(); if ((dst == src) || !rt || !tex) { return false; } GrPipelineBuilder pipelineBuilder; pipelineBuilder.setRenderTarget(rt); SkMatrix matrix; matrix.setTranslate(SkIntToScalar(clippedSrcRect.fLeft - clippedDstPoint.fX), SkIntToScalar(clippedSrcRect.fTop - clippedDstPoint.fY)); matrix.postIDiv(tex->width(), tex->height()); pipelineBuilder.addColorTextureProcessor(tex, matrix); SkIRect dstRect = SkIRect::MakeXYWH(clippedDstPoint.fX, clippedDstPoint.fY, clippedSrcRect.width(), clippedSrcRect.height()); this->drawSimpleRect(&pipelineBuilder, GrColor_WHITE, SkMatrix::I(), dstRect); return true; }
void GrDrawTarget::clear(const SkIRect* rect, GrColor color, bool canIgnoreRect, GrRenderTarget* renderTarget) { SkIRect rtRect = SkIRect::MakeWH(renderTarget->width(), renderTarget->height()); SkIRect clippedRect; if (!rect || (canIgnoreRect && this->caps()->fullClearIsFree()) || rect->contains(rtRect)) { rect = &rtRect; } else { clippedRect = *rect; if (!clippedRect.intersect(rtRect)) { return; } rect = &clippedRect; } if (this->caps()->useDrawInsteadOfClear()) { // This works around a driver bug with clear by drawing a rect instead. // The driver will ignore a clear if it is the only thing rendered to a // target before the target is read. if (rect == &rtRect) { this->discard(renderTarget); } GrPipelineBuilder pipelineBuilder; pipelineBuilder.setXPFactory( GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref(); pipelineBuilder.setRenderTarget(renderTarget); SkRect scalarRect = SkRect::Make(*rect); SkAutoTUnref<GrDrawBatch> batch( GrRectBatchFactory::CreateNonAAFill(color, SkMatrix::I(), scalarRect, nullptr, nullptr)); this->drawBatch(pipelineBuilder, batch); } else { GrBatch* batch = new GrClearBatch(*rect, color, renderTarget); this->recordBatch(batch); batch->unref(); } }
static void test_path(GrDrawTarget* dt, GrDrawContext* drawContext, GrResourceProvider* rp, const SkPath& path) { GrTessellatingPathRenderer tess; GrPipelineBuilder pipelineBuilder; pipelineBuilder.setXPFactory( GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref(); pipelineBuilder.setRenderTarget(drawContext->accessRenderTarget()); GrNoClip noClip; GrStyle style(SkStrokeRec::kFill_InitStyle); GrPathRenderer::DrawPathArgs args; args.fTarget = dt; args.fPipelineBuilder = &pipelineBuilder; args.fClip = &noClip; args.fResourceProvider = rp; args.fColor = GrColor_WHITE; args.fViewMatrix = &SkMatrix::I(); args.fPath = &path; args.fStyle = &style; args.fAntiAlias = false; tess.drawPath(args); }
void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder, GrDrawContext* drawContext, const GrClip& clip, const SkMatrix& viewMatrix, const GrPath* path, GrPathRendering::FillType fill) { // TODO: extract portions of checkDraw that are relevant to path stenciling. SkASSERT(path); SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); // Setup clip GrAppliedClip appliedClip; if (!clip.apply(fContext, pipelineBuilder, drawContext, nullptr, &appliedClip)) { return; } // TODO: respect fClipBatchToBounds if we ever start computing bounds here. // Coverage AA does not make sense when rendering to the stencil buffer. The caller should never // attempt this in a situation that would require coverage AA. SkASSERT(!appliedClip.clipCoverageFragmentProcessor()); GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* stencilAttachment = fResourceProvider->attachStencilAttachment(rt); if (!stencilAttachment) { SkDebugf("ERROR creating stencil attachment. Draw skipped.\n"); return; } GrBatch* batch = GrStencilPathBatch::Create(viewMatrix, pipelineBuilder.isHWAntialias(), fill, appliedClip.hasStencilClip(), stencilAttachment->bits(), appliedClip.scissorState(), pipelineBuilder.getRenderTarget(), path); this->recordBatch(batch); batch->unref(); }
void GrDrawTarget::clear(const SkIRect* rect, GrColor color, bool canIgnoreRect, GrRenderTarget* renderTarget) { if (fCaps->useDrawInsteadOfClear()) { // This works around a driver bug with clear by drawing a rect instead. // The driver will ignore a clear if it is the only thing rendered to a // target before the target is read. SkIRect rtRect = SkIRect::MakeWH(renderTarget->width(), renderTarget->height()); if (NULL == rect || canIgnoreRect || rect->contains(rtRect)) { rect = &rtRect; // We first issue a discard() since that may help tilers. this->discard(renderTarget); } GrPipelineBuilder pipelineBuilder; pipelineBuilder.setRenderTarget(renderTarget); this->drawSimpleRect(pipelineBuilder, color, SkMatrix::I(), *rect); } else { this->onClear(rect, color, canIgnoreRect, renderTarget); } }
void GrClipMaskManager::setPipelineBuilderStencil(const GrPipelineBuilder& pipelineBuilder, GrPipelineBuilder::AutoRestoreStencil* ars) { // We make two copies of the StencilSettings here (except in the early // exit scenario. One copy from draw state to the stack var. Then another // from the stack var to the gpu. We could make this class hold a ptr to // GrGpu's fStencilSettings and eliminate the stack copy here. // use stencil for clipping if clipping is enabled and the clip // has been written into the stencil. GrStencilSettings settings; // The GrGpu client may not be using the stencil buffer but we may need to // enable it in order to respect a stencil clip. if (pipelineBuilder.getStencil().isDisabled()) { if (GrClipMaskManager::kRespectClip_StencilClipMode == fClipMode) { settings = basic_apply_stencil_clip_settings(); } else { return; } } else { settings = pipelineBuilder.getStencil(); } int stencilBits = 0; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* stencilAttachment = fDrawTarget->cmmAccess().resourceProvider()->attachStencilAttachment(rt); if (stencilAttachment) { stencilBits = stencilAttachment->bits(); } SkASSERT(fDrawTarget->caps()->stencilWrapOpsSupport() || !settings.usesWrapOp()); SkASSERT(fDrawTarget->caps()->twoSidedStencilSupport() || !settings.isTwoSided()); this->adjustStencilParams(&settings, fClipMode, stencilBits); ars->set(&pipelineBuilder); ars->setStencil(settings); }
void GrDrawTarget::drawPaths(const GrPipelineBuilder& pipelineBuilder, const GrPathProcessor* pathProc, const GrPathRange* pathRange, const void* indices, PathIndexType indexType, const float transformValues[], PathTransformType transformType, int count, GrPathRendering::FillType fill) { SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); SkASSERT(pathRange); SkASSERT(indices); SkASSERT(0 == reinterpret_cast<intptr_t>(indices) % GrPathRange::PathIndexSizeInBytes(indexType)); SkASSERT(transformValues); // Setup clip GrScissorState scissorState; GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; GrPipelineBuilder::AutoRestoreStencil ars; if (!this->setupClip(pipelineBuilder, &arfps, &ars, &scissorState, NULL)) { return; } // set stencil settings for path GrStencilSettings stencilSettings; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment(); this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); // Don't compute a bounding box for dst copy texture, we'll opt // instead for it to just copy the entire dst. Realistically this is a moot // point, because any context that supports NV_path_rendering will also // support NV_blend_equation_advanced. GrDrawTarget::PipelineInfo pipelineInfo(pipelineBuilder, &scissorState, pathProc, NULL, this); if (pipelineInfo.mustSkipDraw()) { return; } this->onDrawPaths(pathProc, pathRange, indices, indexType, transformValues, transformType, count, stencilSettings, pipelineInfo); }
void GrDrawTarget::stencilPath(const GrPipelineBuilder& pipelineBuilder, const GrPathProcessor* pathProc, const GrPath* path, GrPathRendering::FillType fill) { // TODO: extract portions of checkDraw that are relevant to path stenciling. SkASSERT(path); SkASSERT(this->caps()->shaderCaps()->pathRenderingSupport()); // Setup clip GrScissorState scissorState; GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; GrPipelineBuilder::AutoRestoreStencil ars; if (!this->setupClip(pipelineBuilder, &arfps, &ars, &scissorState, NULL)) { return; } // set stencil settings for path GrStencilSettings stencilSettings; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrStencilAttachment* sb = rt->renderTargetPriv().attachStencilAttachment(); this->getPathStencilSettingsForFilltype(fill, sb, &stencilSettings); this->onStencilPath(pipelineBuilder, pathProc, path, scissorState, stencilSettings); }
bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { SkASSERT(!args.fStroke->isHairlineStyle()); const SkPath& path = *args.fPath; GrPipelineBuilder* pipelineBuilder = args.fPipelineBuilder; const SkMatrix& viewMatrix = *args.fViewMatrix; SkASSERT(pipelineBuilder->getStencil().isDisabled()); if (args.fAntiAlias) { SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled()); pipelineBuilder->enableState(GrPipelineBuilder::kHWAntialias_Flag); } SkAutoTUnref<GrPath> p(get_gr_path(fResourceProvider, path, *args.fStroke)); if (path.isInverseFillType()) { GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass, kKeep_StencilOp, kZero_StencilOp, // We know our rect will hit pixels outside the clip and the user bits will be 0 // outside the clip. So we can't just fill where the user bits are 0. We also need to // check that the clip bit is set. kEqualIfInClip_StencilFunc, 0xffff, 0x0000, 0xffff); pipelineBuilder->setStencil(kInvertedStencilPass); // fake inverse with a stencil and cover SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(GrColor_WHITE, viewMatrix)); args.fTarget->stencilPath(*pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType())); SkMatrix invert = SkMatrix::I(); SkRect bounds = SkRect::MakeLTRB(0, 0, SkIntToScalar(pipelineBuilder->getRenderTarget()->width()), SkIntToScalar(pipelineBuilder->getRenderTarget()->height())); SkMatrix vmi; // mapRect through persp matrix may not be correct if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { vmi.mapRect(&bounds); // theoretically could set bloat = 0, instead leave it because of matrix inversion // precision. SkScalar bloat = viewMatrix.getMaxScale() * SK_ScalarHalf; bounds.outset(bloat, bloat); } else { if (!viewMatrix.invert(&invert)) { return false; } } const SkMatrix& viewM = viewMatrix.hasPerspective() ? SkMatrix::I() : viewMatrix; args.fTarget->drawNonAARect(*pipelineBuilder, args.fColor, viewM, bounds, invert); } else { GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kKeep_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); pipelineBuilder->setStencil(kStencilPass); SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(args.fColor, viewMatrix)); args.fTarget->drawPath(*pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType())); } pipelineBuilder->stencil()->setDisabled(); return true; }
bool GrDrawTarget::setupDstReadIfNecessary(const GrPipelineBuilder& pipelineBuilder, const GrPipelineOptimizations& optimizations, GrXferProcessor::DstTexture* dstTexture, const SkRect& batchBounds) { SkRect bounds = batchBounds; bounds.outset(0.5f, 0.5f); if (!pipelineBuilder.willXPNeedDstTexture(*this->caps(), optimizations)) { return true; } GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); if (this->caps()->textureBarrierSupport()) { if (GrTexture* rtTex = rt->asTexture()) { // The render target is a texture, so we can read from it directly in the shader. The XP // will be responsible to detect this situation and request a texture barrier. dstTexture->setTexture(rtTex); dstTexture->setOffset(0, 0); return true; } } SkIRect copyRect; pipelineBuilder.clip().getConservativeBounds(rt->width(), rt->height(), ©Rect); SkIRect drawIBounds; bounds.roundOut(&drawIBounds); if (!copyRect.intersect(drawIBounds)) { #ifdef SK_DEBUG GrCapsDebugf(this->caps(), "Missed an early reject. " "Bailing on draw from setupDstReadIfNecessary.\n"); #endif return false; } // MSAA consideration: When there is support for reading MSAA samples in the shader we could // have per-sample dst values by making the copy multisampled. GrSurfaceDesc desc; if (!fGpu->initCopySurfaceDstDesc(rt, &desc)) { desc.fOrigin = kDefault_GrSurfaceOrigin; desc.fFlags = kRenderTarget_GrSurfaceFlag; desc.fConfig = rt->config(); } desc.fWidth = copyRect.width(); desc.fHeight = copyRect.height(); static const uint32_t kFlags = 0; SkAutoTUnref<GrTexture> copy(fResourceProvider->createApproxTexture(desc, kFlags)); if (!copy) { SkDebugf("Failed to create temporary copy of destination texture.\n"); return false; } SkIPoint dstPoint = {0, 0}; this->copySurface(copy, rt, copyRect, dstPoint); dstTexture->setTexture(copy); dstTexture->setOffset(copyRect.fLeft, copyRect.fTop); return true; }
GrPipeline::GrPipeline(const GrPipelineBuilder& pipelineBuilder, const GrProcOptInfo& colorPOI, const GrProcOptInfo& coveragePOI, const GrDrawTargetCaps& caps, const GrScissorState& scissorState, const GrDeviceCoordTexture* dstCopy) { // Create XferProcessor from DS's XPFactory SkAutoTUnref<GrXferProcessor> xferProcessor( pipelineBuilder.getXPFactory()->createXferProcessor(colorPOI, coveragePOI, dstCopy, caps)); GrColor overrideColor = GrColor_ILLEGAL; if (colorPOI.firstEffectiveStageIndex() != 0) { overrideColor = colorPOI.inputColorToEffectiveStage(); } GrXferProcessor::OptFlags optFlags; if (xferProcessor) { fXferProcessor.reset(xferProcessor.get()); optFlags = xferProcessor->getOptimizations(colorPOI, coveragePOI, pipelineBuilder.getStencil().doesWrite(), &overrideColor, caps); } // When path rendering the stencil settings are not always set on the GrPipelineBuilder // so we must check the draw type. In cases where we will skip drawing we simply return a // null GrPipeline. if (!xferProcessor || (GrXferProcessor::kSkipDraw_OptFlag & optFlags)) { // Set the fields that don't default init and return. The lack of a render target will // indicate that this can be skipped. fFlags = 0; fDrawFace = GrPipelineBuilder::kInvalid_DrawFace; return; } fRenderTarget.reset(pipelineBuilder.fRenderTarget.get()); SkASSERT(fRenderTarget); fScissorState = scissorState; fStencilSettings = pipelineBuilder.getStencil(); fDrawFace = pipelineBuilder.getDrawFace(); fFlags = 0; if (pipelineBuilder.isHWAntialias()) { fFlags |= kHWAA_Flag; } if (pipelineBuilder.isDither()) { fFlags |= kDither_Flag; } if (pipelineBuilder.snapVerticesToPixelCenters()) { fFlags |= kSnapVertices_Flag; } int firstColorStageIdx = colorPOI.firstEffectiveStageIndex(); // TODO: Once we can handle single or four channel input into coverage stages then we can use // GrPipelineBuilder's coverageProcInfo (like color above) to set this initial information. int firstCoverageStageIdx = 0; this->adjustProgramFromOptimizations(pipelineBuilder, optFlags, colorPOI, coveragePOI, &firstColorStageIdx, &firstCoverageStageIdx); bool usesLocalCoords = false; // Copy Stages from PipelineBuilder to Pipeline for (int i = firstColorStageIdx; i < pipelineBuilder.numColorFragmentStages(); ++i) { SkNEW_APPEND_TO_TARRAY(&fFragmentStages, GrPendingFragmentStage, (pipelineBuilder.fColorStages[i])); usesLocalCoords = usesLocalCoords || pipelineBuilder.fColorStages[i].processor()->usesLocalCoords(); } fNumColorStages = fFragmentStages.count(); for (int i = firstCoverageStageIdx; i < pipelineBuilder.numCoverageFragmentStages(); ++i) { SkNEW_APPEND_TO_TARRAY(&fFragmentStages, GrPendingFragmentStage, (pipelineBuilder.fCoverageStages[i])); usesLocalCoords = usesLocalCoords || pipelineBuilder.fCoverageStages[i].processor()->usesLocalCoords(); } // let the GP init the batch tracker fInitBT.fColorIgnored = SkToBool(optFlags & GrXferProcessor::kIgnoreColor_OptFlag); fInitBT.fOverrideColor = fInitBT.fColorIgnored ? GrColor_ILLEGAL : overrideColor; fInitBT.fCoverageIgnored = SkToBool(optFlags & GrXferProcessor::kIgnoreCoverage_OptFlag); fInitBT.fUsesLocalCoords = usesLocalCoords; fInitBT.fCanTweakAlphaForCoverage = SkToBool(optFlags & GrXferProcessor::kCanTweakAlphaForCoverage_OptFlag); }
bool GrDrawingManager::ProgramUnitTest(GrContext* context, GrDrawTarget* drawTarget, int maxStages) { GrDrawingManager* drawingManager = context->drawingManager(); // setup dummy textures GrSurfaceDesc dummyDesc; dummyDesc.fFlags = kRenderTarget_GrSurfaceFlag; dummyDesc.fConfig = kSkia8888_GrPixelConfig; dummyDesc.fWidth = 34; dummyDesc.fHeight = 18; SkAutoTUnref<GrTexture> dummyTexture1( context->textureProvider()->createTexture(dummyDesc, false, nullptr, 0)); dummyDesc.fFlags = kNone_GrSurfaceFlags; dummyDesc.fConfig = kAlpha_8_GrPixelConfig; dummyDesc.fWidth = 16; dummyDesc.fHeight = 22; SkAutoTUnref<GrTexture> dummyTexture2( context->textureProvider()->createTexture(dummyDesc, false, nullptr, 0)); if (!dummyTexture1 || ! dummyTexture2) { SkDebugf("Could not allocate dummy textures"); return false; } GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()}; // dummy scissor state GrScissorState scissor; // wide open clip GrClip clip; SkRandom random; static const int NUM_TESTS = 2048; for (int t = 0; t < NUM_TESTS; t++) { // setup random render target(can fail) SkAutoTUnref<GrRenderTarget> rt(random_render_target( context->textureProvider(), &random, context->caps())); if (!rt.get()) { SkDebugf("Could not allocate render target"); return false; } GrPipelineBuilder pipelineBuilder; pipelineBuilder.setRenderTarget(rt.get()); pipelineBuilder.setClip(clip); SkAutoTUnref<GrDrawBatch> batch(GrRandomDrawBatch(&random, context)); SkASSERT(batch); GrProcessorTestData ptd(&random, context, context->caps(), dummyTextures); set_random_color_coverage_stages(&pipelineBuilder, &ptd, maxStages); set_random_xpf(&pipelineBuilder, &ptd); set_random_state(&pipelineBuilder, &random); set_random_stencil(&pipelineBuilder, &random); drawTarget->drawBatch(pipelineBuilder, batch); } // Flush everything, test passes if flush is successful(ie, no asserts are hit, no crashes) drawingManager->flush(); // Validate that GrFPs work correctly without an input. GrSurfaceDesc rtDesc; rtDesc.fWidth = kRenderTargetWidth; rtDesc.fHeight = kRenderTargetHeight; rtDesc.fFlags = kRenderTarget_GrSurfaceFlag; rtDesc.fConfig = kRGBA_8888_GrPixelConfig; SkAutoTUnref<GrRenderTarget> rt( context->textureProvider()->createTexture(rtDesc, false)->asRenderTarget()); int fpFactoryCnt = GrProcessorTestFactory<GrFragmentProcessor>::Count(); for (int i = 0; i < fpFactoryCnt; ++i) { // Since FP factories internally randomize, call each 10 times. for (int j = 0; j < 10; ++j) { SkAutoTUnref<GrDrawBatch> batch(GrRandomDrawBatch(&random, context)); SkASSERT(batch); GrProcessorTestData ptd(&random, context, context->caps(), dummyTextures); GrPipelineBuilder builder; builder.setXPFactory(GrPorterDuffXPFactory::Create(SkXfermode::kSrc_Mode))->unref(); builder.setRenderTarget(rt); builder.setClip(clip); SkAutoTUnref<const GrFragmentProcessor> fp( GrProcessorTestFactory<GrFragmentProcessor>::CreateIdx(i, &ptd)); SkAutoTUnref<const GrFragmentProcessor> blockFP( BlockInputFragmentProcessor::Create(fp)); builder.addColorFragmentProcessor(blockFP); drawTarget->drawBatch(builder, batch); drawingManager->flush(); } } return true; }
bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { GR_AUDIT_TRAIL_AUTO_FRAME(args.fTarget->getAuditTrail(), "GrStencilAndCoverPathRenderer::onDrawPath"); SkASSERT(!args.fStyle->strokeRec().isHairlineStyle()); const SkPath& path = *args.fPath; GrPipelineBuilder* pipelineBuilder = args.fPipelineBuilder; const SkMatrix& viewMatrix = *args.fViewMatrix; SkASSERT(!pipelineBuilder->hasUserStencilSettings()); if (args.fAntiAlias) { SkASSERT(pipelineBuilder->getRenderTarget()->isStencilBufferMultisampled()); pipelineBuilder->enableState(GrPipelineBuilder::kHWAntialias_Flag); } SkAutoTUnref<GrPath> p(get_gr_path(fResourceProvider, path, *args.fStyle)); if (path.isInverseFillType()) { static constexpr GrUserStencilSettings kInvertedCoverPass( GrUserStencilSettings::StaticInit< 0x0000, // We know our rect will hit pixels outside the clip and the user bits will be 0 // outside the clip. So we can't just fill where the user bits are 0. We also need // to check that the clip bit is set. GrUserStencilTest::kEqualIfInClip, 0xffff, GrUserStencilOp::kKeep, GrUserStencilOp::kZero, 0xffff>() ); pipelineBuilder->setUserStencil(&kInvertedCoverPass); // fake inverse with a stencil and cover args.fTarget->stencilPath(*pipelineBuilder, viewMatrix, p, p->getFillType()); SkMatrix invert = SkMatrix::I(); SkRect bounds = SkRect::MakeLTRB(0, 0, SkIntToScalar(pipelineBuilder->getRenderTarget()->width()), SkIntToScalar(pipelineBuilder->getRenderTarget()->height())); SkMatrix vmi; // mapRect through persp matrix may not be correct if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { vmi.mapRect(&bounds); // theoretically could set bloat = 0, instead leave it because of matrix inversion // precision. SkScalar bloat = viewMatrix.getMaxScale() * SK_ScalarHalf; bounds.outset(bloat, bloat); } else { if (!viewMatrix.invert(&invert)) { return false; } } const SkMatrix& viewM = viewMatrix.hasPerspective() ? SkMatrix::I() : viewMatrix; if (pipelineBuilder->getRenderTarget()->hasMixedSamples()) { pipelineBuilder->disableState(GrPipelineBuilder::kHWAntialias_Flag); } SkAutoTUnref<GrDrawBatch> batch( GrRectBatchFactory::CreateNonAAFill(args.fColor, viewM, bounds, nullptr, &invert)); args.fTarget->drawBatch(*pipelineBuilder, batch); } else { static constexpr GrUserStencilSettings kCoverPass( GrUserStencilSettings::StaticInit< 0x0000, GrUserStencilTest::kNotEqual, 0xffff, GrUserStencilOp::kZero, GrUserStencilOp::kKeep, 0xffff>() ); pipelineBuilder->setUserStencil(&kCoverPass); SkAutoTUnref<GrDrawPathBatchBase> batch( GrDrawPathBatch::Create(viewMatrix, args.fColor, p->getFillType(), p)); args.fTarget->drawPathBatch(*pipelineBuilder, batch); } pipelineBuilder->disableUserStencil(); return true; }
//////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software bool GrClipMaskManager::SetupClipping(GrContext* context, const GrPipelineBuilder& pipelineBuilder, GrDrawContext* drawContext, const GrClipStackClip& clip, const SkRect* devBounds, GrAppliedClip* out) { if (!clip.clipStack() || clip.clipStack()->isWideOpen()) { return true; } GrReducedClip::ElementList elements; int32_t genID = 0; GrReducedClip::InitialState initialState = GrReducedClip::kAllIn_InitialState; SkIRect clipSpaceIBounds; bool requiresAA = false; SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(drawContext->width(), drawContext->height()); clipSpaceRTIBounds.offset(clip.origin()); SkIRect clipSpaceReduceQueryBounds; #define DISABLE_DEV_BOUNDS_FOR_CLIP_REDUCTION 0 if (devBounds && !DISABLE_DEV_BOUNDS_FOR_CLIP_REDUCTION) { SkIRect devIBounds = devBounds->roundOut(); devIBounds.offset(clip.origin()); if (!clipSpaceReduceQueryBounds.intersect(clipSpaceRTIBounds, devIBounds)) { return false; } } else { clipSpaceReduceQueryBounds = clipSpaceRTIBounds; } GrReducedClip::ReduceClipStack(*clip.clipStack(), clipSpaceReduceQueryBounds, &elements, &genID, &initialState, &clipSpaceIBounds, &requiresAA); if (elements.isEmpty()) { if (GrReducedClip::kAllIn_InitialState == initialState) { if (clipSpaceIBounds == clipSpaceRTIBounds) { return true; } } else { return false; } } // An element count of 4 was chosen because of the common pattern in Blink of: // isect RR // diff RR // isect convex_poly // isect convex_poly // when drawing rounded div borders. This could probably be tuned based on a // configuration's relative costs of switching RTs to generate a mask vs // longer shaders. if (elements.count() <= kMaxAnalyticElements) { SkVector clipToRTOffset = { SkIntToScalar(-clip.origin().fX), SkIntToScalar(-clip.origin().fY) }; // When there are multiple samples we want to do per-sample clipping, not compute a // fractional pixel coverage. bool disallowAnalyticAA = drawContext->isStencilBufferMultisampled(); if (disallowAnalyticAA && !drawContext->numColorSamples()) { // With a single color sample, any coverage info is lost from color once it hits the // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe // is multisampled. disallowAnalyticAA = pipelineBuilder.isHWAntialias() || pipelineBuilder.hasUserStencilSettings(); } sk_sp<GrFragmentProcessor> clipFP; if (elements.isEmpty() || (requiresAA && get_analytic_clip_processor(elements, disallowAnalyticAA, clipToRTOffset, devBounds, &clipFP))) { SkIRect scissorSpaceIBounds(clipSpaceIBounds); scissorSpaceIBounds.offset(-clip.origin()); if (!devBounds || !SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) { out->makeScissoredFPBased(std::move(clipFP), scissorSpaceIBounds); return true; } out->makeFPBased(std::move(clipFP)); return true; } } // If the stencil buffer is multisampled we can use it to do everything. if (!drawContext->isStencilBufferMultisampled() && requiresAA) { sk_sp<GrTexture> result; // The top-left of the mask corresponds to the top-left corner of the bounds. SkVector clipToMaskOffset = { SkIntToScalar(-clipSpaceIBounds.fLeft), SkIntToScalar(-clipSpaceIBounds.fTop) }; if (UseSWOnlyPath(context, pipelineBuilder, drawContext, clipToMaskOffset, elements)) { // The clip geometry is complex enough that it will be more efficient to create it // entirely in software result = CreateSoftwareClipMask(context->textureProvider(), genID, initialState, elements, clipToMaskOffset, clipSpaceIBounds); } else { result = CreateAlphaClipMask(context, genID, initialState, elements, clipToMaskOffset, clipSpaceIBounds); // If createAlphaClipMask fails it means UseSWOnlyPath has a bug SkASSERT(result); } if (result) { // The mask's top left coord should be pinned to the rounded-out top left corner of // clipSpace bounds. We determine the mask's position WRT to the render target here. SkIRect rtSpaceMaskBounds = clipSpaceIBounds; rtSpaceMaskBounds.offset(-clip.origin()); out->makeFPBased(create_fp_for_mask(result.get(), rtSpaceMaskBounds)); return true; } // if alpha clip mask creation fails fall through to the non-AA code paths } // use the stencil clip if we can't represent the clip as a rectangle. SkIPoint clipSpaceToStencilSpaceOffset = -clip.origin(); CreateStencilClipMask(context, drawContext, genID, initialState, elements, clipSpaceIBounds, clipSpaceToStencilSpaceOffset); // This must occur after createStencilClipMask. That function may change the scissor. Also, it // only guarantees that the stencil mask is correct within the bounds it was passed, so we must // use both stencil and scissor test to the bounds for the final draw. SkIRect scissorSpaceIBounds(clipSpaceIBounds); scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset); out->makeScissoredStencil(true, scissorSpaceIBounds); return true; }
//////////////////////////////////////////////////////////////////////////////// // sort out what kind of clip mask needs to be created: alpha, stencil, // scissor, or entirely software bool GrClipMaskManager::setupClipping(const GrPipelineBuilder& pipelineBuilder, GrPipelineBuilder::AutoRestoreStencil* ars, GrScissorState* scissorState, const SkRect* devBounds, GrAppliedClip* out) { if (kRespectClip_StencilClipMode == fClipMode) { fClipMode = kIgnoreClip_StencilClipMode; } GrReducedClip::ElementList elements(16); int32_t genID = 0; GrReducedClip::InitialState initialState = GrReducedClip::kAllIn_InitialState; SkIRect clipSpaceIBounds; bool requiresAA = false; GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); // GrDrawTarget should have filtered this for us SkASSERT(rt); SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height()); const GrClip& clip = pipelineBuilder.clip(); if (clip.isWideOpen(clipSpaceRTIBounds)) { this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; } // The clip mask manager always draws with a single IRect so we special case that logic here // Image filters just use a rect, so we also special case that logic switch (clip.clipType()) { case GrClip::kWideOpen_ClipType: SkFAIL("Should have caught this with clip.isWideOpen()"); return true; case GrClip::kIRect_ClipType: { SkIRect scissor = clip.irect(); if (scissor.intersect(clipSpaceRTIBounds)) { scissorState->set(scissor); this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; } return false; } case GrClip::kClipStack_ClipType: { clipSpaceRTIBounds.offset(clip.origin()); GrReducedClip::ReduceClipStack(*clip.clipStack(), clipSpaceRTIBounds, &elements, &genID, &initialState, &clipSpaceIBounds, &requiresAA); if (elements.isEmpty()) { if (GrReducedClip::kAllIn_InitialState == initialState) { if (clipSpaceIBounds == clipSpaceRTIBounds) { this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; } } else { return false; } } } break; } // An element count of 4 was chosen because of the common pattern in Blink of: // isect RR // diff RR // isect convex_poly // isect convex_poly // when drawing rounded div borders. This could probably be tuned based on a // configuration's relative costs of switching RTs to generate a mask vs // longer shaders. if (elements.count() <= kMaxAnalyticElements) { SkVector clipToRTOffset = { SkIntToScalar(-clip.origin().fX), SkIntToScalar(-clip.origin().fY) }; // When there are multiple color samples we want to do per-sample clipping, not compute // a fractional pixel coverage. bool disallowAnalyticAA = pipelineBuilder.getRenderTarget()->isUnifiedMultisampled(); const GrFragmentProcessor* clipFP = nullptr; if (elements.isEmpty() || (requiresAA && !disallowAnalyticAA && SkToBool(clipFP = this->getAnalyticClipProcessor(elements, clipToRTOffset, devBounds)))) { SkIRect scissorSpaceIBounds(clipSpaceIBounds); scissorSpaceIBounds.offset(-clip.origin()); if (nullptr == devBounds || !SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) { scissorState->set(scissorSpaceIBounds); } this->setPipelineBuilderStencil(pipelineBuilder, ars); out->fClipCoverageFP.reset(clipFP); return true; } } // If MSAA is enabled we can do everything in the stencil buffer. if (0 == rt->numStencilSamples() && requiresAA) { SkAutoTUnref<GrTexture> result; // The top-left of the mask corresponds to the top-left corner of the bounds. SkVector clipToMaskOffset = { SkIntToScalar(-clipSpaceIBounds.fLeft), SkIntToScalar(-clipSpaceIBounds.fTop) }; if (this->useSWOnlyPath(pipelineBuilder, clipToMaskOffset, elements)) { // The clip geometry is complex enough that it will be more efficient to create it // entirely in software result.reset(this->createSoftwareClipMask(genID, initialState, elements, clipToMaskOffset, clipSpaceIBounds)); } else { result.reset(this->createAlphaClipMask(genID, initialState, elements, clipToMaskOffset, clipSpaceIBounds)); } if (result) { // The mask's top left coord should be pinned to the rounded-out top left corner of // clipSpace bounds. We determine the mask's position WRT to the render target here. SkIRect rtSpaceMaskBounds = clipSpaceIBounds; rtSpaceMaskBounds.offset(-clip.origin()); out->fClipCoverageFP.reset(create_fp_for_mask(result, rtSpaceMaskBounds)); this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; } // if alpha clip mask creation fails fall through to the non-AA code paths } // use the stencil clip if we can't represent the clip as a rectangle. SkIPoint clipSpaceToStencilSpaceOffset = -clip.origin(); this->createStencilClipMask(rt, genID, initialState, elements, clipSpaceIBounds, clipSpaceToStencilSpaceOffset); // This must occur after createStencilClipMask. That function may change the scissor. Also, it // only guarantees that the stencil mask is correct within the bounds it was passed, so we must // use both stencil and scissor test to the bounds for the final draw. SkIRect scissorSpaceIBounds(clipSpaceIBounds); scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset); scissorState->set(scissorSpaceIBounds); this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; }
void GrDrawTarget::drawBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawContext* drawContext, const GrClip& clip, GrDrawBatch* batch) { // Setup clip GrAppliedClip appliedClip; if (!clip.apply(fContext, pipelineBuilder, drawContext, &batch->bounds(), &appliedClip)) { return; } // TODO: this is the only remaining usage of the AutoRestoreFragmentProcessorState - remove it GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; if (appliedClip.getClipCoverageFragmentProcessor()) { arfps.set(&pipelineBuilder); arfps.addCoverageFragmentProcessor(sk_ref_sp(appliedClip.getClipCoverageFragmentProcessor())); } GrPipeline::CreateArgs args; args.fPipelineBuilder = &pipelineBuilder; args.fDrawContext = drawContext; args.fCaps = this->caps(); args.fScissor = &appliedClip.scissorState(); args.fHasStencilClip = appliedClip.hasStencilClip(); if (pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip()) { if (!fResourceProvider->attachStencilAttachment(drawContext->accessRenderTarget())) { SkDebugf("ERROR creating stencil attachment. Draw skipped.\n"); return; } } batch->getPipelineOptimizations(&args.fOpts); GrScissorState finalScissor; if (args.fOpts.fOverrides.fUsePLSDstRead || fClipBatchToBounds) { GrGLIRect viewport; viewport.fLeft = 0; viewport.fBottom = 0; viewport.fWidth = drawContext->width(); viewport.fHeight = drawContext->height(); SkIRect ibounds; ibounds.fLeft = SkTPin(SkScalarFloorToInt(batch->bounds().fLeft), viewport.fLeft, viewport.fWidth); ibounds.fTop = SkTPin(SkScalarFloorToInt(batch->bounds().fTop), viewport.fBottom, viewport.fHeight); ibounds.fRight = SkTPin(SkScalarCeilToInt(batch->bounds().fRight), viewport.fLeft, viewport.fWidth); ibounds.fBottom = SkTPin(SkScalarCeilToInt(batch->bounds().fBottom), viewport.fBottom, viewport.fHeight); if (appliedClip.scissorState().enabled()) { const SkIRect& scissorRect = appliedClip.scissorState().rect(); if (!ibounds.intersect(scissorRect)) { return; } } finalScissor.set(ibounds); args.fScissor = &finalScissor; } args.fOpts.fColorPOI.completeCalculations( sk_sp_address_as_pointer_address(pipelineBuilder.fColorFragmentProcessors.begin()), pipelineBuilder.numColorFragmentProcessors()); args.fOpts.fCoveragePOI.completeCalculations( sk_sp_address_as_pointer_address(pipelineBuilder.fCoverageFragmentProcessors.begin()), pipelineBuilder.numCoverageFragmentProcessors()); if (!this->setupDstReadIfNecessary(pipelineBuilder, drawContext->accessRenderTarget(), clip, args.fOpts, &args.fDstTexture, batch->bounds())) { return; } if (!batch->installPipeline(args)) { return; } #ifdef ENABLE_MDB SkASSERT(fRenderTarget); batch->pipeline()->addDependenciesTo(fRenderTarget); #endif this->recordBatch(batch); }
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkVector& clipToMaskOffset, const SkIRect& clipSpaceIBounds) { GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider(); GrUniqueKey key; GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key); if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) { return texture; } SkAutoTUnref<GrTexture> texture(this->createCachedMask( clipSpaceIBounds.width(), clipSpaceIBounds.height(), key, true)); // There's no texture in the cache. Let's try to allocate it then. if (!texture) { return nullptr; } // Set the matrix so that rendered clip elements are transformed to mask space from clip // space. SkMatrix translate; translate.setTranslate(clipToMaskOffset); // The texture may be larger than necessary, this rect represents the part of the texture // we populate with a rasterization of the clip. SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); // The scratch texture that we are drawing into can be substantially larger than the mask. Only // clear the part that we care about. fDrawTarget->clear(&maskSpaceIBounds, GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, true, texture->asRenderTarget()); // When we use the stencil in the below loop it is important to have this clip installed. // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first // pass must not set values outside of this bounds or stencil values outside the rect won't be // cleared. GrClip clip(maskSpaceIBounds); SkAutoTUnref<GrTexture> temp; // walk through each clip element and perform its set op for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { const Element* element = iter.get(); SkRegion::Op op = element->getOp(); bool invert = element->isInverseFilled(); if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { GrPipelineBuilder pipelineBuilder; pipelineBuilder.setClip(clip); GrPathRenderer* pr = nullptr; bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, texture, &pr, element); GrTexture* dst; // This is the bounds of the clip element in the space of the alpha-mask. The temporary // mask buffer can be substantially larger than the actually clip stack element. We // touch the minimum number of pixels necessary and use decal mode to combine it with // the accumulator. SkIRect maskSpaceElementIBounds; if (useTemp) { if (invert) { maskSpaceElementIBounds = maskSpaceIBounds; } else { SkRect elementBounds = element->getBounds(); elementBounds.offset(clipToMaskOffset); elementBounds.roundOut(&maskSpaceElementIBounds); } if (!temp) { temp.reset(this->createTempMask(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom)); if (!temp) { texture->resourcePriv().removeUniqueKey(); return nullptr; } } dst = temp; // clear the temp target and set blend to replace fDrawTarget->clear(&maskSpaceElementIBounds, invert ? 0xffffffff : 0x00000000, true, dst->asRenderTarget()); set_coverage_drawing_xpf(SkRegion::kReplace_Op, invert, &pipelineBuilder); } else { // draw directly into the result with the stencil set to make the pixels affected // by the clip shape be non-zero. dst = texture; GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, kReplace_StencilOp, kReplace_StencilOp, kAlways_StencilFunc, 0xffff, 0xffff, 0xffff); pipelineBuilder.setStencil(kStencilInElement); set_coverage_drawing_xpf(op, invert, &pipelineBuilder); } if (!this->drawElement(&pipelineBuilder, translate, dst, element, pr)) { texture->resourcePriv().removeUniqueKey(); return nullptr; } if (useTemp) { GrPipelineBuilder backgroundPipelineBuilder; backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget()); // Now draw into the accumulator using the real operation and the temp buffer as a // texture this->mergeMask(&backgroundPipelineBuilder, texture, temp, op, maskSpaceIBounds, maskSpaceElementIBounds); } else { GrPipelineBuilder backgroundPipelineBuilder; backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget()); set_coverage_drawing_xpf(op, !invert, &backgroundPipelineBuilder); // Draw to the exterior pixels (those with a zero stencil value). GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, kZero_StencilOp, kZero_StencilOp, kEqual_StencilFunc, 0xffff, 0x0000, 0xffff); backgroundPipelineBuilder.setStencil(kDrawOutsideElement); // The color passed in here does not matter since the coverageSetOpXP won't read it. fDrawTarget->drawNonAARect(backgroundPipelineBuilder, GrColor_WHITE, translate, clipSpaceIBounds); } } else { GrPipelineBuilder pipelineBuilder; // all the remaining ops can just be directly draw into the accumulation buffer set_coverage_drawing_xpf(op, false, &pipelineBuilder); // The color passed in here does not matter since the coverageSetOpXP won't read it. this->drawElement(&pipelineBuilder, translate, texture, element); } } return texture.detach(); }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(GrRenderTarget* rt, int32_t elementsGenID, GrReducedClip::InitialState initialState, const GrReducedClip::ElementList& elements, const SkIRect& clipSpaceIBounds, const SkIPoint& clipSpaceToStencilOffset) { SkASSERT(rt); GrStencilAttachment* stencilAttachment = fDrawTarget->cmmAccess().resourceProvider()->attachStencilAttachment(rt); if (nullptr == stencilAttachment) { return false; } if (stencilAttachment->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) { stencilAttachment->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset); // Set the matrix so that rendered clip elements are transformed from clip to stencil space. SkVector translate = { SkIntToScalar(clipSpaceToStencilOffset.fX), SkIntToScalar(clipSpaceToStencilOffset.fY) }; SkMatrix viewMatrix; viewMatrix.setTranslate(translate); // We set the current clip to the bounds so that our recursive draws are scissored to them. SkIRect stencilSpaceIBounds(clipSpaceIBounds); stencilSpaceIBounds.offset(clipSpaceToStencilOffset); GrClip clip(stencilSpaceIBounds); int clipBit = stencilAttachment->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); fDrawTarget->cmmAccess().clearStencilClip(stencilSpaceIBounds, GrReducedClip::kAllIn_InitialState == initialState, rt); // walk through each clip element and perform its set op // with the existing clip. for (GrReducedClip::ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { const Element* element = iter.get(); GrPipelineBuilder pipelineBuilder; pipelineBuilder.setClip(clip); pipelineBuilder.setRenderTarget(rt); pipelineBuilder.setDisableColorXPFactory(); // if the target is MSAA then we want MSAA enabled when the clip is soft if (rt->isStencilBufferMultisampled()) { pipelineBuilder.setState(GrPipelineBuilder::kHWAntialias_Flag, element->isAA()); } bool fillInverted = false; // enabled at bottom of loop fClipMode = kIgnoreClip_StencilClipMode; // This will be used to determine whether the clip shape can be rendered into the // stencil with arbitrary stencil settings. GrPathRenderer::StencilSupport stencilSupport; GrStrokeInfo stroke(SkStrokeRec::kFill_InitStyle); SkRegion::Op op = element->getOp(); GrPathRenderer* pr = nullptr; SkPath clipPath; if (Element::kRect_Type == element->getType()) { stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; fillInverted = false; } else { element->asPath(&clipPath); fillInverted = clipPath.isInverseFillType(); if (fillInverted) { clipPath.toggleInverseFillType(); } pr = this->getContext()->getPathRenderer(fDrawTarget, &pipelineBuilder, viewMatrix, clipPath, stroke, false, GrPathRendererChain::kStencilOnly_DrawType, &stencilSupport); if (nullptr == pr) { return false; } } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; bool canRenderDirectToStencil = GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; bool canDrawDirectToClip; // Given the renderer, the element, // fill rule, and set operation can // we render the element directly to // stencil bit used for clipping. canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, clipBit, fillInverted, &passes, stencilSettings); // draw the element to the client stencil bits if necessary if (!canDrawDirectToClip) { GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, kIncClamp_StencilOp, kIncClamp_StencilOp, kAlways_StencilFunc, 0xffff, 0x0000, 0xffff); if (Element::kRect_Type == element->getType()) { *pipelineBuilder.stencil() = gDrawToStencil; // We need this AGP until everything is in GrBatch fDrawTarget->drawNonAARect(pipelineBuilder, GrColor_WHITE, viewMatrix, element->getRect()); } else { if (!clipPath.isEmpty()) { if (canRenderDirectToStencil) { *pipelineBuilder.stencil() = gDrawToStencil; GrPathRenderer::DrawPathArgs args; args.fTarget = fDrawTarget; args.fResourceProvider = this->getContext()->resourceProvider(); args.fPipelineBuilder = &pipelineBuilder; args.fColor = GrColor_WHITE; args.fViewMatrix = &viewMatrix; args.fPath = &clipPath; args.fStroke = &stroke; args.fAntiAlias = false; pr->drawPath(args); } else { GrPathRenderer::StencilPathArgs args; args.fTarget = fDrawTarget; args.fResourceProvider = this->getContext()->resourceProvider(); args.fPipelineBuilder = &pipelineBuilder; args.fViewMatrix = &viewMatrix; args.fPath = &clipPath; args.fStroke = &stroke; pr->stencilPath(args); } } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. fClipMode = kModifyClip_StencilClipMode; for (int p = 0; p < passes; ++p) { *pipelineBuilder.stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (Element::kRect_Type == element->getType()) { // We need this AGP until everything is in GrBatch fDrawTarget->drawNonAARect(pipelineBuilder, GrColor_WHITE, viewMatrix, element->getRect()); } else { GrPathRenderer::DrawPathArgs args; args.fTarget = fDrawTarget; args.fResourceProvider = this->getContext()->resourceProvider(); args.fPipelineBuilder = &pipelineBuilder; args.fColor = GrColor_WHITE; args.fViewMatrix = &viewMatrix; args.fPath = &clipPath; args.fStroke = &stroke; args.fAntiAlias = false; pr->drawPath(args); } } else { // The view matrix is setup to do clip space -> stencil space translation, so // draw rect in clip space. fDrawTarget->drawNonAARect(pipelineBuilder, GrColor_WHITE, viewMatrix, SkRect::Make(clipSpaceIBounds)); } } } } fClipMode = kRespectClip_StencilClipMode; return true; }