/* * 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(const 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. SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { const Element* element = iter.get(); // rects can always be drawn directly w/o using the software path // so only paths need to be checked if (Element::kPath_Type == element->getType() && path_needs_SW_renderer(this->getContext(), fGpu, element->getPath(), stroke, element->isAA())) { return true; } } return false; }
//////////////////////////////////////////////////////////////////////////////// // 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); } } }