1*c8dee2aaSAndroid Build Coastguard Worker /* 2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2010 Google Inc. 3*c8dee2aaSAndroid Build Coastguard Worker * 4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be 5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file. 6*c8dee2aaSAndroid Build Coastguard Worker */ 7*c8dee2aaSAndroid Build Coastguard Worker 8*c8dee2aaSAndroid Build Coastguard Worker #ifndef GrClip_DEFINED 9*c8dee2aaSAndroid Build Coastguard Worker #define GrClip_DEFINED 10*c8dee2aaSAndroid Build Coastguard Worker 11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h" 12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h" 13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h" 14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/gpu/ganesh/GrTypesPriv.h" 15*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrAppliedClip.h" 16*c8dee2aaSAndroid Build Coastguard Worker 17*c8dee2aaSAndroid Build Coastguard Worker class GrDrawOp; 18*c8dee2aaSAndroid Build Coastguard Worker class GrRecordingContext; 19*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu { 20*c8dee2aaSAndroid Build Coastguard Worker namespace ganesh { 21*c8dee2aaSAndroid Build Coastguard Worker class SurfaceDrawContext; 22*c8dee2aaSAndroid Build Coastguard Worker } 23*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu 24*c8dee2aaSAndroid Build Coastguard Worker 25*c8dee2aaSAndroid Build Coastguard Worker /** 26*c8dee2aaSAndroid Build Coastguard Worker * GrClip is an abstract base class for applying a clip. It constructs a clip mask if necessary, and 27*c8dee2aaSAndroid Build Coastguard Worker * fills out a GrAppliedClip instructing the caller on how to set up the draw state. 28*c8dee2aaSAndroid Build Coastguard Worker */ 29*c8dee2aaSAndroid Build Coastguard Worker class GrClip { 30*c8dee2aaSAndroid Build Coastguard Worker public: 31*c8dee2aaSAndroid Build Coastguard Worker enum class Effect { 32*c8dee2aaSAndroid Build Coastguard Worker // The clip conservatively modifies the draw's coverage but doesn't eliminate the draw 33*c8dee2aaSAndroid Build Coastguard Worker kClipped, 34*c8dee2aaSAndroid Build Coastguard Worker // The clip definitely does not modify the draw's coverage and the draw can be performed 35*c8dee2aaSAndroid Build Coastguard Worker // without clipping (beyond the automatic device bounds clip). 36*c8dee2aaSAndroid Build Coastguard Worker kUnclipped, 37*c8dee2aaSAndroid Build Coastguard Worker // The clip definitely eliminates all of the draw's coverage and the draw can be skipped 38*c8dee2aaSAndroid Build Coastguard Worker kClippedOut 39*c8dee2aaSAndroid Build Coastguard Worker }; 40*c8dee2aaSAndroid Build Coastguard Worker 41*c8dee2aaSAndroid Build Coastguard Worker struct PreClipResult { 42*c8dee2aaSAndroid Build Coastguard Worker Effect fEffect; 43*c8dee2aaSAndroid Build Coastguard Worker SkRRect fRRect; // Ignore if 'isRRect' is false 44*c8dee2aaSAndroid Build Coastguard Worker GrAA fAA; // Ignore if 'isRRect' is false 45*c8dee2aaSAndroid Build Coastguard Worker bool fIsRRect; 46*c8dee2aaSAndroid Build Coastguard Worker PreClipResultPreClipResult47*c8dee2aaSAndroid Build Coastguard Worker PreClipResult(Effect effect) : fEffect(effect), fIsRRect(false) {} PreClipResultPreClipResult48*c8dee2aaSAndroid Build Coastguard Worker PreClipResult(SkRect rect, GrAA aa) : PreClipResult(SkRRect::MakeRect(rect), aa) {} PreClipResultPreClipResult49*c8dee2aaSAndroid Build Coastguard Worker PreClipResult(SkRRect rrect, GrAA aa) 50*c8dee2aaSAndroid Build Coastguard Worker : fEffect(Effect::kClipped) 51*c8dee2aaSAndroid Build Coastguard Worker , fRRect(rrect) 52*c8dee2aaSAndroid Build Coastguard Worker , fAA(aa) 53*c8dee2aaSAndroid Build Coastguard Worker , fIsRRect(true) {} 54*c8dee2aaSAndroid Build Coastguard Worker }; 55*c8dee2aaSAndroid Build Coastguard Worker ~GrClip()56*c8dee2aaSAndroid Build Coastguard Worker virtual ~GrClip() {} 57*c8dee2aaSAndroid Build Coastguard Worker 58*c8dee2aaSAndroid Build Coastguard Worker /** 59*c8dee2aaSAndroid Build Coastguard Worker * Compute a conservative pixel bounds restricted to the given render target dimensions. 60*c8dee2aaSAndroid Build Coastguard Worker * The returned bounds represent the limits of pixels that can be drawn; anything outside of the 61*c8dee2aaSAndroid Build Coastguard Worker * bounds will be entirely clipped out. 62*c8dee2aaSAndroid Build Coastguard Worker */ 63*c8dee2aaSAndroid Build Coastguard Worker virtual SkIRect getConservativeBounds() const = 0; 64*c8dee2aaSAndroid Build Coastguard Worker 65*c8dee2aaSAndroid Build Coastguard Worker /** 66*c8dee2aaSAndroid Build Coastguard Worker * This computes a GrAppliedClip from the clip which in turn can be used to build a GrPipeline. 67*c8dee2aaSAndroid Build Coastguard Worker * To determine the appropriate clipping implementation the GrClip subclass must know whether 68*c8dee2aaSAndroid Build Coastguard Worker * the draw will enable HW AA or uses the stencil buffer. On input 'bounds' is a conservative 69*c8dee2aaSAndroid Build Coastguard Worker * bounds of the draw that is to be clipped. If kClipped or kUnclipped is returned, the 'bounds' 70*c8dee2aaSAndroid Build Coastguard Worker * will have been updated to be contained within the clip bounds (or the device's, for wide-open 71*c8dee2aaSAndroid Build Coastguard Worker * clips). If kNoDraw is returned, 'bounds' and the applied clip are in an undetermined state 72*c8dee2aaSAndroid Build Coastguard Worker * and should be ignored (and the draw should be skipped). 73*c8dee2aaSAndroid Build Coastguard Worker */ 74*c8dee2aaSAndroid Build Coastguard Worker virtual Effect apply(GrRecordingContext*, 75*c8dee2aaSAndroid Build Coastguard Worker skgpu::ganesh::SurfaceDrawContext*, 76*c8dee2aaSAndroid Build Coastguard Worker GrDrawOp*, 77*c8dee2aaSAndroid Build Coastguard Worker GrAAType, 78*c8dee2aaSAndroid Build Coastguard Worker GrAppliedClip*, 79*c8dee2aaSAndroid Build Coastguard Worker SkRect* bounds) const = 0; 80*c8dee2aaSAndroid Build Coastguard Worker 81*c8dee2aaSAndroid Build Coastguard Worker /** 82*c8dee2aaSAndroid Build Coastguard Worker * Perform preliminary, conservative analysis on the draw bounds as if it were provided to 83*c8dee2aaSAndroid Build Coastguard Worker * apply(). The results of this are returned the PreClipResults struct, where 'result.fEffect' 84*c8dee2aaSAndroid Build Coastguard Worker * corresponds to what 'apply' would return. If this value is kUnclipped or kNoDraw, then it 85*c8dee2aaSAndroid Build Coastguard Worker * can be assumed that apply() would also always result in the same Effect. 86*c8dee2aaSAndroid Build Coastguard Worker * 87*c8dee2aaSAndroid Build Coastguard Worker * If kClipped is returned, apply() may further refine the effect to kUnclipped or kNoDraw, 88*c8dee2aaSAndroid Build Coastguard Worker * with one exception. When 'result.fIsRRect' is true, preApply() reports the single round rect 89*c8dee2aaSAndroid Build Coastguard Worker * and anti-aliased state that would act as an intersection on the draw geometry. If no further 90*c8dee2aaSAndroid Build Coastguard Worker * action is taken to modify the draw, apply() will represent this round rect in the applied 91*c8dee2aaSAndroid Build Coastguard Worker * clip. 92*c8dee2aaSAndroid Build Coastguard Worker * 93*c8dee2aaSAndroid Build Coastguard Worker * When set, 'result.fRRect' will intersect with the render target bounds but may extend 94*c8dee2aaSAndroid Build Coastguard Worker * beyond it. If the render target bounds are the only clip effect on the draw, this is reported 95*c8dee2aaSAndroid Build Coastguard Worker * as kUnclipped and not as a degenerate rrect that matches the bounds. 96*c8dee2aaSAndroid Build Coastguard Worker */ preApply(const SkRect & drawBounds,GrAA aa)97*c8dee2aaSAndroid Build Coastguard Worker virtual PreClipResult preApply(const SkRect& drawBounds, GrAA aa) const { 98*c8dee2aaSAndroid Build Coastguard Worker SkIRect pixelBounds = GetPixelIBounds(drawBounds, aa); 99*c8dee2aaSAndroid Build Coastguard Worker bool outside = !SkIRect::Intersects(pixelBounds, this->getConservativeBounds()); 100*c8dee2aaSAndroid Build Coastguard Worker return outside ? Effect::kClippedOut : Effect::kClipped; 101*c8dee2aaSAndroid Build Coastguard Worker } 102*c8dee2aaSAndroid Build Coastguard Worker 103*c8dee2aaSAndroid Build Coastguard Worker /** 104*c8dee2aaSAndroid Build Coastguard Worker * This is the maximum distance that a draw may extend beyond a clip's boundary and still count 105*c8dee2aaSAndroid Build Coastguard Worker * count as "on the other side". We leave some slack because floating point rounding error is 106*c8dee2aaSAndroid Build Coastguard Worker * likely to blame. The rationale for 1e-3 is that in the coverage case (and barring unexpected 107*c8dee2aaSAndroid Build Coastguard Worker * rounding), as long as coverage stays within 0.5 * 1/256 of its intended value it shouldn't 108*c8dee2aaSAndroid Build Coastguard Worker * have any effect on the final pixel values. 109*c8dee2aaSAndroid Build Coastguard Worker */ 110*c8dee2aaSAndroid Build Coastguard Worker constexpr static SkScalar kBoundsTolerance = 1e-3f; 111*c8dee2aaSAndroid Build Coastguard Worker 112*c8dee2aaSAndroid Build Coastguard Worker /** 113*c8dee2aaSAndroid Build Coastguard Worker * This is the slack around a half-pixel vertex coordinate where we don't trust the GPU's 114*c8dee2aaSAndroid Build Coastguard Worker * rasterizer to round consistently. The rounding method is not defined in GPU specs, and 115*c8dee2aaSAndroid Build Coastguard Worker * rasterizer precision frequently introduces errors where a fraction < 1/2 still rounds up. 116*c8dee2aaSAndroid Build Coastguard Worker * 117*c8dee2aaSAndroid Build Coastguard Worker * For non-AA bounds edges, an edge value between 0.45 and 0.55 will round in or round out 118*c8dee2aaSAndroid Build Coastguard Worker * depending on what side its on. Outside of this range, the non-AA edge will snap using round() 119*c8dee2aaSAndroid Build Coastguard Worker */ 120*c8dee2aaSAndroid Build Coastguard Worker constexpr static SkScalar kHalfPixelRoundingTolerance = 5e-2f; 121*c8dee2aaSAndroid Build Coastguard Worker 122*c8dee2aaSAndroid Build Coastguard Worker /** 123*c8dee2aaSAndroid Build Coastguard Worker * Returns true if the given draw bounds count as entirely inside the clip. 124*c8dee2aaSAndroid Build Coastguard Worker 125*c8dee2aaSAndroid Build Coastguard Worker * @param innerClipBounds device-space rect fully contained by the clip 126*c8dee2aaSAndroid Build Coastguard Worker * @param drawBounds device-space bounds of the query region. 127*c8dee2aaSAndroid Build Coastguard Worker */ IsInsideClip(const SkIRect & innerClipBounds,const SkRect & drawBounds,GrAA aa)128*c8dee2aaSAndroid Build Coastguard Worker static bool IsInsideClip(const SkIRect& innerClipBounds, const SkRect& drawBounds, GrAA aa) { 129*c8dee2aaSAndroid Build Coastguard Worker return innerClipBounds.contains(GetPixelIBounds(drawBounds, aa)); 130*c8dee2aaSAndroid Build Coastguard Worker } 131*c8dee2aaSAndroid Build Coastguard Worker 132*c8dee2aaSAndroid Build Coastguard Worker /** 133*c8dee2aaSAndroid Build Coastguard Worker * Returns true if the given draw bounds count as entirely outside the clip. 134*c8dee2aaSAndroid Build Coastguard Worker 135*c8dee2aaSAndroid Build Coastguard Worker * @param outerClipBounds device-space rect that contains the clip 136*c8dee2aaSAndroid Build Coastguard Worker * @param drawBounds device-space bounds of the query region. 137*c8dee2aaSAndroid Build Coastguard Worker * @param aa whether or not the draw will use anti-aliasing 138*c8dee2aaSAndroid Build Coastguard Worker */ IsOutsideClip(const SkIRect & outerClipBounds,const SkRect & drawBounds,GrAA aa)139*c8dee2aaSAndroid Build Coastguard Worker static bool IsOutsideClip(const SkIRect& outerClipBounds, const SkRect& drawBounds, GrAA aa) { 140*c8dee2aaSAndroid Build Coastguard Worker return !SkIRect::Intersects(outerClipBounds, GetPixelIBounds(drawBounds, aa)); 141*c8dee2aaSAndroid Build Coastguard Worker } 142*c8dee2aaSAndroid Build Coastguard Worker 143*c8dee2aaSAndroid Build Coastguard Worker // Modifies the behavior of GetPixelIBounds 144*c8dee2aaSAndroid Build Coastguard Worker enum class BoundsType { 145*c8dee2aaSAndroid Build Coastguard Worker /** 146*c8dee2aaSAndroid Build Coastguard Worker * Returns the tightest integer pixel bounding box such that the rasterization of a shape 147*c8dee2aaSAndroid Build Coastguard Worker * contained in the analytic 'bounds', using the 'aa' method, will only have non-zero 148*c8dee2aaSAndroid Build Coastguard Worker * coverage for pixels inside the returned bounds. Pixels outside the bounds will either 149*c8dee2aaSAndroid Build Coastguard Worker * not be touched, or will have 0 coverage that creates no visual change. 150*c8dee2aaSAndroid Build Coastguard Worker */ 151*c8dee2aaSAndroid Build Coastguard Worker kExterior, 152*c8dee2aaSAndroid Build Coastguard Worker /** 153*c8dee2aaSAndroid Build Coastguard Worker * Returns the largest integer pixel bounding box such that were 'bounds' to be rendered as 154*c8dee2aaSAndroid Build Coastguard Worker * a solid fill using 'aa', every pixel in the returned bounds will have full coverage. 155*c8dee2aaSAndroid Build Coastguard Worker * 156*c8dee2aaSAndroid Build Coastguard Worker * This effectively determines the pixels that are definitely covered by a draw or clip. It 157*c8dee2aaSAndroid Build Coastguard Worker * effectively performs the opposite operations as GetOuterPixelBounds. It rounds in instead 158*c8dee2aaSAndroid Build Coastguard Worker * of out for coverage AA and non-AA near pixel centers. 159*c8dee2aaSAndroid Build Coastguard Worker */ 160*c8dee2aaSAndroid Build Coastguard Worker kInterior 161*c8dee2aaSAndroid Build Coastguard Worker }; 162*c8dee2aaSAndroid Build Coastguard Worker 163*c8dee2aaSAndroid Build Coastguard Worker /** 164*c8dee2aaSAndroid Build Coastguard Worker * Convert the analytic bounds of a shape into an integer pixel bounds, where the given aa type 165*c8dee2aaSAndroid Build Coastguard Worker * is used when the shape is rendered. The bounds mode can be used to query exterior or interior 166*c8dee2aaSAndroid Build Coastguard Worker * pixel boundaries. Interior bounds only make sense when its know that the analytic bounds 167*c8dee2aaSAndroid Build Coastguard Worker * are filled completely. 168*c8dee2aaSAndroid Build Coastguard Worker * 169*c8dee2aaSAndroid Build Coastguard Worker * NOTE: When using kExterior_Bounds, some coverage-AA rendering methods may still touch a pixel 170*c8dee2aaSAndroid Build Coastguard Worker * center outside of these bounds but will evaluate to 0 coverage. This is visually acceptable, 171*c8dee2aaSAndroid Build Coastguard Worker * but an additional outset of 1px should be used for dst proxy access. 172*c8dee2aaSAndroid Build Coastguard Worker */ 173*c8dee2aaSAndroid Build Coastguard Worker static SkIRect GetPixelIBounds(const SkRect& bounds, GrAA aa, 174*c8dee2aaSAndroid Build Coastguard Worker BoundsType mode = BoundsType::kExterior) { 175*c8dee2aaSAndroid Build Coastguard Worker auto roundLow = [aa](float v) { 176*c8dee2aaSAndroid Build Coastguard Worker v += kBoundsTolerance; 177*c8dee2aaSAndroid Build Coastguard Worker return aa == GrAA::kNo ? SkScalarRoundToInt(v - kHalfPixelRoundingTolerance) 178*c8dee2aaSAndroid Build Coastguard Worker : SkScalarFloorToInt(v); 179*c8dee2aaSAndroid Build Coastguard Worker }; 180*c8dee2aaSAndroid Build Coastguard Worker auto roundHigh = [aa](float v) { 181*c8dee2aaSAndroid Build Coastguard Worker v -= kBoundsTolerance; 182*c8dee2aaSAndroid Build Coastguard Worker return aa == GrAA::kNo ? SkScalarRoundToInt(v + kHalfPixelRoundingTolerance) 183*c8dee2aaSAndroid Build Coastguard Worker : SkScalarCeilToInt(v); 184*c8dee2aaSAndroid Build Coastguard Worker }; 185*c8dee2aaSAndroid Build Coastguard Worker 186*c8dee2aaSAndroid Build Coastguard Worker if (bounds.isEmpty()) { 187*c8dee2aaSAndroid Build Coastguard Worker return SkIRect::MakeEmpty(); 188*c8dee2aaSAndroid Build Coastguard Worker } 189*c8dee2aaSAndroid Build Coastguard Worker 190*c8dee2aaSAndroid Build Coastguard Worker if (mode == BoundsType::kExterior) { 191*c8dee2aaSAndroid Build Coastguard Worker return SkIRect::MakeLTRB(roundLow(bounds.fLeft), roundLow(bounds.fTop), 192*c8dee2aaSAndroid Build Coastguard Worker roundHigh(bounds.fRight), roundHigh(bounds.fBottom)); 193*c8dee2aaSAndroid Build Coastguard Worker } else { 194*c8dee2aaSAndroid Build Coastguard Worker return SkIRect::MakeLTRB(roundHigh(bounds.fLeft), roundHigh(bounds.fTop), 195*c8dee2aaSAndroid Build Coastguard Worker roundLow(bounds.fRight), roundLow(bounds.fBottom)); 196*c8dee2aaSAndroid Build Coastguard Worker } 197*c8dee2aaSAndroid Build Coastguard Worker } 198*c8dee2aaSAndroid Build Coastguard Worker 199*c8dee2aaSAndroid Build Coastguard Worker /** 200*c8dee2aaSAndroid Build Coastguard Worker * Returns true if the given rect counts as aligned with pixel boundaries. 201*c8dee2aaSAndroid Build Coastguard Worker */ IsPixelAligned(const SkRect & rect)202*c8dee2aaSAndroid Build Coastguard Worker static bool IsPixelAligned(const SkRect& rect) { 203*c8dee2aaSAndroid Build Coastguard Worker return SkScalarAbs(SkScalarRoundToScalar(rect.fLeft) - rect.fLeft) <= kBoundsTolerance && 204*c8dee2aaSAndroid Build Coastguard Worker SkScalarAbs(SkScalarRoundToScalar(rect.fTop) - rect.fTop) <= kBoundsTolerance && 205*c8dee2aaSAndroid Build Coastguard Worker SkScalarAbs(SkScalarRoundToScalar(rect.fRight) - rect.fRight) <= kBoundsTolerance && 206*c8dee2aaSAndroid Build Coastguard Worker SkScalarAbs(SkScalarRoundToScalar(rect.fBottom) - rect.fBottom) <= kBoundsTolerance; 207*c8dee2aaSAndroid Build Coastguard Worker } 208*c8dee2aaSAndroid Build Coastguard Worker }; 209*c8dee2aaSAndroid Build Coastguard Worker 210*c8dee2aaSAndroid Build Coastguard Worker 211*c8dee2aaSAndroid Build Coastguard Worker /** 212*c8dee2aaSAndroid Build Coastguard Worker * GrHardClip never uses coverage FPs. It can only enforce the clip using the already-existing 213*c8dee2aaSAndroid Build Coastguard Worker * stencil buffer contents and/or fixed-function state like scissor. Always aliased if MSAA is off. 214*c8dee2aaSAndroid Build Coastguard Worker */ 215*c8dee2aaSAndroid Build Coastguard Worker class GrHardClip : public GrClip { 216*c8dee2aaSAndroid Build Coastguard Worker public: 217*c8dee2aaSAndroid Build Coastguard Worker /** 218*c8dee2aaSAndroid Build Coastguard Worker * Sets the appropriate hardware state modifications on GrAppliedHardClip that will implement 219*c8dee2aaSAndroid Build Coastguard Worker * the clip. On input 'bounds' is a conservative bounds of the draw that is to be clipped. After 220*c8dee2aaSAndroid Build Coastguard Worker * return 'bounds' has been intersected with a conservative bounds of the clip. 221*c8dee2aaSAndroid Build Coastguard Worker */ 222*c8dee2aaSAndroid Build Coastguard Worker virtual Effect apply(GrAppliedHardClip* out, SkIRect* bounds) const = 0; 223*c8dee2aaSAndroid Build Coastguard Worker 224*c8dee2aaSAndroid Build Coastguard Worker private: apply(GrRecordingContext *,skgpu::ganesh::SurfaceDrawContext *,GrDrawOp *,GrAAType aa,GrAppliedClip * out,SkRect * bounds)225*c8dee2aaSAndroid Build Coastguard Worker Effect apply(GrRecordingContext*, 226*c8dee2aaSAndroid Build Coastguard Worker skgpu::ganesh::SurfaceDrawContext*, 227*c8dee2aaSAndroid Build Coastguard Worker GrDrawOp*, 228*c8dee2aaSAndroid Build Coastguard Worker GrAAType aa, 229*c8dee2aaSAndroid Build Coastguard Worker GrAppliedClip* out, 230*c8dee2aaSAndroid Build Coastguard Worker SkRect* bounds) const final { 231*c8dee2aaSAndroid Build Coastguard Worker SkIRect pixelBounds = GetPixelIBounds(*bounds, GrAA(aa != GrAAType::kNone)); 232*c8dee2aaSAndroid Build Coastguard Worker Effect effect = this->apply(&out->hardClip(), &pixelBounds); 233*c8dee2aaSAndroid Build Coastguard Worker bounds->intersect(SkRect::Make(pixelBounds)); 234*c8dee2aaSAndroid Build Coastguard Worker return effect; 235*c8dee2aaSAndroid Build Coastguard Worker } 236*c8dee2aaSAndroid Build Coastguard Worker }; 237*c8dee2aaSAndroid Build Coastguard Worker 238*c8dee2aaSAndroid Build Coastguard Worker #endif 239