1 2/* 3 * Copyright 2012 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9#include "GrClipMaskManager.h" 10#include "GrAAConvexPathRenderer.h" 11#include "GrAAHairLinePathRenderer.h" 12#include "GrAARectRenderer.h" 13#include "GrDrawTargetCaps.h" 14#include "GrGpu.h" 15#include "GrPaint.h" 16#include "GrPathRenderer.h" 17#include "GrRenderTarget.h" 18#include "GrStencilBuffer.h" 19#include "GrSWMaskHelper.h" 20#include "effects/GrTextureDomain.h" 21#include "SkRasterClip.h" 22#include "SkStrokeRec.h" 23#include "SkTLazy.h" 24 25#define GR_AA_CLIP 1 26 27typedef SkClipStack::Element Element; 28 29using namespace GrReducedClip; 30 31//////////////////////////////////////////////////////////////////////////////// 32namespace { 33// set up the draw state to enable the aa clipping mask. Besides setting up the 34// stage matrix this also alters the vertex layout 35void setup_drawstate_aaclip(GrGpu* gpu, 36 GrTexture* result, 37 const SkIRect &devBound) { 38 GrDrawState* drawState = gpu->drawState(); 39 SkASSERT(drawState); 40 41 SkMatrix mat; 42 // We want to use device coords to compute the texture coordinates. We set our matrix to be 43 // equal to the view matrix followed by an offset to the devBound, and then a scaling matrix to 44 // normalized coords. We apply this matrix to the vertex positions rather than local coords. 45 mat.setIDiv(result->width(), result->height()); 46 mat.preTranslate(SkIntToScalar(-devBound.fLeft), 47 SkIntToScalar(-devBound.fTop)); 48 mat.preConcat(drawState->getViewMatrix()); 49 50 SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height()); 51 // This could be a long-lived effect that is cached with the alpha-mask. 52 drawState->addCoverageEffect( 53 GrTextureDomainEffect::Create(result, 54 mat, 55 GrTextureDomain::MakeTexelDomain(result, domainTexels), 56 GrTextureDomain::kDecal_Mode, 57 GrTextureParams::kNone_FilterMode, 58 kPosition_GrCoordSet))->unref(); 59} 60 61bool path_needs_SW_renderer(GrContext* context, 62 GrGpu* gpu, 63 const SkPath& origPath, 64 const SkStrokeRec& stroke, 65 bool doAA) { 66 // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer 67 SkTCopyOnFirstWrite<SkPath> path(origPath); 68 if (path->isInverseFillType()) { 69 path.writable()->toggleInverseFillType(); 70 } 71 // last (false) parameter disallows use of the SW path renderer 72 GrPathRendererChain::DrawType type = doAA ? 73 GrPathRendererChain::kColorAntiAlias_DrawType : 74 GrPathRendererChain::kColor_DrawType; 75 76 return NULL == context->getPathRenderer(*path, stroke, gpu, false, type); 77} 78 79} 80 81/* 82 * This method traverses the clip stack to see if the GrSoftwarePathRenderer 83 * will be used on any element. If so, it returns true to indicate that the 84 * entire clip should be rendered in SW and then uploaded en masse to the gpu. 85 */ 86bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) { 87 88 // TODO: generalize this function so that when 89 // a clip gets complex enough it can just be done in SW regardless 90 // of whether it would invoke the GrSoftwarePathRenderer. 91 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 92 93 for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { 94 const Element* element = iter.get(); 95 // rects can always be drawn directly w/o using the software path 96 // so only paths need to be checked 97 if (Element::kPath_Type == element->getType() && 98 path_needs_SW_renderer(this->getContext(), fGpu, 99 element->getPath(), 100 stroke, 101 element->isAA())) { 102 return true; 103 } 104 } 105 return false; 106} 107 108//////////////////////////////////////////////////////////////////////////////// 109// sort out what kind of clip mask needs to be created: alpha, stencil, 110// scissor, or entirely software 111bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn, 112 GrDrawState::AutoRestoreEffects* are) { 113 fCurrClipMaskType = kNone_ClipMaskType; 114 115 ElementList elements(16); 116 int32_t genID; 117 InitialState initialState; 118 SkIRect clipSpaceIBounds; 119 bool requiresAA; 120 bool isRect = false; 121 122 GrDrawState* drawState = fGpu->drawState(); 123 124 const GrRenderTarget* rt = drawState->getRenderTarget(); 125 // GrDrawTarget should have filtered this for us 126 SkASSERT(NULL != rt); 127 128 bool ignoreClip = !drawState->isClipState() || clipDataIn->fClipStack->isWideOpen(); 129 130 if (!ignoreClip) { 131 SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height()); 132 clipSpaceRTIBounds.offset(clipDataIn->fOrigin); 133 ReduceClipStack(*clipDataIn->fClipStack, 134 clipSpaceRTIBounds, 135 &elements, 136 &genID, 137 &initialState, 138 &clipSpaceIBounds, 139 &requiresAA); 140 if (elements.isEmpty()) { 141 if (kAllIn_InitialState == initialState) { 142 ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds; 143 isRect = true; 144 } else { 145 return false; 146 } 147 } 148 } 149 150 if (ignoreClip) { 151 fGpu->disableScissor(); 152 this->setGpuStencil(); 153 return true; 154 } 155 156#if GR_AA_CLIP 157 // TODO: catch isRect && requiresAA and use clip planes if available rather than a mask. 158 159 // If MSAA is enabled we can do everything in the stencil buffer. 160 if (0 == rt->numSamples() && requiresAA) { 161 GrTexture* result = NULL; 162 163 if (this->useSWOnlyPath(elements)) { 164 // The clip geometry is complex enough that it will be more efficient to create it 165 // entirely in software 166 result = this->createSoftwareClipMask(genID, 167 initialState, 168 elements, 169 clipSpaceIBounds); 170 } else { 171 result = this->createAlphaClipMask(genID, 172 initialState, 173 elements, 174 clipSpaceIBounds); 175 } 176 177 if (NULL != result) { 178 // The mask's top left coord should be pinned to the rounded-out top left corner of 179 // clipSpace bounds. We determine the mask's position WRT to the render target here. 180 SkIRect rtSpaceMaskBounds = clipSpaceIBounds; 181 rtSpaceMaskBounds.offset(-clipDataIn->fOrigin); 182 are->set(fGpu->drawState()); 183 setup_drawstate_aaclip(fGpu, result, rtSpaceMaskBounds); 184 fGpu->disableScissor(); 185 this->setGpuStencil(); 186 return true; 187 } 188 // if alpha clip mask creation fails fall through to the non-AA code paths 189 } 190#endif // GR_AA_CLIP 191 192 // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't 193 // be created. In either case, free up the texture in the anti-aliased mask cache. 194 // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g., 195 // clears, InOrderDrawBuffer playbacks) that hit the stencil buffer path. These may be 196 // "incorrectly" clearing the AA cache. 197 fAACache.reset(); 198 199 // If the clip is a rectangle then just set the scissor. Otherwise, create 200 // a stencil mask. 201 if (isRect) { 202 SkIRect clipRect = clipSpaceIBounds; 203 clipRect.offset(-clipDataIn->fOrigin); 204 fGpu->enableScissor(clipRect); 205 this->setGpuStencil(); 206 return true; 207 } 208 209 // use the stencil clip if we can't represent the clip as a rectangle. 210 SkIPoint clipSpaceToStencilSpaceOffset = -clipDataIn->fOrigin; 211 this->createStencilClipMask(genID, 212 initialState, 213 elements, 214 clipSpaceIBounds, 215 clipSpaceToStencilSpaceOffset); 216 217 // This must occur after createStencilClipMask. That function may change the scissor. Also, it 218 // only guarantees that the stencil mask is correct within the bounds it was passed, so we must 219 // use both stencil and scissor test to the bounds for the final draw. 220 SkIRect scissorSpaceIBounds(clipSpaceIBounds); 221 scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset); 222 fGpu->enableScissor(scissorSpaceIBounds); 223 this->setGpuStencil(); 224 return true; 225} 226 227#define VISUALIZE_COMPLEX_CLIP 0 228 229#if VISUALIZE_COMPLEX_CLIP 230 #include "SkRandom.h" 231 SkRandom gRandom; 232 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU()); 233#else 234 #define SET_RANDOM_COLOR 235#endif 236 237namespace { 238 239//////////////////////////////////////////////////////////////////////////////// 240// set up the OpenGL blend function to perform the specified 241// boolean operation for alpha clip mask creation 242void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) { 243 244 switch (op) { 245 case SkRegion::kReplace_Op: 246 drawState->setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff); 247 break; 248 case SkRegion::kIntersect_Op: 249 drawState->setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff); 250 break; 251 case SkRegion::kUnion_Op: 252 drawState->setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff); 253 break; 254 case SkRegion::kXOR_Op: 255 drawState->setBlendFunc(kIDC_GrBlendCoeff, kISC_GrBlendCoeff); 256 break; 257 case SkRegion::kDifference_Op: 258 drawState->setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff); 259 break; 260 case SkRegion::kReverseDifference_Op: 261 drawState->setBlendFunc(kIDC_GrBlendCoeff, kZero_GrBlendCoeff); 262 break; 263 default: 264 SkASSERT(false); 265 break; 266 } 267} 268 269} 270 271//////////////////////////////////////////////////////////////////////////////// 272bool GrClipMaskManager::drawElement(GrTexture* target, 273 const SkClipStack::Element* element, 274 GrPathRenderer* pr) { 275 GrDrawState* drawState = fGpu->drawState(); 276 277 drawState->setRenderTarget(target->asRenderTarget()); 278 279 switch (element->getType()) { 280 case Element::kRect_Type: 281 // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the 282 // entire mask bounds and writes 0 outside the rect. 283 if (element->isAA()) { 284 getContext()->getAARectRenderer()->fillAARect(fGpu, 285 fGpu, 286 element->getRect(), 287 SkMatrix::I(), 288 element->getRect(), 289 false); 290 } else { 291 fGpu->drawSimpleRect(element->getRect(), NULL); 292 } 293 return true; 294 case Element::kPath_Type: { 295 SkTCopyOnFirstWrite<SkPath> path(element->getPath()); 296 if (path->isInverseFillType()) { 297 path.writable()->toggleInverseFillType(); 298 } 299 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 300 if (NULL == pr) { 301 GrPathRendererChain::DrawType type; 302 type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType : 303 GrPathRendererChain::kColor_DrawType; 304 pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type); 305 } 306 if (NULL == pr) { 307 return false; 308 } 309 pr->drawPath(element->getPath(), stroke, fGpu, element->isAA()); 310 break; 311 } 312 default: 313 // something is wrong if we're trying to draw an empty element. 314 GrCrash("Unexpected element type"); 315 return false; 316 } 317 return true; 318} 319 320bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target, 321 const SkClipStack::Element* element, 322 GrPathRenderer** pr) { 323 GrDrawState* drawState = fGpu->drawState(); 324 drawState->setRenderTarget(target->asRenderTarget()); 325 326 switch (element->getType()) { 327 case Element::kRect_Type: 328 return true; 329 case Element::kPath_Type: { 330 SkTCopyOnFirstWrite<SkPath> path(element->getPath()); 331 if (path->isInverseFillType()) { 332 path.writable()->toggleInverseFillType(); 333 } 334 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 335 GrPathRendererChain::DrawType type = element->isAA() ? 336 GrPathRendererChain::kStencilAndColorAntiAlias_DrawType : 337 GrPathRendererChain::kStencilAndColor_DrawType; 338 *pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type); 339 return NULL != *pr; 340 } 341 default: 342 // something is wrong if we're trying to draw an empty element. 343 GrCrash("Unexpected element type"); 344 return false; 345 } 346} 347 348void GrClipMaskManager::mergeMask(GrTexture* dstMask, 349 GrTexture* srcMask, 350 SkRegion::Op op, 351 const SkIRect& dstBound, 352 const SkIRect& srcBound) { 353 GrDrawState::AutoViewMatrixRestore avmr; 354 GrDrawState* drawState = fGpu->drawState(); 355 SkAssertResult(avmr.setIdentity(drawState)); 356 GrDrawState::AutoRestoreEffects are(drawState); 357 358 drawState->setRenderTarget(dstMask->asRenderTarget()); 359 360 setup_boolean_blendcoeffs(drawState, op); 361 362 SkMatrix sampleM; 363 sampleM.setIDiv(srcMask->width(), srcMask->height()); 364 365 drawState->addColorEffect( 366 GrTextureDomainEffect::Create(srcMask, 367 sampleM, 368 GrTextureDomain::MakeTexelDomain(srcMask, srcBound), 369 GrTextureDomain::kDecal_Mode, 370 GrTextureParams::kNone_FilterMode))->unref(); 371 fGpu->drawSimpleRect(SkRect::Make(dstBound), NULL); 372} 373 374// get a texture to act as a temporary buffer for AA clip boolean operations 375// TODO: given the expense of createTexture we may want to just cache this too 376void GrClipMaskManager::getTemp(int width, int height, GrAutoScratchTexture* temp) { 377 if (NULL != temp->texture()) { 378 // we've already allocated the temp texture 379 return; 380 } 381 382 GrTextureDesc desc; 383 desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit; 384 desc.fWidth = width; 385 desc.fHeight = height; 386 desc.fConfig = kAlpha_8_GrPixelConfig; 387 388 temp->set(this->getContext(), desc); 389} 390 391//////////////////////////////////////////////////////////////////////////////// 392// Handles caching & allocation (if needed) of a clip alpha-mask texture for both the sw-upload 393// or gpu-rendered cases. Returns true if there is no more work to be done (i.e., we got a cache 394// hit) 395bool GrClipMaskManager::getMaskTexture(int32_t elementsGenID, 396 const SkIRect& clipSpaceIBounds, 397 GrTexture** result, 398 bool willUpload) { 399 bool cached = fAACache.canReuse(elementsGenID, clipSpaceIBounds); 400 if (!cached) { 401 402 // There isn't a suitable entry in the cache so we create a new texture to store the mask. 403 // Since we are setting up the cache we know the last lookup was a miss. Free up the 404 // currently cached mask so it can be reused. 405 fAACache.reset(); 406 407 GrTextureDesc desc; 408 desc.fFlags = willUpload ? kNone_GrTextureFlags : kRenderTarget_GrTextureFlagBit; 409 desc.fWidth = clipSpaceIBounds.width(); 410 desc.fHeight = clipSpaceIBounds.height(); 411 desc.fConfig = kRGBA_8888_GrPixelConfig; 412 if (willUpload || this->getContext()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) { 413 // We would always like A8 but it isn't supported on all platforms 414 desc.fConfig = kAlpha_8_GrPixelConfig; 415 } 416 417 fAACache.acquireMask(elementsGenID, desc, clipSpaceIBounds); 418 } 419 420 *result = fAACache.getLastMask(); 421 return cached; 422} 423 424//////////////////////////////////////////////////////////////////////////////// 425// Create a 8-bit clip mask in alpha 426GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID, 427 InitialState initialState, 428 const ElementList& elements, 429 const SkIRect& clipSpaceIBounds) { 430 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 431 432 GrTexture* result; 433 if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, false)) { 434 fCurrClipMaskType = kAlpha_ClipMaskType; 435 return result; 436 } 437 438 if (NULL == result) { 439 fAACache.reset(); 440 return NULL; 441 } 442 443 // The top-left of the mask corresponds to the top-left corner of the bounds. 444 SkVector clipToMaskOffset = { 445 SkIntToScalar(-clipSpaceIBounds.fLeft), 446 SkIntToScalar(-clipSpaceIBounds.fTop) 447 }; 448 // The texture may be larger than necessary, this rect represents the part of the texture 449 // we populate with a rasterization of the clip. 450 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); 451 452 // Set the matrix so that rendered clip elements are transformed to mask space from clip space. 453 SkMatrix translate; 454 translate.setTranslate(clipToMaskOffset); 455 GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &translate); 456 457 GrDrawState* drawState = fGpu->drawState(); 458 459 // We're drawing a coverage mask and want coverage to be run through the blend function. 460 drawState->enableState(GrDrawState::kCoverageDrawing_StateBit); 461 462 // The scratch texture that we are drawing into can be substantially larger than the mask. Only 463 // clear the part that we care about. 464 fGpu->clear(&maskSpaceIBounds, 465 kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000, 466 true, 467 result->asRenderTarget()); 468 469 // When we use the stencil in the below loop it is important to have this clip installed. 470 // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first 471 // pass must not set values outside of this bounds or stencil values outside the rect won't be 472 // cleared. 473 GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds); 474 drawState->enableState(GrDrawState::kClip_StateBit); 475 476 GrAutoScratchTexture temp; 477 // walk through each clip element and perform its set op 478 for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) { 479 const Element* element = iter.get(); 480 SkRegion::Op op = element->getOp(); 481 bool invert = element->isInverseFilled(); 482 483 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 484 GrPathRenderer* pr = NULL; 485 bool useTemp = !this->canStencilAndDrawElement(result, element, &pr); 486 GrTexture* dst; 487 // This is the bounds of the clip element in the space of the alpha-mask. The temporary 488 // mask buffer can be substantially larger than the actually clip stack element. We 489 // touch the minimum number of pixels necessary and use decal mode to combine it with 490 // the accumulator. 491 SkIRect maskSpaceElementIBounds; 492 493 if (useTemp) { 494 if (invert) { 495 maskSpaceElementIBounds = maskSpaceIBounds; 496 } else { 497 SkRect elementBounds = element->getBounds(); 498 elementBounds.offset(clipToMaskOffset); 499 elementBounds.roundOut(&maskSpaceElementIBounds); 500 } 501 502 this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp); 503 if (NULL == temp.texture()) { 504 fAACache.reset(); 505 return NULL; 506 } 507 dst = temp.texture(); 508 // clear the temp target and set blend to replace 509 fGpu->clear(&maskSpaceElementIBounds, 510 invert ? 0xffffffff : 0x00000000, 511 true, 512 dst->asRenderTarget()); 513 setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op); 514 515 } else { 516 // draw directly into the result with the stencil set to make the pixels affected 517 // by the clip shape be non-zero. 518 dst = result; 519 GR_STATIC_CONST_SAME_STENCIL(kStencilInElement, 520 kReplace_StencilOp, 521 kReplace_StencilOp, 522 kAlways_StencilFunc, 523 0xffff, 524 0xffff, 525 0xffff); 526 drawState->setStencil(kStencilInElement); 527 setup_boolean_blendcoeffs(drawState, op); 528 } 529 530 drawState->setAlpha(invert ? 0x00 : 0xff); 531 532 if (!this->drawElement(dst, element, pr)) { 533 fAACache.reset(); 534 return NULL; 535 } 536 537 if (useTemp) { 538 // Now draw into the accumulator using the real operation and the temp buffer as a 539 // texture 540 this->mergeMask(result, 541 temp.texture(), 542 op, 543 maskSpaceIBounds, 544 maskSpaceElementIBounds); 545 } else { 546 // Draw to the exterior pixels (those with a zero stencil value). 547 drawState->setAlpha(invert ? 0xff : 0x00); 548 GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement, 549 kZero_StencilOp, 550 kZero_StencilOp, 551 kEqual_StencilFunc, 552 0xffff, 553 0x0000, 554 0xffff); 555 drawState->setStencil(kDrawOutsideElement); 556 fGpu->drawSimpleRect(clipSpaceIBounds); 557 drawState->disableStencil(); 558 } 559 } else { 560 // all the remaining ops can just be directly draw into the accumulation buffer 561 drawState->setAlpha(0xff); 562 setup_boolean_blendcoeffs(drawState, op); 563 this->drawElement(result, element); 564 } 565 } 566 567 fCurrClipMaskType = kAlpha_ClipMaskType; 568 return result; 569} 570 571//////////////////////////////////////////////////////////////////////////////// 572// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device 573// (as opposed to canvas) coordinates 574bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID, 575 InitialState initialState, 576 const ElementList& elements, 577 const SkIRect& clipSpaceIBounds, 578 const SkIPoint& clipSpaceToStencilOffset) { 579 580 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 581 582 GrDrawState* drawState = fGpu->drawState(); 583 SkASSERT(drawState->isClipState()); 584 585 GrRenderTarget* rt = drawState->getRenderTarget(); 586 SkASSERT(NULL != rt); 587 588 // TODO: dynamically attach a SB when needed. 589 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer(); 590 if (NULL == stencilBuffer) { 591 return false; 592 } 593 594 if (stencilBuffer->mustRenderClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset)) { 595 596 stencilBuffer->setLastClip(elementsGenID, clipSpaceIBounds, clipSpaceToStencilOffset); 597 598 // Set the matrix so that rendered clip elements are transformed from clip to stencil space. 599 SkVector translate = { 600 SkIntToScalar(clipSpaceToStencilOffset.fX), 601 SkIntToScalar(clipSpaceToStencilOffset.fY) 602 }; 603 SkMatrix matrix; 604 matrix.setTranslate(translate); 605 GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit, &matrix); 606 drawState = fGpu->drawState(); 607 608 drawState->setRenderTarget(rt); 609 610 // We set the current clip to the bounds so that our recursive draws are scissored to them. 611 SkIRect stencilSpaceIBounds(clipSpaceIBounds); 612 stencilSpaceIBounds.offset(clipSpaceToStencilOffset); 613 GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds); 614 drawState->enableState(GrDrawState::kClip_StateBit); 615 616#if !VISUALIZE_COMPLEX_CLIP 617 drawState->enableState(GrDrawState::kNoColorWrites_StateBit); 618#endif 619 620 int clipBit = stencilBuffer->bits(); 621 SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers"); 622 clipBit = (1 << (clipBit-1)); 623 624 fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState); 625 626 // walk through each clip element and perform its set op 627 // with the existing clip. 628 for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) { 629 const Element* element = iter.get(); 630 bool fillInverted = false; 631 // enabled at bottom of loop 632 drawState->disableState(GrGpu::kModifyStencilClip_StateBit); 633 // if the target is MSAA then we want MSAA enabled when the clip is soft 634 if (rt->isMultisampled()) { 635 drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA()); 636 } 637 638 // This will be used to determine whether the clip shape can be rendered into the 639 // stencil with arbitrary stencil settings. 640 GrPathRenderer::StencilSupport stencilSupport; 641 642 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 643 644 SkRegion::Op op = element->getOp(); 645 646 GrPathRenderer* pr = NULL; 647 SkTCopyOnFirstWrite<SkPath> clipPath; 648 if (Element::kRect_Type == element->getType()) { 649 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; 650 fillInverted = false; 651 } else { 652 SkASSERT(Element::kPath_Type == element->getType()); 653 clipPath.init(element->getPath()); 654 fillInverted = clipPath->isInverseFillType(); 655 if (fillInverted) { 656 clipPath.writable()->toggleInverseFillType(); 657 } 658 pr = this->getContext()->getPathRenderer(*clipPath, 659 stroke, 660 fGpu, 661 false, 662 GrPathRendererChain::kStencilOnly_DrawType, 663 &stencilSupport); 664 if (NULL == pr) { 665 return false; 666 } 667 } 668 669 int passes; 670 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses]; 671 672 bool canRenderDirectToStencil = 673 GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport; 674 bool canDrawDirectToClip; // Given the renderer, the element, 675 // fill rule, and set operation can 676 // we render the element directly to 677 // stencil bit used for clipping. 678 canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, 679 canRenderDirectToStencil, 680 clipBit, 681 fillInverted, 682 &passes, 683 stencilSettings); 684 685 // draw the element to the client stencil bits if necessary 686 if (!canDrawDirectToClip) { 687 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil, 688 kIncClamp_StencilOp, 689 kIncClamp_StencilOp, 690 kAlways_StencilFunc, 691 0xffff, 692 0x0000, 693 0xffff); 694 SET_RANDOM_COLOR 695 if (Element::kRect_Type == element->getType()) { 696 *drawState->stencil() = gDrawToStencil; 697 fGpu->drawSimpleRect(element->getRect(), NULL); 698 } else { 699 SkASSERT(Element::kPath_Type == element->getType()); 700 if (!clipPath->isEmpty()) { 701 if (canRenderDirectToStencil) { 702 *drawState->stencil() = gDrawToStencil; 703 pr->drawPath(*clipPath, stroke, fGpu, false); 704 } else { 705 pr->stencilPath(*clipPath, stroke, fGpu); 706 } 707 } 708 } 709 } 710 711 // now we modify the clip bit by rendering either the clip 712 // element directly or a bounding rect of the entire clip. 713 drawState->enableState(GrGpu::kModifyStencilClip_StateBit); 714 for (int p = 0; p < passes; ++p) { 715 *drawState->stencil() = stencilSettings[p]; 716 if (canDrawDirectToClip) { 717 if (Element::kRect_Type == element->getType()) { 718 SET_RANDOM_COLOR 719 fGpu->drawSimpleRect(element->getRect(), NULL); 720 } else { 721 SkASSERT(Element::kPath_Type == element->getType()); 722 SET_RANDOM_COLOR 723 pr->drawPath(*clipPath, stroke, fGpu, false); 724 } 725 } else { 726 SET_RANDOM_COLOR 727 // The view matrix is setup to do clip space -> stencil space translation, so 728 // draw rect in clip space. 729 fGpu->drawSimpleRect(SkRect::Make(clipSpaceIBounds), NULL); 730 } 731 } 732 } 733 } 734 // set this last because recursive draws may overwrite it back to kNone. 735 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 736 fCurrClipMaskType = kStencil_ClipMaskType; 737 return true; 738} 739 740 741// mapping of clip-respecting stencil funcs to normal stencil funcs 742// mapping depends on whether stencil-clipping is in effect. 743static const GrStencilFunc 744 gSpecialToBasicStencilFunc[2][kClipStencilFuncCount] = { 745 {// Stencil-Clipping is DISABLED, we are effectively always inside the clip 746 // In the Clip Funcs 747 kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc 748 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc 749 kLess_StencilFunc, // kLessIfInClip_StencilFunc 750 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc 751 // Special in the clip func that forces user's ref to be 0. 752 kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc 753 // make ref 0 and do normal nequal. 754 }, 755 {// Stencil-Clipping is ENABLED 756 // In the Clip Funcs 757 kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc 758 // eq stencil clip bit, mask 759 // out user bits. 760 761 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc 762 // add stencil bit to mask and ref 763 764 kLess_StencilFunc, // kLessIfInClip_StencilFunc 765 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc 766 // for both of these we can add 767 // the clip bit to the mask and 768 // ref and compare as normal 769 // Special in the clip func that forces user's ref to be 0. 770 kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc 771 // make ref have only the clip bit set 772 // and make comparison be less 773 // 10..0 < 1..user_bits.. 774 } 775}; 776 777namespace { 778// Sets the settings to clip against the stencil buffer clip while ignoring the 779// client bits. 780const GrStencilSettings& basic_apply_stencil_clip_settings() { 781 // stencil settings to use when clip is in stencil 782 GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings, 783 kKeep_StencilOp, 784 kKeep_StencilOp, 785 kAlwaysIfInClip_StencilFunc, 786 0x0000, 787 0x0000, 788 0x0000); 789 return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings); 790} 791} 792 793void GrClipMaskManager::setGpuStencil() { 794 // We make two copies of the StencilSettings here (except in the early 795 // exit scenario. One copy from draw state to the stack var. Then another 796 // from the stack var to the gpu. We could make this class hold a ptr to 797 // GrGpu's fStencilSettings and eliminate the stack copy here. 798 799 const GrDrawState& drawState = fGpu->getDrawState(); 800 801 // use stencil for clipping if clipping is enabled and the clip 802 // has been written into the stencil. 803 GrClipMaskManager::StencilClipMode clipMode; 804 if (this->isClipInStencil() && drawState.isClipState()) { 805 clipMode = GrClipMaskManager::kRespectClip_StencilClipMode; 806 // We can't be modifying the clip and respecting it at the same time. 807 SkASSERT(!drawState.isStateFlagEnabled( 808 GrGpu::kModifyStencilClip_StateBit)); 809 } else if (drawState.isStateFlagEnabled( 810 GrGpu::kModifyStencilClip_StateBit)) { 811 clipMode = GrClipMaskManager::kModifyClip_StencilClipMode; 812 } else { 813 clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode; 814 } 815 816 GrStencilSettings settings; 817 // The GrGpu client may not be using the stencil buffer but we may need to 818 // enable it in order to respect a stencil clip. 819 if (drawState.getStencil().isDisabled()) { 820 if (GrClipMaskManager::kRespectClip_StencilClipMode == clipMode) { 821 settings = basic_apply_stencil_clip_settings(); 822 } else { 823 fGpu->disableStencil(); 824 return; 825 } 826 } else { 827 settings = drawState.getStencil(); 828 } 829 830 // TODO: dynamically attach a stencil buffer 831 int stencilBits = 0; 832 GrStencilBuffer* stencilBuffer = 833 drawState.getRenderTarget()->getStencilBuffer(); 834 if (NULL != stencilBuffer) { 835 stencilBits = stencilBuffer->bits(); 836 } 837 838 SkASSERT(fGpu->caps()->stencilWrapOpsSupport() || !settings.usesWrapOp()); 839 SkASSERT(fGpu->caps()->twoSidedStencilSupport() || !settings.isTwoSided()); 840 this->adjustStencilParams(&settings, clipMode, stencilBits); 841 fGpu->setStencilSettings(settings); 842} 843 844void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings, 845 StencilClipMode mode, 846 int stencilBitCnt) { 847 SkASSERT(stencilBitCnt > 0); 848 849 if (kModifyClip_StencilClipMode == mode) { 850 // We assume that this clip manager itself is drawing to the GrGpu and 851 // has already setup the correct values. 852 return; 853 } 854 855 unsigned int clipBit = (1 << (stencilBitCnt - 1)); 856 unsigned int userBits = clipBit - 1; 857 858 GrStencilSettings::Face face = GrStencilSettings::kFront_Face; 859 bool twoSided = fGpu->caps()->twoSidedStencilSupport(); 860 861 bool finished = false; 862 while (!finished) { 863 GrStencilFunc func = settings->func(face); 864 uint16_t writeMask = settings->writeMask(face); 865 uint16_t funcMask = settings->funcMask(face); 866 uint16_t funcRef = settings->funcRef(face); 867 868 SkASSERT((unsigned) func < kStencilFuncCount); 869 870 writeMask &= userBits; 871 872 if (func >= kBasicStencilFuncCount) { 873 int respectClip = kRespectClip_StencilClipMode == mode; 874 if (respectClip) { 875 // The GrGpu class should have checked this 876 SkASSERT(this->isClipInStencil()); 877 switch (func) { 878 case kAlwaysIfInClip_StencilFunc: 879 funcMask = clipBit; 880 funcRef = clipBit; 881 break; 882 case kEqualIfInClip_StencilFunc: 883 case kLessIfInClip_StencilFunc: 884 case kLEqualIfInClip_StencilFunc: 885 funcMask = (funcMask & userBits) | clipBit; 886 funcRef = (funcRef & userBits) | clipBit; 887 break; 888 case kNonZeroIfInClip_StencilFunc: 889 funcMask = (funcMask & userBits) | clipBit; 890 funcRef = clipBit; 891 break; 892 default: 893 GrCrash("Unknown stencil func"); 894 } 895 } else { 896 funcMask &= userBits; 897 funcRef &= userBits; 898 } 899 const GrStencilFunc* table = 900 gSpecialToBasicStencilFunc[respectClip]; 901 func = table[func - kBasicStencilFuncCount]; 902 SkASSERT(func >= 0 && func < kBasicStencilFuncCount); 903 } else { 904 funcMask &= userBits; 905 funcRef &= userBits; 906 } 907 908 settings->setFunc(face, func); 909 settings->setWriteMask(face, writeMask); 910 settings->setFuncMask(face, funcMask); 911 settings->setFuncRef(face, funcRef); 912 913 if (GrStencilSettings::kFront_Face == face) { 914 face = GrStencilSettings::kBack_Face; 915 finished = !twoSided; 916 } else { 917 finished = true; 918 } 919 } 920 if (!twoSided) { 921 settings->copyFrontSettingsToBack(); 922 } 923} 924 925//////////////////////////////////////////////////////////////////////////////// 926GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t elementsGenID, 927 GrReducedClip::InitialState initialState, 928 const GrReducedClip::ElementList& elements, 929 const SkIRect& clipSpaceIBounds) { 930 SkASSERT(kNone_ClipMaskType == fCurrClipMaskType); 931 932 GrTexture* result; 933 if (this->getMaskTexture(elementsGenID, clipSpaceIBounds, &result, true)) { 934 return result; 935 } 936 937 if (NULL == result) { 938 fAACache.reset(); 939 return NULL; 940 } 941 942 // The mask texture may be larger than necessary. We round out the clip space bounds and pin 943 // the top left corner of the resulting rect to the top left of the texture. 944 SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height()); 945 946 GrSWMaskHelper helper(this->getContext()); 947 948 SkMatrix matrix; 949 matrix.setTranslate(SkIntToScalar(-clipSpaceIBounds.fLeft), 950 SkIntToScalar(-clipSpaceIBounds.fTop)); 951 helper.init(maskSpaceIBounds, &matrix); 952 953 helper.clear(kAllIn_InitialState == initialState ? 0xFF : 0x00); 954 955 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); 956 957 for (ElementList::Iter iter(elements.headIter()) ; NULL != iter.get(); iter.next()) { 958 959 const Element* element = iter.get(); 960 SkRegion::Op op = element->getOp(); 961 962 if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) { 963 // Intersect and reverse difference require modifying pixels outside of the geometry 964 // that is being "drawn". In both cases we erase all the pixels outside of the geometry 965 // but leave the pixels inside the geometry alone. For reverse difference we invert all 966 // the pixels before clearing the ones outside the geometry. 967 if (SkRegion::kReverseDifference_Op == op) { 968 SkRect temp = SkRect::Make(clipSpaceIBounds); 969 // invert the entire scene 970 helper.draw(temp, SkRegion::kXOR_Op, false, 0xFF); 971 } 972 973 if (Element::kRect_Type == element->getType()) { 974 // convert the rect to a path so we can invert the fill 975 SkPath temp; 976 temp.addRect(element->getRect()); 977 temp.setFillType(SkPath::kInverseEvenOdd_FillType); 978 979 helper.draw(temp, stroke, SkRegion::kReplace_Op, 980 element->isAA(), 981 0x00); 982 } else { 983 SkASSERT(Element::kPath_Type == element->getType()); 984 SkPath clipPath = element->getPath(); 985 clipPath.toggleInverseFillType(); 986 helper.draw(clipPath, stroke, 987 SkRegion::kReplace_Op, 988 element->isAA(), 989 0x00); 990 } 991 992 continue; 993 } 994 995 // The other ops (union, xor, diff) only affect pixels inside 996 // the geometry so they can just be drawn normally 997 if (Element::kRect_Type == element->getType()) { 998 helper.draw(element->getRect(), op, element->isAA(), 0xFF); 999 } else { 1000 SkASSERT(Element::kPath_Type == element->getType()); 1001 helper.draw(element->getPath(), stroke, op, element->isAA(), 0xFF); 1002 } 1003 } 1004 1005 helper.toTexture(result); 1006 1007 fCurrClipMaskType = kAlpha_ClipMaskType; 1008 return result; 1009} 1010 1011//////////////////////////////////////////////////////////////////////////////// 1012void GrClipMaskManager::releaseResources() { 1013 fAACache.releaseResources(); 1014} 1015 1016void GrClipMaskManager::setGpu(GrGpu* gpu) { 1017 fGpu = gpu; 1018 fAACache.setContext(gpu->getContext()); 1019} 1020 1021void GrClipMaskManager::adjustPathStencilParams(GrStencilSettings* settings) { 1022 const GrDrawState& drawState = fGpu->getDrawState(); 1023 GrClipMaskManager::StencilClipMode clipMode; 1024 if (this->isClipInStencil() && drawState.isClipState()) { 1025 clipMode = GrClipMaskManager::kRespectClip_StencilClipMode; 1026 // We can't be modifying the clip and respecting it at the same time. 1027 SkASSERT(!drawState.isStateFlagEnabled( 1028 GrGpu::kModifyStencilClip_StateBit)); 1029 } else if (drawState.isStateFlagEnabled( 1030 GrGpu::kModifyStencilClip_StateBit)) { 1031 clipMode = GrClipMaskManager::kModifyClip_StencilClipMode; 1032 } else { 1033 clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode; 1034 } 1035 1036 // TODO: dynamically attach a stencil buffer 1037 int stencilBits = 0; 1038 GrStencilBuffer* stencilBuffer = 1039 drawState.getRenderTarget()->getStencilBuffer(); 1040 if (NULL != stencilBuffer) { 1041 stencilBits = stencilBuffer->bits(); 1042 this->adjustStencilParams(settings, clipMode, stencilBits); 1043 } 1044} 1045