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