//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(InitialState initialState, const ElementList& elements, const SkIRect& clipSpaceIBounds, const SkIPoint& clipSpaceToStencilOffset) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrDrawState* drawState = fGpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } int32_t genID = elements.tail()->getGenID(); if (stencilBuffer->mustRenderClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset)) { stencilBuffer->setLastClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset); GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit); drawState = fGpu->drawState(); drawState->setRenderTarget(rt); // We set the current clip to the bounds so that our recursive draws are scissored to them. SkIRect stencilSpaceIBounds(clipSpaceIBounds); stencilSpaceIBounds.offset(clipSpaceToStencilOffset); GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds); drawState->enableState(GrDrawState::kClip_StateBit); // Set the matrix so that rendered clip elements are transformed from clip to stencil space. SkVector translate = { SkIntToScalar(clipSpaceToStencilOffset.fX), SkIntToScalar(clipSpaceToStencilOffset.fY) }; drawState->viewMatrix()->setTranslate(translate); #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState); // walk through each clip element and perform its set op // with the existing clip. for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) { const Element* element = iter.get(); bool fillInverted = false; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); // if the target is MSAA then we want MSAA enabled when the clip is soft if (rt->isMultisampled()) { drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA()); } // This will be used to determine whether the clip shape can be rendered into the // stencil with arbitrary stencil settings. GrPathRenderer::StencilSupport stencilSupport; SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); SkRegion::Op op = element->getOp(); GrPathRenderer* pr = NULL; SkTCopyOnFirstWrite<SkPath> clipPath; if (Element::kRect_Type == element->getType()) { stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; fillInverted = false; } else { GrAssert(Element::kPath_Type == element->getType()); clipPath.init(element->getPath()); fillInverted = clipPath->isInverseFillType(); if (fillInverted) { clipPath.writable()->toggleInverseFillType(); } pr = this->getContext()->getPathRenderer(*clipPath, stroke, fGpu, false, GrPathRendererChain::kStencilOnly_DrawType, &stencilSupport); if (NULL == 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); SET_RANDOM_COLOR if (Element::kRect_Type == element->getType()) { *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(element->getRect(), NULL); } else { GrAssert(Element::kPath_Type == element->getType()); if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, stroke, fGpu, false); } else { pr->stencilPath(*clipPath, stroke, fGpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (Element::kRect_Type == element->getType()) { SET_RANDOM_COLOR fGpu->drawSimpleRect(element->getRect(), NULL); } else { GrAssert(Element::kPath_Type == element->getType()); SET_RANDOM_COLOR pr->drawPath(*clipPath, stroke, fGpu, false); } } else { SET_RANDOM_COLOR // The view matrix is setup to do clip space -> stencil space translation, so // draw rect in clip space. fGpu->drawSimpleRect(SkRect::MakeFromIRect(clipSpaceIBounds), NULL); } } }
bool GrClipMaskManager::installClipEffects(const ElementList& elements, GrDrawState::AutoRestoreEffects* are, const SkVector& clipToRTOffset, const SkRect* drawBounds) { GrDrawState* drawState = fGpu->drawState(); SkRect boundsInClipSpace; if (NULL != drawBounds) { boundsInClipSpace = *drawBounds; boundsInClipSpace.offset(-clipToRTOffset.fX, -clipToRTOffset.fY); } are->set(drawState); GrRenderTarget* rt = drawState->getRenderTarget(); ElementList::Iter iter(elements); bool setARE = false; bool failed = false; while (NULL != iter.get()) { SkRegion::Op op = iter.get()->getOp(); bool invert; bool skip = false; switch (op) { case SkRegion::kReplace_Op: SkASSERT(iter.get() == elements.head()); // Fallthrough, handled same as intersect. case SkRegion::kIntersect_Op: invert = false; if (NULL != drawBounds && iter.get()->contains(boundsInClipSpace)) { skip = true; } break; case SkRegion::kDifference_Op: invert = true; // We don't currently have a cheap test for whether a rect is fully outside an // element's primitive, so don't attempt to set skip. break; default: failed = true; break; } if (failed) { break; } if (!skip) { GrEffectEdgeType edgeType; if (GR_AA_CLIP && iter.get()->isAA()) { if (rt->isMultisampled()) { // Coverage based AA clips don't place nicely with MSAA. failed = true; break; } edgeType = invert ? kInverseFillAA_GrEffectEdgeType : kFillAA_GrEffectEdgeType; } else { edgeType = invert ? kInverseFillBW_GrEffectEdgeType : kFillBW_GrEffectEdgeType; } SkAutoTUnref<GrEffectRef> effect; switch (iter.get()->getType()) { case SkClipStack::Element::kPath_Type: effect.reset(GrConvexPolyEffect::Create(edgeType, iter.get()->getPath(), &clipToRTOffset)); break; case SkClipStack::Element::kRRect_Type: { SkRRect rrect = iter.get()->getRRect(); rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY); effect.reset(GrRRectEffect::Create(edgeType, rrect)); break; } case SkClipStack::Element::kRect_Type: { SkRect rect = iter.get()->getRect(); rect.offset(clipToRTOffset.fX, clipToRTOffset.fY); effect.reset(GrConvexPolyEffect::Create(edgeType, rect)); break; } default: break; } if (effect) { if (!setARE) { are->set(fGpu->drawState()); setARE = true; } fGpu->drawState()->addCoverageEffect(effect); } else { failed = true; break; } } iter.next(); } if (failed) { are->set(NULL); } return !failed; }
//////////////////////////////////////////////////////////////////////////////// // Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device // (as opposed to canvas) coordinates bool GrClipMaskManager::createStencilClipMask(const GrClipData& clipDataIn, const GrIRect& devClipBounds) { GrAssert(kNone_ClipMaskType == fCurrClipMaskType); GrDrawState* drawState = fGpu->drawState(); GrAssert(drawState->isClipState()); GrRenderTarget* rt = drawState->getRenderTarget(); GrAssert(NULL != rt); // TODO: dynamically attach a SB when needed. GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); if (NULL == stencilBuffer) { return false; } if (stencilBuffer->mustRenderClip(clipDataIn, rt->width(), rt->height())) { stencilBuffer->setLastClip(clipDataIn, rt->width(), rt->height()); // we set the current clip to the bounds so that our recursive // draws are scissored to them. We use the copy of the complex clip // we just stashed on the SB to render from. We set it back after // we finish drawing it into the stencil. const GrClipData* oldClipData = fGpu->getClip(); // The origin of 'newClipData' is (0, 0) so it is okay to place // a device-coordinate bound in 'newClipStack' SkClipStack newClipStack(devClipBounds); GrClipData newClipData; newClipData.fClipStack = &newClipStack; fGpu->setClip(&newClipData); GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit); drawState = fGpu->drawState(); drawState->setRenderTarget(rt); GrDrawTarget::AutoGeometryPush agp(fGpu); if (0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) { // Add the saveLayer's offset to the view matrix rather than // offset each individual draw drawState->viewMatrix()->setTranslate( SkIntToScalar(-clipDataIn.fOrigin.fX), SkIntToScalar(-clipDataIn.fOrigin.fY)); } #if !VISUALIZE_COMPLEX_CLIP drawState->enableState(GrDrawState::kNoColorWrites_StateBit); #endif int clipBit = stencilBuffer->bits(); SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); clipBit = (1 << (clipBit-1)); GrIRect devRTRect = GrIRect::MakeWH(rt->width(), rt->height()); bool clearToInside; SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning SkClipStack::Iter iter(*oldClipData->fClipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter, devRTRect, &clearToInside, &firstOp, clipDataIn); fGpu->clearStencilClip(devClipBounds, clearToInside); bool first = true; // walk through each clip element and perform its set op // with the existing clip. for ( ; NULL != clip; clip = iter.nextCombined()) { GrPathFill fill; bool fillInverted = false; // enabled at bottom of loop drawState->disableState(GrGpu::kModifyStencilClip_StateBit); // if the target is MSAA then we want MSAA enabled when the clip is soft if (rt->isMultisampled()) { drawState->setState(GrDrawState::kHWAntialias_StateBit, clip->fDoAA); } // Can the clip element be drawn directly to the stencil buffer // with a non-inverted fill rule without extra passes to // resolve in/out status? bool canRenderDirectToStencil = false; SkRegion::Op op = clip->fOp; if (first) { first = false; op = firstOp; } GrPathRenderer* pr = NULL; const SkPath* clipPath = NULL; if (NULL != clip->fRect) { canRenderDirectToStencil = true; fill = kEvenOdd_GrPathFill; fillInverted = false; // there is no point in intersecting a screen filling // rectangle. if (SkRegion::kIntersect_Op == op && contains(*clip->fRect, devRTRect, oldClipData->fOrigin)) { continue; } } else { GrAssert(NULL != clip->fPath); fill = get_path_fill(*clip->fPath); fillInverted = GrIsFillInverted(fill); fill = GrNonInvertedFill(fill); clipPath = clip->fPath; pr = this->getContext()->getPathRenderer(*clipPath, fill, fGpu, false, true); if (NULL == pr) { fGpu->setClip(oldClipData); return false; } canRenderDirectToStencil = !pr->requiresStencilPass(*clipPath, fill, fGpu); } int passes; GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; 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); SET_RANDOM_COLOR if (NULL != clip->fRect) { *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(*clip->fRect, NULL); } else { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; pr->drawPath(*clipPath, fill, fGpu, false); } else { pr->drawPathToStencil(*clipPath, fill, fGpu); } } } // now we modify the clip bit by rendering either the clip // element directly or a bounding rect of the entire clip. drawState->enableState(GrGpu::kModifyStencilClip_StateBit); for (int p = 0; p < passes; ++p) { *drawState->stencil() = stencilSettings[p]; if (canDrawDirectToClip) { if (NULL != clip->fRect) { SET_RANDOM_COLOR fGpu->drawSimpleRect(*clip->fRect, NULL); } else { SET_RANDOM_COLOR pr->drawPath(*clipPath, fill, fGpu, false); } } else { SET_RANDOM_COLOR // 'devClipBounds' is already in device coordinates so the // translation in the view matrix is inappropriate. // Convert it to canvas space so the drawn rect will // be in the correct location GrRect canvClipBounds; canvClipBounds.set(devClipBounds); device_to_canvas(&canvClipBounds, clipDataIn.fOrigin); fGpu->drawSimpleRect(canvClipBounds, NULL); } } }
bool GrClipMaskManager::installClipEffects(GrPipelineBuilder* pipelineBuilder, GrPipelineBuilder::AutoRestoreFragmentProcessors* arfp, const GrReducedClip::ElementList& elements, const SkVector& clipToRTOffset, const SkRect* drawBounds) { SkRect boundsInClipSpace; if (drawBounds) { boundsInClipSpace = *drawBounds; boundsInClipSpace.offset(-clipToRTOffset.fX, -clipToRTOffset.fY); } arfp->set(pipelineBuilder); GrRenderTarget* rt = pipelineBuilder->getRenderTarget(); GrReducedClip::ElementList::Iter iter(elements); bool failed = false; while (iter.get()) { SkRegion::Op op = iter.get()->getOp(); bool invert; bool skip = false; switch (op) { case SkRegion::kReplace_Op: SkASSERT(iter.get() == elements.head()); // Fallthrough, handled same as intersect. case SkRegion::kIntersect_Op: invert = false; if (drawBounds && iter.get()->contains(boundsInClipSpace)) { skip = true; } break; case SkRegion::kDifference_Op: invert = true; // We don't currently have a cheap test for whether a rect is fully outside an // element's primitive, so don't attempt to set skip. break; default: failed = true; break; } if (failed) { break; } if (!skip) { GrPrimitiveEdgeType edgeType; if (iter.get()->isAA()) { if (rt->isMultisampled()) { // Coverage based AA clips don't place nicely with MSAA. failed = true; break; } edgeType = invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProcessorEdgeType; } else { edgeType = invert ? kInverseFillBW_GrProcessorEdgeType : kFillBW_GrProcessorEdgeType; } SkAutoTUnref<GrFragmentProcessor> fp; switch (iter.get()->getType()) { case SkClipStack::Element::kPath_Type: fp.reset(GrConvexPolyEffect::Create(edgeType, iter.get()->getPath(), &clipToRTOffset)); break; case SkClipStack::Element::kRRect_Type: { SkRRect rrect = iter.get()->getRRect(); rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY); fp.reset(GrRRectEffect::Create(edgeType, rrect)); break; } case SkClipStack::Element::kRect_Type: { SkRect rect = iter.get()->getRect(); rect.offset(clipToRTOffset.fX, clipToRTOffset.fY); fp.reset(GrConvexPolyEffect::Create(edgeType, rect)); break; } default: break; } if (fp) { pipelineBuilder->addCoverageProcessor(fp); } else { failed = true; break; } } iter.next(); } if (failed) { arfp->set(NULL); } return !failed; }