//////////////////////////////////////////////////////////////////////////////// // 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, const SkRect* devBounds, GrAppliedClip* out) { if (kRespectClip_StencilClipMode == fClipMode) { fClipMode = kIgnoreClip_StencilClipMode; } GrReducedClip::ElementList elements; 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()); GrClip devBoundsClip; bool doDevBoundsClip = fDebugClipBatchToBounds && devBounds; if (doDevBoundsClip) { add_rect_to_clip(pipelineBuilder.clip(), *devBounds, &devBoundsClip); } const GrClip& clip = doDevBoundsClip ? devBoundsClip : 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)) { out->fScissorState.set(scissor); this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; } return false; } case GrClip::kClipStack_ClipType: { 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) { 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 samples we want to do per-sample clipping, not compute a // fractional pixel coverage. bool disallowAnalyticAA = rt->isUnifiedMultisampled() || pipelineBuilder.hasMixedSamples(); const GrFragmentProcessor* clipFP = nullptr; if (elements.isEmpty() || (requiresAA && this->getAnalyticClipProcessor(elements, disallowAnalyticAA, clipToRTOffset, devBounds, &clipFP))) { SkIRect scissorSpaceIBounds(clipSpaceIBounds); scissorSpaceIBounds.offset(-clip.origin()); if (nullptr == devBounds || !SkRect::Make(scissorSpaceIBounds).contains(*devBounds)) { out->fScissorState.set(scissorSpaceIBounds); } this->setPipelineBuilderStencil(pipelineBuilder, ars); out->fClipCoverageFP.reset(clipFP); return true; } } // If the stencil buffer is multisampled we can use it to do everything. if (!rt->isStencilBufferMultisampled() && 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 (UseSWOnlyPath(this->getContext(), pipelineBuilder, rt, clipToMaskOffset, elements)) { // The clip geometry is complex enough that it will be more efficient to create it // entirely in software result.reset(CreateSoftwareClipMask(this->getContext(), genID, initialState, elements, clipToMaskOffset, clipSpaceIBounds)); } else { result.reset(CreateAlphaClipMask(this->getContext(), 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->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); out->fScissorState.set(scissorSpaceIBounds); this->setPipelineBuilderStencil(pipelineBuilder, ars); return true; }