Exemple #1
0
////////////////////////////////////////////////////////////////////////////////
// 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;
}