GrReducedClip.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. /*
  2. * Copyright 2016 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "src/core/SkClipOpPriv.h"
  8. #include "src/gpu/GrAppliedClip.h"
  9. #include "src/gpu/GrClip.h"
  10. #include "src/gpu/GrColor.h"
  11. #include "src/gpu/GrDrawingManager.h"
  12. #include "src/gpu/GrFixedClip.h"
  13. #include "src/gpu/GrPathRenderer.h"
  14. #include "src/gpu/GrRecordingContextPriv.h"
  15. #include "src/gpu/GrReducedClip.h"
  16. #include "src/gpu/GrRenderTargetContext.h"
  17. #include "src/gpu/GrRenderTargetContextPriv.h"
  18. #include "src/gpu/GrStencilClip.h"
  19. #include "src/gpu/GrStencilSettings.h"
  20. #include "src/gpu/GrStyle.h"
  21. #include "src/gpu/GrUserStencilSettings.h"
  22. #include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
  23. #include "src/gpu/effects/GrConvexPolyEffect.h"
  24. #include "src/gpu/effects/GrRRectEffect.h"
  25. #include "src/gpu/effects/generated/GrAARectEffect.h"
  26. #include "src/gpu/geometry/GrShape.h"
  27. /**
  28. * There are plenty of optimizations that could be added here. Maybe flips could be folded into
  29. * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
  30. * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
  31. * based on later intersect operations, and perhaps remove intersect-rects. We could optionally
  32. * take a rect in case the caller knows a bound on what is to be drawn through this clip.
  33. */
  34. GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
  35. const GrCaps* caps, int maxWindowRectangles, int maxAnalyticFPs,
  36. int maxCCPRClipPaths)
  37. : fCaps(caps)
  38. , fMaxWindowRectangles(maxWindowRectangles)
  39. , fMaxAnalyticFPs(maxAnalyticFPs)
  40. , fMaxCCPRClipPaths(maxCCPRClipPaths) {
  41. SkASSERT(!queryBounds.isEmpty());
  42. SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows);
  43. SkASSERT(fMaxCCPRClipPaths <= fMaxAnalyticFPs);
  44. fHasScissor = false;
  45. fAAClipRectGenID = SK_InvalidGenID;
  46. if (stack.isWideOpen()) {
  47. fInitialState = InitialState::kAllIn;
  48. return;
  49. }
  50. SkClipStack::BoundsType stackBoundsType;
  51. SkRect stackBounds;
  52. bool iior;
  53. stack.getBounds(&stackBounds, &stackBoundsType, &iior);
  54. if (GrClip::IsOutsideClip(stackBounds, queryBounds)) {
  55. bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
  56. fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
  57. return;
  58. }
  59. if (iior) {
  60. // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds.
  61. // This should only be true if aa/non-aa status matches among all elements.
  62. SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
  63. if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
  64. fInitialState = InitialState::kAllIn;
  65. return;
  66. }
  67. SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
  68. if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
  69. // The clip is a non-aa rect. Here we just implement the entire thing using fScissor.
  70. stackBounds.round(&fScissor);
  71. fHasScissor = true;
  72. fInitialState = fScissor.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
  73. return;
  74. }
  75. SkRect tightBounds;
  76. SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
  77. fScissor = GrClip::GetPixelIBounds(tightBounds);
  78. if (fScissor.isEmpty()) {
  79. fInitialState = InitialState::kAllOut;
  80. return;
  81. }
  82. fHasScissor = true;
  83. fAAClipRect = stackBounds;
  84. fAAClipRectGenID = stack.getTopmostGenID();
  85. SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
  86. fInitialState = InitialState::kAllIn;
  87. } else {
  88. SkRect tighterQuery = queryBounds;
  89. if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
  90. // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This
  91. // new clip will be enforced by the scissor.)
  92. SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
  93. }
  94. fScissor = GrClip::GetPixelIBounds(tighterQuery);
  95. if (fScissor.isEmpty()) {
  96. fInitialState = InitialState::kAllOut;
  97. return;
  98. }
  99. fHasScissor = true;
  100. // Now that we have determined the bounds to use and filtered out the trivial cases, call
  101. // the helper that actually walks the stack.
  102. this->walkStack(stack, tighterQuery);
  103. }
  104. if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect?
  105. ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, GrAA::kYes)) {
  106. if (fMaskElements.isEmpty()) {
  107. // Use a replace since it is faster than intersect.
  108. fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
  109. fInitialState = InitialState::kAllOut;
  110. } else {
  111. fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
  112. }
  113. fMaskRequiresAA = true;
  114. fMaskGenID = fAAClipRectGenID;
  115. }
  116. }
  117. void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) {
  118. // walk backwards until we get to:
  119. // a) the beginning
  120. // b) an operation that is known to make the bounds all inside/outside
  121. // c) a replace operation
  122. enum class InitialTriState {
  123. kUnknown = -1,
  124. kAllIn = (int)GrReducedClip::InitialState::kAllIn,
  125. kAllOut = (int)GrReducedClip::InitialState::kAllOut
  126. } initialTriState = InitialTriState::kUnknown;
  127. // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
  128. // TODO: track these per saved clip so that we can consider them on the forward pass.
  129. bool embiggens = false;
  130. bool emsmallens = false;
  131. // We use a slightly relaxed set of query bounds for element containment tests. This is to
  132. // account for floating point rounding error that may have occurred during coord transforms.
  133. SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
  134. GrClip::kBoundsTolerance);
  135. if (relaxedQueryBounds.isEmpty()) {
  136. relaxedQueryBounds = queryBounds;
  137. }
  138. SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
  139. int numAAElements = 0;
  140. while (InitialTriState::kUnknown == initialTriState) {
  141. const Element* element = iter.prev();
  142. if (nullptr == element) {
  143. initialTriState = InitialTriState::kAllIn;
  144. break;
  145. }
  146. if (SkClipStack::kEmptyGenID == element->getGenID()) {
  147. initialTriState = InitialTriState::kAllOut;
  148. break;
  149. }
  150. if (SkClipStack::kWideOpenGenID == element->getGenID()) {
  151. initialTriState = InitialTriState::kAllIn;
  152. break;
  153. }
  154. bool skippable = false;
  155. bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
  156. switch (element->getOp()) {
  157. case kDifference_SkClipOp:
  158. // check if the shape subtracted either contains the entire bounds (and makes
  159. // the clip empty) or is outside the bounds and therefore can be skipped.
  160. if (element->isInverseFilled()) {
  161. if (element->contains(relaxedQueryBounds)) {
  162. skippable = true;
  163. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  164. initialTriState = InitialTriState::kAllOut;
  165. skippable = true;
  166. } else if (!embiggens) {
  167. ClipResult result = this->clipInsideElement(element);
  168. if (ClipResult::kMadeEmpty == result) {
  169. return;
  170. }
  171. skippable = (ClipResult::kClipped == result);
  172. }
  173. } else {
  174. if (element->contains(relaxedQueryBounds)) {
  175. initialTriState = InitialTriState::kAllOut;
  176. skippable = true;
  177. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  178. skippable = true;
  179. } else if (!embiggens) {
  180. ClipResult result = this->clipOutsideElement(element);
  181. if (ClipResult::kMadeEmpty == result) {
  182. return;
  183. }
  184. skippable = (ClipResult::kClipped == result);
  185. }
  186. }
  187. if (!skippable) {
  188. emsmallens = true;
  189. }
  190. break;
  191. case kIntersect_SkClipOp:
  192. // check if the shape intersected contains the entire bounds and therefore can
  193. // be skipped or it is outside the entire bounds and therefore makes the clip
  194. // empty.
  195. if (element->isInverseFilled()) {
  196. if (element->contains(relaxedQueryBounds)) {
  197. initialTriState = InitialTriState::kAllOut;
  198. skippable = true;
  199. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  200. skippable = true;
  201. } else if (!embiggens) {
  202. ClipResult result = this->clipOutsideElement(element);
  203. if (ClipResult::kMadeEmpty == result) {
  204. return;
  205. }
  206. skippable = (ClipResult::kClipped == result);
  207. }
  208. } else {
  209. if (element->contains(relaxedQueryBounds)) {
  210. skippable = true;
  211. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  212. initialTriState = InitialTriState::kAllOut;
  213. skippable = true;
  214. } else if (!embiggens) {
  215. ClipResult result = this->clipInsideElement(element);
  216. if (ClipResult::kMadeEmpty == result) {
  217. return;
  218. }
  219. skippable = (ClipResult::kClipped == result);
  220. }
  221. }
  222. if (!skippable) {
  223. emsmallens = true;
  224. }
  225. break;
  226. case kUnion_SkClipOp:
  227. // If the union-ed shape contains the entire bounds then after this element
  228. // the bounds is entirely inside the clip. If the union-ed shape is outside the
  229. // bounds then this op can be skipped.
  230. if (element->isInverseFilled()) {
  231. if (element->contains(relaxedQueryBounds)) {
  232. skippable = true;
  233. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  234. initialTriState = InitialTriState::kAllIn;
  235. skippable = true;
  236. }
  237. } else {
  238. if (element->contains(relaxedQueryBounds)) {
  239. initialTriState = InitialTriState::kAllIn;
  240. skippable = true;
  241. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  242. skippable = true;
  243. }
  244. }
  245. if (!skippable) {
  246. embiggens = true;
  247. }
  248. break;
  249. case kXOR_SkClipOp:
  250. // If the bounds is entirely inside the shape being xor-ed then the effect is
  251. // to flip the inside/outside state of every point in the bounds. We may be
  252. // able to take advantage of this in the forward pass. If the xor-ed shape
  253. // doesn't intersect the bounds then it can be skipped.
  254. if (element->isInverseFilled()) {
  255. if (element->contains(relaxedQueryBounds)) {
  256. skippable = true;
  257. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  258. isFlip = true;
  259. }
  260. } else {
  261. if (element->contains(relaxedQueryBounds)) {
  262. isFlip = true;
  263. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  264. skippable = true;
  265. }
  266. }
  267. if (!skippable) {
  268. emsmallens = embiggens = true;
  269. }
  270. break;
  271. case kReverseDifference_SkClipOp:
  272. // When the bounds is entirely within the rev-diff shape then this behaves like xor
  273. // and reverses every point inside the bounds. If the shape is completely outside
  274. // the bounds then we know after this element is applied that the bounds will be
  275. // all outside the current clip.B
  276. if (element->isInverseFilled()) {
  277. if (element->contains(relaxedQueryBounds)) {
  278. initialTriState = InitialTriState::kAllOut;
  279. skippable = true;
  280. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  281. isFlip = true;
  282. }
  283. } else {
  284. if (element->contains(relaxedQueryBounds)) {
  285. isFlip = true;
  286. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  287. initialTriState = InitialTriState::kAllOut;
  288. skippable = true;
  289. }
  290. }
  291. if (!skippable) {
  292. emsmallens = embiggens = true;
  293. }
  294. break;
  295. case kReplace_SkClipOp:
  296. // Replace will always terminate our walk. We will either begin the forward walk
  297. // at the replace op or detect here than the shape is either completely inside
  298. // or completely outside the bounds. In this latter case it can be skipped by
  299. // setting the correct value for initialTriState.
  300. if (element->isInverseFilled()) {
  301. if (element->contains(relaxedQueryBounds)) {
  302. initialTriState = InitialTriState::kAllOut;
  303. skippable = true;
  304. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  305. initialTriState = InitialTriState::kAllIn;
  306. skippable = true;
  307. } else if (!embiggens) {
  308. ClipResult result = this->clipOutsideElement(element);
  309. if (ClipResult::kMadeEmpty == result) {
  310. return;
  311. }
  312. if (ClipResult::kClipped == result) {
  313. initialTriState = InitialTriState::kAllIn;
  314. skippable = true;
  315. }
  316. }
  317. } else {
  318. if (element->contains(relaxedQueryBounds)) {
  319. initialTriState = InitialTriState::kAllIn;
  320. skippable = true;
  321. } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
  322. initialTriState = InitialTriState::kAllOut;
  323. skippable = true;
  324. } else if (!embiggens) {
  325. ClipResult result = this->clipInsideElement(element);
  326. if (ClipResult::kMadeEmpty == result) {
  327. return;
  328. }
  329. if (ClipResult::kClipped == result) {
  330. initialTriState = InitialTriState::kAllIn;
  331. skippable = true;
  332. }
  333. }
  334. }
  335. if (!skippable) {
  336. initialTriState = InitialTriState::kAllOut;
  337. embiggens = emsmallens = true;
  338. }
  339. break;
  340. default:
  341. SkDEBUGFAIL("Unexpected op.");
  342. break;
  343. }
  344. if (!skippable) {
  345. if (fMaskElements.isEmpty()) {
  346. // This will be the last element. Record the stricter genID.
  347. fMaskGenID = element->getGenID();
  348. }
  349. // if it is a flip, change it to a bounds-filling rect
  350. if (isFlip) {
  351. SkASSERT(kXOR_SkClipOp == element->getOp() ||
  352. kReverseDifference_SkClipOp == element->getOp());
  353. fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(),
  354. kReverseDifference_SkClipOp, false);
  355. } else {
  356. Element* newElement = fMaskElements.addToHead(*element);
  357. if (newElement->isAA()) {
  358. ++numAAElements;
  359. }
  360. // Intersecting an inverse shape is the same as differencing the non-inverse shape.
  361. // Replacing with an inverse shape is the same as setting initialState=kAllIn and
  362. // differencing the non-inverse shape.
  363. bool isReplace = kReplace_SkClipOp == newElement->getOp();
  364. if (newElement->isInverseFilled() &&
  365. (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
  366. newElement->invertShapeFillType();
  367. newElement->setOp(kDifference_SkClipOp);
  368. if (isReplace) {
  369. SkASSERT(InitialTriState::kAllOut == initialTriState);
  370. initialTriState = InitialTriState::kAllIn;
  371. }
  372. }
  373. }
  374. }
  375. }
  376. if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
  377. (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
  378. fMaskElements.reset();
  379. numAAElements = 0;
  380. } else {
  381. Element* element = fMaskElements.headIter().get();
  382. while (element) {
  383. bool skippable = false;
  384. switch (element->getOp()) {
  385. case kDifference_SkClipOp:
  386. // subtracting from the empty set yields the empty set.
  387. skippable = InitialTriState::kAllOut == initialTriState;
  388. break;
  389. case kIntersect_SkClipOp:
  390. // intersecting with the empty set yields the empty set
  391. if (InitialTriState::kAllOut == initialTriState) {
  392. skippable = true;
  393. } else {
  394. // We can clear to zero and then simply draw the clip element.
  395. initialTriState = InitialTriState::kAllOut;
  396. element->setOp(kReplace_SkClipOp);
  397. }
  398. break;
  399. case kUnion_SkClipOp:
  400. if (InitialTriState::kAllIn == initialTriState) {
  401. // unioning the infinite plane with anything is a no-op.
  402. skippable = true;
  403. } else {
  404. // unioning the empty set with a shape is the shape.
  405. element->setOp(kReplace_SkClipOp);
  406. }
  407. break;
  408. case kXOR_SkClipOp:
  409. if (InitialTriState::kAllOut == initialTriState) {
  410. // xor could be changed to diff in the kAllIn case, not sure it's a win.
  411. element->setOp(kReplace_SkClipOp);
  412. }
  413. break;
  414. case kReverseDifference_SkClipOp:
  415. if (InitialTriState::kAllIn == initialTriState) {
  416. // subtracting the whole plane will yield the empty set.
  417. skippable = true;
  418. initialTriState = InitialTriState::kAllOut;
  419. } else {
  420. // this picks up flips inserted in the backwards pass.
  421. skippable = element->isInverseFilled() ?
  422. GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
  423. element->contains(relaxedQueryBounds);
  424. if (skippable) {
  425. initialTriState = InitialTriState::kAllIn;
  426. } else {
  427. element->setOp(kReplace_SkClipOp);
  428. }
  429. }
  430. break;
  431. case kReplace_SkClipOp:
  432. skippable = false; // we would have skipped it in the backwards walk if we
  433. // could've.
  434. break;
  435. default:
  436. SkDEBUGFAIL("Unexpected op.");
  437. break;
  438. }
  439. if (!skippable) {
  440. break;
  441. } else {
  442. if (element->isAA()) {
  443. --numAAElements;
  444. }
  445. fMaskElements.popHead();
  446. element = fMaskElements.headIter().get();
  447. }
  448. }
  449. }
  450. fMaskRequiresAA = numAAElements > 0;
  451. SkASSERT(InitialTriState::kUnknown != initialTriState);
  452. fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
  453. }
  454. GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) {
  455. SkIRect elementIBounds;
  456. if (!element->isAA()) {
  457. element->getBounds().round(&elementIBounds);
  458. } else {
  459. elementIBounds = GrClip::GetPixelIBounds(element->getBounds());
  460. }
  461. SkASSERT(fHasScissor);
  462. if (!fScissor.intersect(elementIBounds)) {
  463. this->makeEmpty();
  464. return ClipResult::kMadeEmpty;
  465. }
  466. switch (element->getDeviceSpaceType()) {
  467. case Element::DeviceSpaceType::kEmpty:
  468. return ClipResult::kMadeEmpty;
  469. case Element::DeviceSpaceType::kRect:
  470. SkASSERT(element->getBounds() == element->getDeviceSpaceRect());
  471. SkASSERT(!element->isInverseFilled());
  472. if (element->isAA()) {
  473. if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet?
  474. fAAClipRect = element->getDeviceSpaceRect();
  475. // fAAClipRectGenID is the value we should use for fMaskGenID if we end up
  476. // moving the AA clip rect into the mask. The mask GenID is simply the topmost
  477. // element's GenID. And since we walk the stack backwards, this means it's just
  478. // the first element we don't skip during our walk.
  479. fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID;
  480. SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
  481. } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) {
  482. this->makeEmpty();
  483. return ClipResult::kMadeEmpty;
  484. }
  485. }
  486. return ClipResult::kClipped;
  487. case Element::DeviceSpaceType::kRRect:
  488. SkASSERT(!element->isInverseFilled());
  489. return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo,
  490. GrAA(element->isAA()));
  491. case Element::DeviceSpaceType::kPath:
  492. return this->addAnalyticFP(element->getDeviceSpacePath(),
  493. Invert(element->isInverseFilled()), GrAA(element->isAA()));
  494. }
  495. SK_ABORT("Unexpected DeviceSpaceType");
  496. return ClipResult::kNotClipped;
  497. }
  498. GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) {
  499. switch (element->getDeviceSpaceType()) {
  500. case Element::DeviceSpaceType::kEmpty:
  501. return ClipResult::kMadeEmpty;
  502. case Element::DeviceSpaceType::kRect:
  503. SkASSERT(!element->isInverseFilled());
  504. if (fWindowRects.count() < fMaxWindowRectangles) {
  505. // Clip out the inside of every rect. We won't be able to entirely skip the AA ones,
  506. // but it saves processing time.
  507. this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA());
  508. if (!element->isAA()) {
  509. return ClipResult::kClipped;
  510. }
  511. }
  512. return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes,
  513. GrAA(element->isAA()));
  514. case Element::DeviceSpaceType::kRRect: {
  515. SkASSERT(!element->isInverseFilled());
  516. const SkRRect& clipRRect = element->getDeviceSpaceRRect();
  517. ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes,
  518. GrAA(element->isAA()));
  519. if (fWindowRects.count() >= fMaxWindowRectangles) {
  520. return clipResult;
  521. }
  522. // Clip out the interiors of round rects with two window rectangles in the shape of a
  523. // "plus". This doesn't let us skip the clip element, but still saves processing time.
  524. SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
  525. SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
  526. if (SkRRect::kComplex_Type == clipRRect.getType()) {
  527. const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
  528. const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
  529. insetTL.fX = SkTMax(insetTL.x(), insetBL.x());
  530. insetTL.fY = SkTMax(insetTL.y(), insetTR.y());
  531. insetBR.fX = SkTMax(insetBR.x(), insetTR.x());
  532. insetBR.fY = SkTMax(insetBR.y(), insetBL.y());
  533. }
  534. const SkRect& bounds = clipRRect.getBounds();
  535. if (insetTL.x() + insetBR.x() >= bounds.width() ||
  536. insetTL.y() + insetBR.y() >= bounds.height()) {
  537. return clipResult; // The interior "plus" is empty.
  538. }
  539. SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
  540. bounds.right(), bounds.bottom() - insetBR.y());
  541. this->addWindowRectangle(horzRect, element->isAA());
  542. if (fWindowRects.count() < fMaxWindowRectangles) {
  543. SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
  544. bounds.right() - insetBR.x(), bounds.bottom());
  545. this->addWindowRectangle(vertRect, element->isAA());
  546. }
  547. return clipResult;
  548. }
  549. case Element::DeviceSpaceType::kPath:
  550. return this->addAnalyticFP(element->getDeviceSpacePath(),
  551. Invert(!element->isInverseFilled()), GrAA(element->isAA()));
  552. }
  553. SK_ABORT("Unexpected DeviceSpaceType");
  554. return ClipResult::kNotClipped;
  555. }
  556. inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
  557. SkIRect window;
  558. if (!elementIsAA) {
  559. elementInteriorRect.round(&window);
  560. } else {
  561. elementInteriorRect.roundIn(&window);
  562. }
  563. if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
  564. fWindowRects.addWindow(window);
  565. }
  566. }
  567. GrClipEdgeType GrReducedClip::GetClipEdgeType(Invert invert, GrAA aa) {
  568. if (Invert::kNo == invert) {
  569. return (GrAA::kYes == aa) ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW;
  570. } else {
  571. return (GrAA::kYes == aa) ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW;
  572. }
  573. }
  574. GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRect& deviceSpaceRect,
  575. Invert invert, GrAA aa) {
  576. if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
  577. return ClipResult::kNotClipped;
  578. }
  579. fAnalyticFPs.push_back(GrAARectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRect));
  580. SkASSERT(fAnalyticFPs.back());
  581. return ClipResult::kClipped;
  582. }
  583. GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRRect& deviceSpaceRRect,
  584. Invert invert, GrAA aa) {
  585. if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
  586. return ClipResult::kNotClipped;
  587. }
  588. if (auto fp = GrRRectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRRect,
  589. *fCaps->shaderCaps())) {
  590. fAnalyticFPs.push_back(std::move(fp));
  591. return ClipResult::kClipped;
  592. }
  593. SkPath deviceSpacePath;
  594. deviceSpacePath.setIsVolatile(true);
  595. deviceSpacePath.addRRect(deviceSpaceRRect);
  596. return this->addAnalyticFP(deviceSpacePath, invert, aa);
  597. }
  598. GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkPath& deviceSpacePath,
  599. Invert invert, GrAA aa) {
  600. if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
  601. return ClipResult::kNotClipped;
  602. }
  603. if (auto fp = GrConvexPolyEffect::Make(GetClipEdgeType(invert, aa), deviceSpacePath)) {
  604. fAnalyticFPs.push_back(std::move(fp));
  605. return ClipResult::kClipped;
  606. }
  607. if (fCCPRClipPaths.count() < fMaxCCPRClipPaths && GrAA::kYes == aa) {
  608. // Set aside CCPR paths for later. We will create their clip FPs once we know the ID of the
  609. // opList they will operate in.
  610. SkPath& ccprClipPath = fCCPRClipPaths.push_back(deviceSpacePath);
  611. if (Invert::kYes == invert) {
  612. ccprClipPath.toggleInverseFillType();
  613. }
  614. return ClipResult::kClipped;
  615. }
  616. return ClipResult::kNotClipped;
  617. }
  618. void GrReducedClip::makeEmpty() {
  619. fHasScissor = false;
  620. fAAClipRectGenID = SK_InvalidGenID;
  621. fWindowRects.reset();
  622. fMaskElements.reset();
  623. fInitialState = InitialState::kAllOut;
  624. }
  625. ////////////////////////////////////////////////////////////////////////////////
  626. // Create a 8-bit clip mask in alpha
  627. static bool stencil_element(GrRenderTargetContext* rtc,
  628. const GrFixedClip& clip,
  629. const GrUserStencilSettings* ss,
  630. const SkMatrix& viewMatrix,
  631. const SkClipStack::Element* element) {
  632. GrAA aa = GrAA(element->isAA());
  633. switch (element->getDeviceSpaceType()) {
  634. case SkClipStack::Element::DeviceSpaceType::kEmpty:
  635. SkDEBUGFAIL("Should never get here with an empty element.");
  636. break;
  637. case SkClipStack::Element::DeviceSpaceType::kRect: {
  638. GrPaint paint;
  639. paint.setCoverageSetOpXPFactory((SkRegion::Op)element->getOp(),
  640. element->isInverseFilled());
  641. rtc->priv().stencilRect(clip, ss, std::move(paint), aa, viewMatrix,
  642. element->getDeviceSpaceRect());
  643. return true;
  644. }
  645. default: {
  646. SkPath path;
  647. element->asDeviceSpacePath(&path);
  648. if (path.isInverseFillType()) {
  649. path.toggleInverseFillType();
  650. }
  651. return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(),
  652. element->isInverseFilled(), aa, viewMatrix, path);
  653. }
  654. }
  655. return false;
  656. }
  657. static void stencil_device_rect(GrRenderTargetContext* rtc,
  658. const GrHardClip& clip,
  659. const GrUserStencilSettings* ss,
  660. GrAA aa,
  661. const SkRect& rect) {
  662. GrPaint paint;
  663. paint.setXPFactory(GrDisableColorXPFactory::Get());
  664. rtc->priv().stencilRect(clip, ss, std::move(paint), aa, SkMatrix::I(), rect);
  665. }
  666. static void draw_element(GrRenderTargetContext* rtc,
  667. const GrClip& clip, // TODO: can this just always be WideOpen?
  668. GrPaint&& paint,
  669. GrAA aa,
  670. const SkMatrix& viewMatrix,
  671. const SkClipStack::Element* element) {
  672. // TODO: Draw rrects directly here.
  673. switch (element->getDeviceSpaceType()) {
  674. case SkClipStack::Element::DeviceSpaceType::kEmpty:
  675. SkDEBUGFAIL("Should never get here with an empty element.");
  676. break;
  677. case SkClipStack::Element::DeviceSpaceType::kRect:
  678. rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect());
  679. break;
  680. default: {
  681. SkPath path;
  682. element->asDeviceSpacePath(&path);
  683. if (path.isInverseFillType()) {
  684. path.toggleInverseFillType();
  685. }
  686. rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
  687. break;
  688. }
  689. }
  690. }
  691. bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
  692. // The texture may be larger than necessary, this rect represents the part of the texture
  693. // we populate with a rasterization of the clip.
  694. GrFixedClip clip(SkIRect::MakeWH(fScissor.width(), fScissor.height()));
  695. if (!fWindowRects.empty()) {
  696. clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()),
  697. GrWindowRectsState::Mode::kExclusive);
  698. }
  699. // The scratch texture that we are drawing into can be substantially larger than the mask. Only
  700. // clear the part that we care about.
  701. SkPMColor4f initialCoverage =
  702. InitialState::kAllIn == this->initialState() ? SK_PMColor4fWHITE : SK_PMColor4fTRANSPARENT;
  703. rtc->priv().clear(clip, initialCoverage, GrRenderTargetContext::CanClearFullscreen::kYes);
  704. // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
  705. SkMatrix translate;
  706. translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top()));
  707. // walk through each clip element and perform its set op
  708. for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
  709. const Element* element = iter.get();
  710. SkRegion::Op op = (SkRegion::Op)element->getOp();
  711. GrAA aa = GrAA(element->isAA());
  712. bool invert = element->isInverseFilled();
  713. if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
  714. // draw directly into the result with the stencil set to make the pixels affected
  715. // by the clip shape be non-zero.
  716. static constexpr GrUserStencilSettings kStencilInElement(
  717. GrUserStencilSettings::StaticInit<
  718. 0xffff,
  719. GrUserStencilTest::kAlways,
  720. 0xffff,
  721. GrUserStencilOp::kReplace,
  722. GrUserStencilOp::kReplace,
  723. 0xffff>()
  724. );
  725. if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
  726. return false;
  727. }
  728. // Draw to the exterior pixels (those with a zero stencil value).
  729. static constexpr GrUserStencilSettings kDrawOutsideElement(
  730. GrUserStencilSettings::StaticInit<
  731. 0x0000,
  732. GrUserStencilTest::kEqual,
  733. 0xffff,
  734. GrUserStencilOp::kZero,
  735. GrUserStencilOp::kZero,
  736. 0xffff>()
  737. );
  738. GrPaint paint;
  739. paint.setCoverageSetOpXPFactory(op, !invert);
  740. rtc->priv().stencilRect(clip, &kDrawOutsideElement, std::move(paint), GrAA::kNo,
  741. translate, SkRect::Make(fScissor));
  742. } else {
  743. // all the remaining ops can just be directly draw into the accumulation buffer
  744. GrPaint paint;
  745. paint.setCoverageSetOpXPFactory(op, false);
  746. draw_element(rtc, clip, std::move(paint), aa, translate, element);
  747. }
  748. }
  749. return true;
  750. }
  751. ////////////////////////////////////////////////////////////////////////////////
  752. // Create a 1-bit clip mask in the stencil buffer.
  753. bool GrReducedClip::drawStencilClipMask(GrRecordingContext* context,
  754. GrRenderTargetContext* renderTargetContext) const {
  755. // We set the current clip to the bounds so that our recursive draws are scissored to them.
  756. GrStencilClip stencilClip(fScissor, this->maskGenID());
  757. if (!fWindowRects.empty()) {
  758. stencilClip.fixedClip().setWindowRectangles(fWindowRects,
  759. GrWindowRectsState::Mode::kExclusive);
  760. }
  761. bool initialState = InitialState::kAllIn == this->initialState();
  762. renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState);
  763. // walk through each clip element and perform its set op with the existing clip.
  764. for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
  765. const Element* element = iter.get();
  766. // MIXED SAMPLES TODO: We can use stencil with mixed samples as well.
  767. bool doStencilMSAA = element->isAA() && renderTargetContext->numSamples() > 1;
  768. // Since we are only drawing to the stencil buffer, we can use kMSAA even if the render
  769. // target is mixed sampled.
  770. auto pathAAType = (doStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
  771. bool fillInverted = false;
  772. // This will be used to determine whether the clip shape can be rendered into the
  773. // stencil with arbitrary stencil settings.
  774. GrPathRenderer::StencilSupport stencilSupport;
  775. SkRegion::Op op = (SkRegion::Op)element->getOp();
  776. GrPathRenderer* pr = nullptr;
  777. SkPath clipPath;
  778. if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
  779. stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
  780. fillInverted = false;
  781. } else {
  782. element->asDeviceSpacePath(&clipPath);
  783. fillInverted = clipPath.isInverseFillType();
  784. if (fillInverted) {
  785. clipPath.toggleInverseFillType();
  786. }
  787. GrShape shape(clipPath, GrStyle::SimpleFill());
  788. GrPathRenderer::CanDrawPathArgs canDrawArgs;
  789. canDrawArgs.fCaps = context->priv().caps();
  790. canDrawArgs.fProxy = renderTargetContext->proxy();
  791. canDrawArgs.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
  792. canDrawArgs.fViewMatrix = &SkMatrix::I();
  793. canDrawArgs.fShape = &shape;
  794. canDrawArgs.fAAType = pathAAType;
  795. canDrawArgs.fHasUserStencilSettings = false;
  796. canDrawArgs.fTargetIsWrappedVkSecondaryCB = renderTargetContext->wrapsVkSecondaryCB();
  797. GrDrawingManager* dm = context->priv().drawingManager();
  798. pr = dm->getPathRenderer(canDrawArgs, false, GrPathRendererChain::DrawType::kStencil,
  799. &stencilSupport);
  800. if (!pr) {
  801. return false;
  802. }
  803. }
  804. bool canRenderDirectToStencil =
  805. GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
  806. bool drawDirectToClip; // Given the renderer, the element,
  807. // fill rule, and set operation should
  808. // we render the element directly to
  809. // stencil bit used for clipping.
  810. GrUserStencilSettings const* const* stencilPasses =
  811. GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
  812. &drawDirectToClip);
  813. // draw the element to the client stencil bits if necessary
  814. if (!drawDirectToClip) {
  815. static constexpr GrUserStencilSettings kDrawToStencil(
  816. GrUserStencilSettings::StaticInit<
  817. 0x0000,
  818. GrUserStencilTest::kAlways,
  819. 0xffff,
  820. GrUserStencilOp::kIncMaybeClamp,
  821. GrUserStencilOp::kIncMaybeClamp,
  822. 0xffff>()
  823. );
  824. if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
  825. stencil_device_rect(renderTargetContext, stencilClip.fixedClip(), &kDrawToStencil,
  826. GrAA(doStencilMSAA), element->getDeviceSpaceRect());
  827. } else {
  828. if (!clipPath.isEmpty()) {
  829. GrShape shape(clipPath, GrStyle::SimpleFill());
  830. if (canRenderDirectToStencil) {
  831. GrPaint paint;
  832. paint.setXPFactory(GrDisableColorXPFactory::Get());
  833. GrPathRenderer::DrawPathArgs args{context,
  834. std::move(paint),
  835. &kDrawToStencil,
  836. renderTargetContext,
  837. &stencilClip.fixedClip(),
  838. &stencilClip.fixedClip().scissorRect(),
  839. &SkMatrix::I(),
  840. &shape,
  841. pathAAType,
  842. false};
  843. pr->drawPath(args);
  844. } else {
  845. GrPathRenderer::StencilPathArgs args;
  846. args.fContext = context;
  847. args.fRenderTargetContext = renderTargetContext;
  848. args.fClip = &stencilClip.fixedClip();
  849. args.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
  850. args.fViewMatrix = &SkMatrix::I();
  851. args.fDoStencilMSAA = GrAA(doStencilMSAA);
  852. args.fShape = &shape;
  853. pr->stencilPath(args);
  854. }
  855. }
  856. }
  857. }
  858. // now we modify the clip bit by rendering either the clip
  859. // element directly or a bounding rect of the entire clip.
  860. for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
  861. if (drawDirectToClip) {
  862. if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
  863. stencil_device_rect(renderTargetContext, stencilClip, *pass,
  864. GrAA(doStencilMSAA), element->getDeviceSpaceRect());
  865. } else {
  866. GrShape shape(clipPath, GrStyle::SimpleFill());
  867. GrPaint paint;
  868. paint.setXPFactory(GrDisableColorXPFactory::Get());
  869. GrPathRenderer::DrawPathArgs args{context,
  870. std::move(paint),
  871. *pass,
  872. renderTargetContext,
  873. &stencilClip,
  874. &stencilClip.fixedClip().scissorRect(),
  875. &SkMatrix::I(),
  876. &shape,
  877. pathAAType,
  878. false};
  879. pr->drawPath(args);
  880. }
  881. } else {
  882. // The view matrix is setup to do clip space -> stencil space translation, so
  883. // draw rect in clip space.
  884. stencil_device_rect(renderTargetContext, stencilClip, *pass, GrAA(doStencilMSAA),
  885. SkRect::Make(fScissor));
  886. }
  887. }
  888. }
  889. return true;
  890. }
  891. std::unique_ptr<GrFragmentProcessor> GrReducedClip::finishAndDetachAnalyticFPs(
  892. GrCoverageCountingPathRenderer* ccpr, uint32_t opListID) {
  893. // Make sure finishAndDetachAnalyticFPs hasn't been called already.
  894. SkDEBUGCODE(for (const auto& fp : fAnalyticFPs) { SkASSERT(fp); })
  895. if (!fCCPRClipPaths.empty()) {
  896. fAnalyticFPs.reserve(fAnalyticFPs.count() + fCCPRClipPaths.count());
  897. for (const SkPath& ccprClipPath : fCCPRClipPaths) {
  898. SkASSERT(ccpr);
  899. SkASSERT(fHasScissor);
  900. auto fp = ccpr->makeClipProcessor(opListID, ccprClipPath, fScissor, *fCaps);
  901. fAnalyticFPs.push_back(std::move(fp));
  902. }
  903. fCCPRClipPaths.reset();
  904. }
  905. return GrFragmentProcessor::RunInSeries(fAnalyticFPs.begin(), fAnalyticFPs.count());
  906. }