GrClipStackClip.cpp revision cb31e51d9355adb1d25ce3e121cde21b05ecf63e
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 8#include "GrClipStackClip.h" 9 10#include "GrAppliedClip.h" 11#include "GrDrawingManager.h" 12#include "GrDrawContextPriv.h" 13#include "GrFixedClip.h" 14#include "GrGpuResourcePriv.h" 15#include "GrRenderTargetPriv.h" 16#include "GrStencilAttachment.h" 17#include "GrSWMaskHelper.h" 18#include "effects/GrConvexPolyEffect.h" 19#include "effects/GrRRectEffect.h" 20#include "effects/GrTextureDomain.h" 21 22typedef SkClipStack::Element Element; 23typedef GrReducedClip::InitialState InitialState; 24typedef GrReducedClip::ElementList ElementList; 25 26static const int kMaxAnalyticElements = 4; 27 28bool GrClipStackClip::quickContains(const SkRect& rect) const { 29 if (!fStack || fStack->isWideOpen()) { 30 return true; 31 } 32 return fStack->quickContains(rect.makeOffset(SkIntToScalar(fOrigin.x()), 33 SkIntToScalar(fOrigin.y()))); 34} 35 36bool GrClipStackClip::quickContains(const SkRRect& rrect) const { 37 if (!fStack || fStack->isWideOpen()) { 38 return true; 39 } 40 return fStack->quickContains(rrect.makeOffset(SkIntToScalar(fOrigin.fX), 41 SkIntToScalar(fOrigin.fY))); 42} 43 44bool GrClipStackClip::isRRect(const SkRect& origRTBounds, SkRRect* rr, bool* aa) const { 45 if (!fStack) { 46 return false; 47 } 48 const SkRect* rtBounds = &origRTBounds; 49 SkRect tempRTBounds; 50 bool origin = fOrigin.fX || fOrigin.fY; 51 if (origin) { 52 tempRTBounds = origRTBounds; 53 tempRTBounds.offset(SkIntToScalar(fOrigin.fX), SkIntToScalar(fOrigin.fY)); 54 rtBounds = &tempRTBounds; 55 } 56 if (fStack->isRRect(*rtBounds, rr, aa)) { 57 if (origin) { 58 rr->offset(-SkIntToScalar(fOrigin.fX), -SkIntToScalar(fOrigin.fY)); 59 } 60 return true; 61 } 62 return false; 63} 64 65void GrClipStackClip::getConservativeBounds(int width, int height, SkIRect* devResult, 66 bool* isIntersectionOfRects) const { 67 if (!fStack) { 68 devResult->setXYWH(0, 0, width, height); 69 if (isIntersectionOfRects) { 70 *isIntersectionOfRects = true; 71 } 72 return; 73 } 74 SkRect devBounds; 75 fStack->getConservativeBounds(-fOrigin.x(), -fOrigin.y(), width, height, &devBounds, 76 isIntersectionOfRects); 77 devBounds.roundOut(devResult); 78} 79 80//////////////////////////////////////////////////////////////////////////////// 81// set up the draw state to enable the aa clipping mask. Besides setting up the 82// stage matrix this also alters the vertex layout 83static sk_sp<GrFragmentProcessor> create_fp_for_mask(GrTexture* result, 84 const SkIRect &devBound) { 85 SkMatrix mat; 86 // We use device coords to compute the texture coordinates. We set our matrix to be a 87 // translation to the devBound, and then a scaling matrix to normalized coords. 88 mat.setIDiv(result->width(), result->height()); 89 mat.preTranslate(SkIntToScalar(-devBound.fLeft), 90 SkIntToScalar(-devBound.fTop)); 91 92 SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height()); 93 return sk_sp<GrFragmentProcessor>(GrTextureDomainEffect::Make( 94 result, 95 nullptr, 96 mat, 97 GrTextureDomain::MakeTexelDomain(result, domainTexels), 98 GrTextureDomain::kDecal_Mode, 99 GrTextureParams::kNone_FilterMode, 100 kDevice_GrCoordSet)); 101} 102 103// Does the path in 'element' require SW rendering? If so, return true (and, 104// optionally, set 'prOut' to NULL. If not, return false (and, optionally, set 105// 'prOut' to the non-SW path renderer that will do the job). 106bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context, 107 bool hasUserStencilSettings, 108 const GrDrawContext* drawContext, 109 const SkMatrix& viewMatrix, 110 const Element* element, 111 GrPathRenderer** prOut, 112 bool needsStencil) { 113 if (Element::kRect_Type == element->getType()) { 114 // rects can always be drawn directly w/o using the software path 115 // TODO: skip rrects once we're drawing them directly. 116 if (prOut) { 117 *prOut = nullptr; 118 } 119 return false; 120 } else { 121 // We shouldn't get here with an empty clip element. 122 SkASSERT(Element::kEmpty_Type != element->getType()); 123 124 // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer 125 SkPath path; 126 element->asPath(&path); 127 if (path.isInverseFillType()) { 128 path.toggleInverseFillType(); 129 } 130 131 GrPathRendererChain::DrawType type; 132 133 if (needsStencil) { 134 type = element->isAA() 135 ? GrPathRendererChain::kStencilAndColorAntiAlias_DrawType 136 : GrPathRendererChain::kStencilAndColor_DrawType; 137 } else { 138 type = element->isAA() 139 ? GrPathRendererChain::kColorAntiAlias_DrawType 140 : GrPathRendererChain::kColor_DrawType; 141 } 142 143 GrShape shape(path, GrStyle::SimpleFill()); 144 GrPathRenderer::CanDrawPathArgs canDrawArgs; 145 canDrawArgs.fShaderCaps = context->caps()->shaderCaps(); 146 canDrawArgs.fViewMatrix = &viewMatrix; 147 canDrawArgs.fShape = &shape; 148 canDrawArgs.fAntiAlias = element->isAA(); 149 canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings; 150 canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled(); 151 152 // the 'false' parameter disallows use of the SW path renderer 153 GrPathRenderer* pr = context->drawingManager()->getPathRenderer(canDrawArgs, false, type); 154 if (prOut) { 155 *prOut = pr; 156 } 157 return SkToBool(!pr); 158 } 159} 160 161/* 162 * This method traverses the clip stack to see if the GrSoftwarePathRenderer 163 * will be used on any element. If so, it returns true to indicate that the 164 * entire clip should be rendered in SW and then uploaded en masse to the gpu. 165 */ 166bool GrClipStackClip::UseSWOnlyPath(GrContext* context, 167 bool hasUserStencilSettings, 168 const GrDrawContext* drawContext, 169 const SkVector& clipToMaskOffset, 170 const ElementList& elements) { 171 // TODO: generalize this function so that when 172 // a clip gets complex enough it can just be done in SW regardless 173 // of whether it would invoke the GrSoftwarePathRenderer. 174 175 // Set the matrix so that rendered clip elements are transformed to mask space from clip 176 // space. 177 const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY); 178 179 for (ElementList::Iter iter(elements); iter.get(); iter.next()) { 180 const Element* element = iter.get(); 181 182 SkRegion::Op op = element->getOp(); 183 bool invert = element->isInverseFilled(); 184 bool needsStencil = invert || 185 SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op; 186 187 if (PathNeedsSWRenderer(context, hasUserStencilSettings, 188 drawContext, translate, element, nullptr, needsStencil)) { 189 return true; 190 } 191 } 192 return false; 193} 194 195static bool get_analytic_clip_processor(const ElementList& elements, 196 bool abortIfAA, 197 const SkVector& clipToRTOffset, 198 const SkRect& drawBounds, 199 sk_sp<GrFragmentProcessor>* resultFP) { 200 SkRect boundsInClipSpace; 201 boundsInClipSpace = drawBounds.makeOffset(-clipToRTOffset.fX, -clipToRTOffset.fY); 202 SkASSERT(elements.count() <= kMaxAnalyticElements); 203 SkSTArray<kMaxAnalyticElements, sk_sp<GrFragmentProcessor>> fps; 204 ElementList::Iter iter(elements); 205 while (iter.get()) { 206 SkRegion::Op op = iter.get()->getOp(); 207 bool invert; 208 bool skip = false; 209 switch (op) { 210 case SkRegion::kReplace_Op: 211 SkASSERT(iter.get() == elements.head()); 212 // Fallthrough, handled same as intersect. 213 case SkRegion::kIntersect_Op: 214 invert = false; 215 if (iter.get()->contains(boundsInClipSpace)) { 216 skip = true; 217 } 218 break; 219 case SkRegion::kDifference_Op: 220 invert = true; 221 // We don't currently have a cheap test for whether a rect is fully outside an 222 // element's primitive, so don't attempt to set skip. 223 break; 224 default: 225 return false; 226 } 227 if (!skip) { 228 GrPrimitiveEdgeType edgeType; 229 if (iter.get()->isAA()) { 230 if (abortIfAA) { 231 return false; 232 } 233 edgeType = 234 invert ? kInverseFillAA_GrProcessorEdgeType : kFillAA_GrProcessorEdgeType; 235 } else { 236 edgeType = 237 invert ? kInverseFillBW_GrProcessorEdgeType : kFillBW_GrProcessorEdgeType; 238 } 239 240 switch (iter.get()->getType()) { 241 case SkClipStack::Element::kPath_Type: 242 fps.emplace_back(GrConvexPolyEffect::Make(edgeType, iter.get()->getPath(), 243 &clipToRTOffset)); 244 break; 245 case SkClipStack::Element::kRRect_Type: { 246 SkRRect rrect = iter.get()->getRRect(); 247 rrect.offset(clipToRTOffset.fX, clipToRTOffset.fY); 248 fps.emplace_back(GrRRectEffect::Make(edgeType, rrect)); 249 break; 250 } 251 case SkClipStack::Element::kRect_Type: { 252 SkRect rect = iter.get()->getRect(); 253 rect.offset(clipToRTOffset.fX, clipToRTOffset.fY); 254 fps.emplace_back(GrConvexPolyEffect::Make(edgeType, rect)); 255 break; 256 } 257 default: 258 break; 259 } 260 if (!fps.back()) { 261 return false; 262 } 263 } 264 iter.next(); 265 } 266 267 *resultFP = nullptr; 268 if (fps.count()) { 269 *resultFP = GrFragmentProcessor::RunInSeries(fps.begin(), fps.count()); 270 } 271 return true; 272} 273 274//////////////////////////////////////////////////////////////////////////////// 275// sort out what kind of clip mask needs to be created: alpha, stencil, 276// scissor, or entirely software 277bool GrClipStackClip::apply(GrContext* context, GrDrawContext* drawContext, bool useHWAA, 278 bool hasUserStencilSettings, GrAppliedClip* out) const { 279 if (!fStack || fStack->isWideOpen()) { 280 return true; 281 } 282 283 SkRect devBounds = SkRect::MakeIWH(drawContext->width(), drawContext->height()); 284 if (!devBounds.intersect(out->clippedDrawBounds())) { 285 return false; 286 } 287 288 const SkScalar clipX = SkIntToScalar(fOrigin.x()), 289 clipY = SkIntToScalar(fOrigin.y()); 290 291 SkRect clipSpaceDevBounds = devBounds.makeOffset(clipX, clipY); 292 const GrReducedClip reducedClip(*fStack, clipSpaceDevBounds); 293 294 if (reducedClip.hasIBounds() && 295 !GrClip::IsInsideClip(reducedClip.ibounds(), clipSpaceDevBounds)) { 296 SkIRect scissorSpaceIBounds(reducedClip.ibounds()); 297 scissorSpaceIBounds.offset(-fOrigin); 298 out->addScissor(scissorSpaceIBounds); 299 } 300 301 if (reducedClip.elements().isEmpty()) { 302 return InitialState::kAllIn == reducedClip.initialState(); 303 } 304 305 SkASSERT(reducedClip.hasIBounds()); 306 307 // Attempt to implement difference clip rects with window rectangles. This will eventually 308 // become more comprehensive. 309 if (drawContext->accessRenderTarget()->renderTargetPriv().supportsWindowRectangles() && 310 1 == reducedClip.elements().count() && !reducedClip.requiresAA() && 311 InitialState::kAllIn == reducedClip.initialState()) { 312 const Element* element = reducedClip.elements().head(); 313 SkRegion::Op op = element->getOp(); 314 if (Element::kRect_Type == element->getType() && 315 (SkRegion::kDifference_Op == op || SkRegion::kXOR_Op == op)) { 316 SkIRect window; 317 element->getRect().round(&window); 318 window.offset(-fOrigin); 319 out->addWindowRectangle(window); 320 return true; 321 } 322 } 323 324 // An element count of 4 was chosen because of the common pattern in Blink of: 325 // isect RR 326 // diff RR 327 // isect convex_poly 328 // isect convex_poly 329 // when drawing rounded div borders. This could probably be tuned based on a 330 // configuration's relative costs of switching RTs to generate a mask vs 331 // longer shaders. 332 if (reducedClip.elements().count() <= kMaxAnalyticElements) { 333 // When there are multiple samples we want to do per-sample clipping, not compute a 334 // fractional pixel coverage. 335 bool disallowAnalyticAA = drawContext->isStencilBufferMultisampled(); 336 if (disallowAnalyticAA && !drawContext->numColorSamples()) { 337 // With a single color sample, any coverage info is lost from color once it hits the 338 // color buffer anyway, so we may as well use coverage AA if nothing else in the pipe 339 // is multisampled. 340 disallowAnalyticAA = useHWAA || hasUserStencilSettings; 341 } 342 sk_sp<GrFragmentProcessor> clipFP; 343 if (reducedClip.requiresAA() && 344 get_analytic_clip_processor(reducedClip.elements(), disallowAnalyticAA, 345 {-clipX, -clipY}, devBounds, &clipFP)) { 346 out->addCoverageFP(std::move(clipFP)); 347 return true; 348 } 349 } 350 351 // If the stencil buffer is multisampled we can use it to do everything. 352 if (!drawContext->isStencilBufferMultisampled() && reducedClip.requiresAA()) { 353 sk_sp<GrTexture> result; 354 355 // The top-left of the mask corresponds to the top-left corner of the bounds. 356 SkVector clipToMaskOffset = { 357 SkIntToScalar(-reducedClip.left()), 358 SkIntToScalar(-reducedClip.top()) 359 }; 360 361 if (UseSWOnlyPath(context, hasUserStencilSettings, drawContext, 362 clipToMaskOffset, reducedClip.elements())) { 363 // The clip geometry is complex enough that it will be more efficient to create it 364 // entirely in software 365 result = CreateSoftwareClipMask(context->textureProvider(), reducedClip, 366 clipToMaskOffset); 367 } else { 368 result = CreateAlphaClipMask(context, reducedClip, clipToMaskOffset); 369 // If createAlphaClipMask fails it means UseSWOnlyPath has a bug 370 SkASSERT(result); 371 } 372 373 if (result) { 374 // The mask's top left coord should be pinned to the rounded-out top left corner of 375 // clipSpace bounds. We determine the mask's position WRT to the render target here. 376 SkIRect rtSpaceMaskBounds = reducedClip.ibounds(); 377 rtSpaceMaskBounds.offset(-fOrigin); 378 out->addCoverageFP(create_fp_for_mask(result.get(), rtSpaceMaskBounds)); 379 return true; 380 } 381 // if alpha clip mask creation fails fall through to the non-AA code paths 382 } 383 384 // use the stencil clip if we can't represent the clip as a rectangle. 385 SkIPoint clipSpaceToStencilSpaceOffset = -fOrigin; 386 CreateStencilClipMask(context, drawContext, reducedClip, clipSpaceToStencilSpaceOffset); 387 out->addStencilClip(); 388 return true; 389} 390 391static bool stencil_element(GrDrawContext* dc, 392 const GrFixedClip& clip, 393 const GrUserStencilSettings* ss, 394 const SkMatrix& viewMatrix, 395 const SkClipStack::Element* element) { 396 397 // TODO: Draw rrects directly here. 398 switch (element->getType()) { 399 case Element::kEmpty_Type: 400 SkDEBUGFAIL("Should never get here with an empty element."); 401 break; 402 case Element::kRect_Type: 403 return dc->drawContextPriv().drawAndStencilRect(clip, ss, 404 element->getOp(), 405 element->isInverseFilled(), 406 element->isAA(), 407 viewMatrix, element->getRect()); 408 break; 409 default: { 410 SkPath path; 411 element->asPath(&path); 412 if (path.isInverseFillType()) { 413 path.toggleInverseFillType(); 414 } 415 416 return dc->drawContextPriv().drawAndStencilPath(clip, ss, 417 element->getOp(), 418 element->isInverseFilled(), 419 element->isAA(), viewMatrix, path); 420 break; 421 } 422 } 423 424 return false; 425} 426 427static void draw_element(GrDrawContext* dc, 428 const GrClip& clip, // TODO: can this just always be WideOpen? 429 const GrPaint &paint, 430 const SkMatrix& viewMatrix, 431 const SkClipStack::Element* element) { 432 433 // TODO: Draw rrects directly here. 434 switch (element->getType()) { 435 case Element::kEmpty_Type: 436 SkDEBUGFAIL("Should never get here with an empty element."); 437 break; 438 case Element::kRect_Type: 439 dc->drawRect(clip, paint, viewMatrix, element->getRect()); 440 break; 441 default: { 442 SkPath path; 443 element->asPath(&path); 444 if (path.isInverseFillType()) { 445 path.toggleInverseFillType(); 446 } 447 448 dc->drawPath(clip, paint, viewMatrix, path, GrStyle::SimpleFill()); 449 break; 450 } 451 } 452} 453 454//////////////////////////////////////////////////////////////////////////////// 455// Create a 8-bit clip mask in alpha 456 457static void GetClipMaskKey(int32_t clipGenID, const SkIRect& bounds, GrUniqueKey* key) { 458 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 459 GrUniqueKey::Builder builder(key, kDomain, 3); 460 builder[0] = clipGenID; 461 builder[1] = SkToU16(bounds.fLeft) | (SkToU16(bounds.fRight) << 16); 462 builder[2] = SkToU16(bounds.fTop) | (SkToU16(bounds.fBottom) << 16); 463} 464 465sk_sp<GrTexture> GrClipStackClip::CreateAlphaClipMask(GrContext* context, 466 const GrReducedClip& reducedClip, 467 const SkVector& clipToMaskOffset) { 468 GrResourceProvider* resourceProvider = context->resourceProvider(); 469 GrUniqueKey key; 470 GetClipMaskKey(reducedClip.elementsGenID(), reducedClip.ibounds(), &key); 471 if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) { 472 return sk_sp<GrTexture>(texture); 473 } 474 475 // There's no texture in the cache. Let's try to allocate it then. 476 GrPixelConfig config = kRGBA_8888_GrPixelConfig; 477 if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { 478 config = kAlpha_8_GrPixelConfig; 479 } 480 481 sk_sp<GrDrawContext> dc(context->makeDrawContext(SkBackingFit::kApprox, 482 reducedClip.width(), 483 reducedClip.height(), 484 config, nullptr)); 485 if (!dc) { 486 return nullptr; 487 } 488 489 // The texture may be larger than necessary, this rect represents the part of the texture 490 // we populate with a rasterization of the clip. 491 SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height()); 492 493 // The scratch texture that we are drawing into can be substantially larger than the mask. Only 494 // clear the part that we care about. 495 dc->clear(&maskSpaceIBounds, InitialState::kAllIn == reducedClip.initialState() ? -1 : 0, true); 496 497 // Set the matrix so that rendered clip elements are transformed to mask space from clip 498 // space. 499 const SkMatrix translate = SkMatrix::MakeTrans(clipToMaskOffset.fX, clipToMaskOffset.fY); 500 501 // It is important that we use maskSpaceIBounds as the stencil rect in the below loop. 502 // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first 503 // pass must not set values outside of this bounds or stencil values outside the rect won't be 504 // cleared. 505 506 // walk through each clip element and perform its set op 507 for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) { 508 const Element* element = iter.get(); 509 SkRegion::Op op = element->getOp(); 510 bool invert = element->isInverseFilled(); 511 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 512 GrFixedClip clip(maskSpaceIBounds); 513 514 // draw directly into the result with the stencil set to make the pixels affected 515 // by the clip shape be non-zero. 516 static constexpr GrUserStencilSettings kStencilInElement( 517 GrUserStencilSettings::StaticInit< 518 0xffff, 519 GrUserStencilTest::kAlways, 520 0xffff, 521 GrUserStencilOp::kReplace, 522 GrUserStencilOp::kReplace, 523 0xffff>() 524 ); 525 if (!stencil_element(dc.get(), clip, &kStencilInElement, 526 translate, element)) { 527 return nullptr; 528 } 529 530 // Draw to the exterior pixels (those with a zero stencil value). 531 static constexpr GrUserStencilSettings kDrawOutsideElement( 532 GrUserStencilSettings::StaticInit< 533 0x0000, 534 GrUserStencilTest::kEqual, 535 0xffff, 536 GrUserStencilOp::kZero, 537 GrUserStencilOp::kZero, 538 0xffff>() 539 ); 540 if (!dc->drawContextPriv().drawAndStencilRect(clip, &kDrawOutsideElement, 541 op, !invert, false, 542 translate, 543 SkRect::Make(reducedClip.ibounds()))) { 544 return nullptr; 545 } 546 } else { 547 // all the remaining ops can just be directly draw into the accumulation buffer 548 GrPaint paint; 549 paint.setAntiAlias(element->isAA()); 550 paint.setCoverageSetOpXPFactory(op, false); 551 552 draw_element(dc.get(), GrNoClip(), paint, translate, element); 553 } 554 } 555 556 sk_sp<GrTexture> texture(dc->asTexture()); 557 SkASSERT(texture); 558 texture->resourcePriv().setUniqueKey(key); 559 return texture; 560} 561 562//////////////////////////////////////////////////////////////////////////////// 563// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device 564// (as opposed to canvas) coordinates 565bool GrClipStackClip::CreateStencilClipMask(GrContext* context, 566 GrDrawContext* drawContext, 567 const GrReducedClip& reducedClip, 568 const SkIPoint& clipSpaceToStencilOffset) { 569 SkASSERT(drawContext); 570 571 GrStencilAttachment* stencilAttachment = context->resourceProvider()->attachStencilAttachment( 572 drawContext->accessRenderTarget()); 573 if (nullptr == stencilAttachment) { 574 return false; 575 } 576 577 // TODO: these need to be swapped over to using a StencilAttachmentProxy 578 if (stencilAttachment->mustRenderClip(reducedClip.elementsGenID(), reducedClip.ibounds(), 579 clipSpaceToStencilOffset)) { 580 stencilAttachment->setLastClip(reducedClip.elementsGenID(), reducedClip.ibounds(), 581 clipSpaceToStencilOffset); 582 // Set the matrix so that rendered clip elements are transformed from clip to stencil space. 583 SkVector translate = { 584 SkIntToScalar(clipSpaceToStencilOffset.fX), 585 SkIntToScalar(clipSpaceToStencilOffset.fY) 586 }; 587 SkMatrix viewMatrix; 588 viewMatrix.setTranslate(translate); 589 590 // We set the current clip to the bounds so that our recursive draws are scissored to them. 591 SkIRect stencilSpaceIBounds(reducedClip.ibounds()); 592 stencilSpaceIBounds.offset(clipSpaceToStencilOffset); 593 GrFixedClip clip(stencilSpaceIBounds); 594 595 bool insideClip = InitialState::kAllIn == reducedClip.initialState(); 596 drawContext->drawContextPriv().clearStencilClip(stencilSpaceIBounds, insideClip); 597 598 // walk through each clip element and perform its set op 599 // with the existing clip. 600 for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) { 601 const Element* element = iter.get(); 602 bool useHWAA = element->isAA() && drawContext->isStencilBufferMultisampled(); 603 604 bool fillInverted = false; 605 // enabled at bottom of loop 606 clip.disableStencilClip(); 607 608 // This will be used to determine whether the clip shape can be rendered into the 609 // stencil with arbitrary stencil settings. 610 GrPathRenderer::StencilSupport stencilSupport; 611 612 SkRegion::Op op = element->getOp(); 613 614 GrPathRenderer* pr = nullptr; 615 SkPath clipPath; 616 if (Element::kRect_Type == element->getType()) { 617 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; 618 fillInverted = false; 619 } else { 620 element->asPath(&clipPath); 621 fillInverted = clipPath.isInverseFillType(); 622 if (fillInverted) { 623 clipPath.toggleInverseFillType(); 624 } 625 626 GrShape shape(clipPath, GrStyle::SimpleFill()); 627 GrPathRenderer::CanDrawPathArgs canDrawArgs; 628 canDrawArgs.fShaderCaps = context->caps()->shaderCaps(); 629 canDrawArgs.fViewMatrix = &viewMatrix; 630 canDrawArgs.fShape = &shape; 631 canDrawArgs.fAntiAlias = false; 632 canDrawArgs.fHasUserStencilSettings = false; 633 canDrawArgs.fIsStencilBufferMSAA = drawContext->isStencilBufferMultisampled(); 634 635 GrDrawingManager* dm = context->drawingManager(); 636 pr = dm->getPathRenderer(canDrawArgs, false, 637 GrPathRendererChain::kStencilOnly_DrawType, 638 &stencilSupport); 639 if (!pr) { 640 return false; 641 } 642 } 643 644 bool canRenderDirectToStencil = 645 GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; 646 bool drawDirectToClip; // Given the renderer, the element, 647 // fill rule, and set operation should 648 // we render the element directly to 649 // stencil bit used for clipping. 650 GrUserStencilSettings const* const* stencilPasses = 651 GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted, 652 &drawDirectToClip); 653 654 // draw the element to the client stencil bits if necessary 655 if (!drawDirectToClip) { 656 static constexpr GrUserStencilSettings kDrawToStencil( 657 GrUserStencilSettings::StaticInit< 658 0x0000, 659 GrUserStencilTest::kAlways, 660 0xffff, 661 GrUserStencilOp::kIncMaybeClamp, 662 GrUserStencilOp::kIncMaybeClamp, 663 0xffff>() 664 ); 665 if (Element::kRect_Type == element->getType()) { 666 drawContext->drawContextPriv().stencilRect(clip, &kDrawToStencil, useHWAA, 667 viewMatrix, element->getRect()); 668 } else { 669 if (!clipPath.isEmpty()) { 670 GrShape shape(clipPath, GrStyle::SimpleFill()); 671 if (canRenderDirectToStencil) { 672 GrPaint paint; 673 paint.setXPFactory(GrDisableColorXPFactory::Make()); 674 paint.setAntiAlias(element->isAA()); 675 676 GrPathRenderer::DrawPathArgs args; 677 args.fResourceProvider = context->resourceProvider(); 678 args.fPaint = &paint; 679 args.fUserStencilSettings = &kDrawToStencil; 680 args.fDrawContext = drawContext; 681 args.fClip = &clip; 682 args.fViewMatrix = &viewMatrix; 683 args.fShape = &shape; 684 args.fAntiAlias = false; 685 args.fGammaCorrect = false; 686 pr->drawPath(args); 687 } else { 688 GrPathRenderer::StencilPathArgs args; 689 args.fResourceProvider = context->resourceProvider(); 690 args.fDrawContext = drawContext; 691 args.fClip = &clip; 692 args.fViewMatrix = &viewMatrix; 693 args.fIsAA = element->isAA(); 694 args.fShape = &shape; 695 pr->stencilPath(args); 696 } 697 } 698 } 699 } 700 701 // Just enable stencil clip. The passes choose whether or not they will actually use it. 702 clip.enableStencilClip(); 703 704 // now we modify the clip bit by rendering either the clip 705 // element directly or a bounding rect of the entire clip. 706 for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) { 707 if (drawDirectToClip) { 708 if (Element::kRect_Type == element->getType()) { 709 drawContext->drawContextPriv().stencilRect(clip, *pass, useHWAA, viewMatrix, 710 element->getRect()); 711 } else { 712 GrShape shape(clipPath, GrStyle::SimpleFill()); 713 GrPaint paint; 714 paint.setXPFactory(GrDisableColorXPFactory::Make()); 715 paint.setAntiAlias(element->isAA()); 716 GrPathRenderer::DrawPathArgs args; 717 args.fResourceProvider = context->resourceProvider(); 718 args.fPaint = &paint; 719 args.fUserStencilSettings = *pass; 720 args.fDrawContext = drawContext; 721 args.fClip = &clip; 722 args.fViewMatrix = &viewMatrix; 723 args.fShape = &shape; 724 args.fAntiAlias = false; 725 args.fGammaCorrect = false; 726 pr->drawPath(args); 727 } 728 } else { 729 // The view matrix is setup to do clip space -> stencil space translation, so 730 // draw rect in clip space. 731 drawContext->drawContextPriv().stencilRect(clip, *pass, false, viewMatrix, 732 SkRect::Make(reducedClip.ibounds())); 733 } 734 } 735 } 736 } 737 return true; 738} 739 740//////////////////////////////////////////////////////////////////////////////// 741sk_sp<GrTexture> GrClipStackClip::CreateSoftwareClipMask(GrTextureProvider* texProvider, 742 const GrReducedClip& reducedClip, 743 const SkVector& clipToMaskOffset) { 744 GrUniqueKey key; 745 GetClipMaskKey(reducedClip.elementsGenID(), reducedClip.ibounds(), &key); 746 if (GrTexture* texture = texProvider->findAndRefTextureByUniqueKey(key)) { 747 return sk_sp<GrTexture>(texture); 748 } 749 750 // The mask texture may be larger than necessary. We round out the clip space bounds and pin 751 // the top left corner of the resulting rect to the top left of the texture. 752 SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height()); 753 754 GrSWMaskHelper helper(texProvider); 755 756 // Set the matrix so that rendered clip elements are transformed to mask space from clip 757 // space. 758 SkMatrix translate; 759 translate.setTranslate(clipToMaskOffset); 760 761 helper.init(maskSpaceIBounds, &translate); 762 helper.clear(InitialState::kAllIn == reducedClip.initialState() ? 0xFF : 0x00); 763 764 for (ElementList::Iter iter(reducedClip.elements()); iter.get(); iter.next()) { 765 const Element* element = iter.get(); 766 SkRegion::Op op = element->getOp(); 767 768 if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 769 // Intersect and reverse difference require modifying pixels outside of the geometry 770 // that is being "drawn". In both cases we erase all the pixels outside of the geometry 771 // but leave the pixels inside the geometry alone. For reverse difference we invert all 772 // the pixels before clearing the ones outside the geometry. 773 if (SkRegion::kReverseDifference_Op == op) { 774 SkRect temp = SkRect::Make(reducedClip.ibounds()); 775 // invert the entire scene 776 helper.drawRect(temp, SkRegion::kXOR_Op, false, 0xFF); 777 } 778 SkPath clipPath; 779 element->asPath(&clipPath); 780 clipPath.toggleInverseFillType(); 781 GrShape shape(clipPath, GrStyle::SimpleFill()); 782 helper.drawShape(shape, SkRegion::kReplace_Op, element->isAA(), 0x00); 783 continue; 784 } 785 786 // The other ops (union, xor, diff) only affect pixels inside 787 // the geometry so they can just be drawn normally 788 if (Element::kRect_Type == element->getType()) { 789 helper.drawRect(element->getRect(), op, element->isAA(), 0xFF); 790 } else { 791 SkPath path; 792 element->asPath(&path); 793 GrShape shape(path, GrStyle::SimpleFill()); 794 helper.drawShape(shape, op, element->isAA(), 0xFF); 795 } 796 } 797 798 // Allocate clip mask texture 799 GrSurfaceDesc desc; 800 desc.fWidth = reducedClip.width(); 801 desc.fHeight = reducedClip.height(); 802 desc.fConfig = kAlpha_8_GrPixelConfig; 803 804 sk_sp<GrTexture> result(texProvider->createApproxTexture(desc)); 805 if (!result) { 806 return nullptr; 807 } 808 result->resourcePriv().setUniqueKey(key); 809 810 helper.toTexture(result.get()); 811 812 return result; 813} 814