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::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); }
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::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(); }
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; } }
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 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::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 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; }
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(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, const GrClip& clip, GrDrawBatch* batch) { // Setup clip GrAppliedClip appliedClip; if (!clip.apply(fClipMaskManager, pipelineBuilder, &batch->bounds(), &appliedClip)) { return; } GrPipelineBuilder::AutoRestoreFragmentProcessorState arfps; if (appliedClip.clipCoverageFragmentProcessor()) { arfps.set(&pipelineBuilder); arfps.addCoverageFragmentProcessor(appliedClip.clipCoverageFragmentProcessor()); } GrPipeline::CreateArgs args; args.fPipelineBuilder = &pipelineBuilder; args.fCaps = this->caps(); args.fScissor = &appliedClip.scissorState(); args.fHasStencilClip = appliedClip.hasStencilClip(); if (pipelineBuilder.hasUserStencilSettings() || appliedClip.hasStencilClip()) { if (!fResourceProvider->attachStencilAttachment(pipelineBuilder.getRenderTarget())) { SkDebugf("ERROR creating stencil attachment. Draw skipped.\n"); return; } } batch->getPipelineOptimizations(&args.fOpts); GrScissorState finalScissor; if (args.fOpts.fOverrides.fUsePLSDstRead || fClipBatchToBounds) { GrRenderTarget* rt = pipelineBuilder.getRenderTarget(); GrGLIRect viewport; viewport.fLeft = 0; viewport.fBottom = 0; viewport.fWidth = rt->width(); viewport.fHeight = rt->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(pipelineBuilder.fColorFragmentProcessors.begin(), pipelineBuilder.numColorFragmentProcessors()); args.fOpts.fCoveragePOI.completeCalculations( pipelineBuilder.fCoverageFragmentProcessors.begin(), pipelineBuilder.numCoverageFragmentProcessors()); if (!this->setupDstReadIfNecessary(pipelineBuilder, 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); }
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::checkDraw(const GrPipelineBuilder& pipelineBuilder, const GrGeometryProcessor* gp, GrPrimitiveType type, int startVertex, int startIndex, int vertexCount, int indexCount) const { #ifdef SK_DEBUG const GeometrySrcState& geoSrc = fGeoSrcStateStack.back(); int maxVertex = startVertex + vertexCount; int maxValidVertex; switch (geoSrc.fVertexSrc) { case kNone_GeometrySrcType: SkFAIL("Attempting to draw without vertex src."); case kReserved_GeometrySrcType: // fallthrough maxValidVertex = geoSrc.fVertexCount; break; case kBuffer_GeometrySrcType: maxValidVertex = static_cast<int>(geoSrc.fVertexBuffer->gpuMemorySize() / geoSrc.fVertexSize); break; } if (maxVertex > maxValidVertex) { SkFAIL("Drawing outside valid vertex range."); } if (indexCount > 0) { int maxIndex = startIndex + indexCount; int maxValidIndex; switch (geoSrc.fIndexSrc) { case kNone_GeometrySrcType: SkFAIL("Attempting to draw indexed geom without index src."); case kReserved_GeometrySrcType: // fallthrough maxValidIndex = geoSrc.fIndexCount; break; case kBuffer_GeometrySrcType: maxValidIndex = static_cast<int>(geoSrc.fIndexBuffer->gpuMemorySize() / sizeof(uint16_t)); break; } if (maxIndex > maxValidIndex) { SkFAIL("Index reads outside valid index range."); } } SkASSERT(pipelineBuilder.getRenderTarget()); if (gp) { int numTextures = gp->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = gp->texture(t); SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget()); } } for (int s = 0; s < pipelineBuilder.numColorFragmentStages(); ++s) { const GrProcessor* effect = pipelineBuilder.getColorFragmentStage(s).processor(); int numTextures = effect->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = effect->texture(t); SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget()); } } for (int s = 0; s < pipelineBuilder.numCoverageFragmentStages(); ++s) { const GrProcessor* effect = pipelineBuilder.getCoverageFragmentStage(s).processor(); int numTextures = effect->numTextures(); for (int t = 0; t < numTextures; ++t) { GrTexture* texture = effect->texture(t); SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget()); } } #endif if (NULL == pipelineBuilder.getRenderTarget()) { return false; } return true; }