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 #include "GrAppliedClip.h" 10 #include "GrContextPriv.h" 11 #include "GrDeferredProxyUploader.h" 12 #include "GrDrawingManager.h" 13 #include "GrFixedClip.h" 14 #include "GrGpuResourcePriv.h" 15 #include "GrProxyProvider.h" 16 #include "GrRenderTargetContextPriv.h" 17 #include "GrSWMaskHelper.h" 18 #include "GrShape.h" 19 #include "GrStencilAttachment.h" 20 #include "GrStyle.h" 21 #include "GrTextureProxy.h" 22 #include "SkClipOpPriv.h" 23 #include "SkMakeUnique.h" 24 #include "SkTaskGroup.h" 25 #include "SkTo.h" 26 #include "SkTraceEvent.h" 27 #include "effects/GrConvexPolyEffect.h" 28 #include "effects/GrRRectEffect.h" 29 #include "effects/GrTextureDomain.h" 30 31 typedef SkClipStack::Element Element; 32 typedef GrReducedClip::InitialState InitialState; 33 typedef GrReducedClip::ElementList ElementList; 34 35 const char GrClipStackClip::kMaskTestTag[] = "clip_mask"; 36 37 bool GrClipStackClip::quickContains(const SkRect& rect) const { 38 if (!fStack || fStack->isWideOpen()) { 39 return true; 40 } 41 return fStack->quickContains(rect); 42 } 43 44 bool GrClipStackClip::quickContains(const SkRRect& rrect) const { 45 if (!fStack || fStack->isWideOpen()) { 46 return true; 47 } 48 return fStack->quickContains(rrect); 49 } 50 51 bool GrClipStackClip::isRRect(const SkRect& origRTBounds, SkRRect* rr, GrAA* aa) const { 52 if (!fStack) { 53 return false; 54 } 55 const SkRect* rtBounds = &origRTBounds; 56 bool isAA; 57 if (fStack->isRRect(*rtBounds, rr, &isAA)) { 58 *aa = GrAA(isAA); 59 return true; 60 } 61 return false; 62 } 63 64 void GrClipStackClip::getConservativeBounds(int width, int height, SkIRect* devResult, 65 bool* isIntersectionOfRects) const { 66 if (!fStack) { 67 devResult->setXYWH(0, 0, width, height); 68 if (isIntersectionOfRects) { 69 *isIntersectionOfRects = true; 70 } 71 return; 72 } 73 SkRect devBounds; 74 fStack->getConservativeBounds(0, 0, width, height, &devBounds, isIntersectionOfRects); 75 devBounds.roundOut(devResult); 76 } 77 78 //////////////////////////////////////////////////////////////////////////////// 79 // set up the draw state to enable the aa clipping mask. 80 static std::unique_ptr<GrFragmentProcessor> create_fp_for_mask(sk_sp<GrTextureProxy> mask, 81 const SkIRect& devBound) { 82 SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height()); 83 return GrDeviceSpaceTextureDecalFragmentProcessor::Make(std::move(mask), domainTexels, 84 {devBound.fLeft, devBound.fTop}); 85 } 86 87 // Does the path in 'element' require SW rendering? If so, return true (and, 88 // optionally, set 'prOut' to NULL. If not, return false (and, optionally, set 89 // 'prOut' to the non-SW path renderer that will do the job). 90 bool GrClipStackClip::PathNeedsSWRenderer(GrContext* context, 91 const SkIRect& scissorRect, 92 bool hasUserStencilSettings, 93 const GrRenderTargetContext* renderTargetContext, 94 const SkMatrix& viewMatrix, 95 const Element* element, 96 GrPathRenderer** prOut, 97 bool needsStencil) { 98 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { 99 // rects can always be drawn directly w/o using the software path 100 // TODO: skip rrects once we're drawing them directly. 101 if (prOut) { 102 *prOut = nullptr; 103 } 104 return false; 105 } else { 106 // We shouldn't get here with an empty clip element. 107 SkASSERT(Element::DeviceSpaceType::kEmpty != element->getDeviceSpaceType()); 108 109 // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer 110 SkPath path; 111 element->asDeviceSpacePath(&path); 112 if (path.isInverseFillType()) { 113 path.toggleInverseFillType(); 114 } 115 116 GrPathRendererChain::DrawType type = 117 needsStencil ? GrPathRendererChain::DrawType::kStencilAndColor 118 : GrPathRendererChain::DrawType::kColor; 119 120 GrShape shape(path, GrStyle::SimpleFill()); 121 GrPathRenderer::CanDrawPathArgs canDrawArgs; 122 canDrawArgs.fCaps = context->contextPriv().caps(); 123 canDrawArgs.fClipConservativeBounds = &scissorRect; 124 canDrawArgs.fViewMatrix = &viewMatrix; 125 canDrawArgs.fShape = &shape; 126 canDrawArgs.fAAType = GrChooseAAType(GrAA(element->isAA()), 127 renderTargetContext->fsaaType(), 128 GrAllowMixedSamples::kYes, 129 *context->contextPriv().caps()); 130 SkASSERT(!renderTargetContext->wrapsVkSecondaryCB()); 131 canDrawArgs.fTargetIsWrappedVkSecondaryCB = false; 132 canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings; 133 134 // the 'false' parameter disallows use of the SW path renderer 135 GrPathRenderer* pr = 136 context->contextPriv().drawingManager()->getPathRenderer(canDrawArgs, false, type); 137 if (prOut) { 138 *prOut = pr; 139 } 140 return SkToBool(!pr); 141 } 142 } 143 144 /* 145 * This method traverses the clip stack to see if the GrSoftwarePathRenderer 146 * will be used on any element. If so, it returns true to indicate that the 147 * entire clip should be rendered in SW and then uploaded en masse to the gpu. 148 */ 149 bool GrClipStackClip::UseSWOnlyPath(GrContext* context, 150 bool hasUserStencilSettings, 151 const GrRenderTargetContext* renderTargetContext, 152 const GrReducedClip& reducedClip) { 153 // TODO: right now it appears that GPU clip masks are strictly slower than software. We may 154 // want to revisit this assumption once we can test with render target sorting. 155 return true; 156 157 // TODO: generalize this function so that when 158 // a clip gets complex enough it can just be done in SW regardless 159 // of whether it would invoke the GrSoftwarePathRenderer. 160 161 // If we're avoiding stencils, always use SW. This includes drawing into a wrapped vulkan 162 // secondary command buffer which can't handle stencils. 163 if (context->contextPriv().caps()->avoidStencilBuffers() || 164 renderTargetContext->wrapsVkSecondaryCB()) { 165 return true; 166 } 167 168 // Set the matrix so that rendered clip elements are transformed to mask space from clip 169 // space. 170 SkMatrix translate; 171 translate.setTranslate(SkIntToScalar(-reducedClip.left()), SkIntToScalar(-reducedClip.top())); 172 173 for (ElementList::Iter iter(reducedClip.maskElements()); iter.get(); iter.next()) { 174 const Element* element = iter.get(); 175 176 SkClipOp op = element->getOp(); 177 bool invert = element->isInverseFilled(); 178 bool needsStencil = invert || 179 kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op; 180 181 if (PathNeedsSWRenderer(context, reducedClip.scissor(), hasUserStencilSettings, 182 renderTargetContext, translate, element, nullptr, needsStencil)) { 183 return true; 184 } 185 } 186 return false; 187 } 188 189 //////////////////////////////////////////////////////////////////////////////// 190 // sort out what kind of clip mask needs to be created: alpha, stencil, 191 // scissor, or entirely software 192 bool GrClipStackClip::apply(GrContext* context, GrRenderTargetContext* renderTargetContext, 193 bool useHWAA, bool hasUserStencilSettings, GrAppliedClip* out, 194 SkRect* bounds) const { 195 SkRect devBounds = SkRect::MakeIWH(renderTargetContext->width(), renderTargetContext->height()); 196 if (!devBounds.intersect(*bounds)) { 197 return false; 198 } 199 200 if (!fStack || fStack->isWideOpen()) { 201 return true; 202 } 203 204 int maxWindowRectangles = renderTargetContext->priv().maxWindowRectangles(); 205 int maxAnalyticFPs = context->contextPriv().caps()->maxClipAnalyticFPs(); 206 if (GrFSAAType::kNone != renderTargetContext->fsaaType()) { 207 // With mixed samples (non-msaa color buffer), any coverage info is lost from color once it 208 // hits the color buffer anyway, so we may as well use coverage AA if nothing else in the 209 // pipe is multisampled. 210 if (renderTargetContext->numColorSamples() > 1 || useHWAA || hasUserStencilSettings) { 211 maxAnalyticFPs = 0; 212 } 213 // We disable MSAA when avoiding stencil. 214 SkASSERT(!context->contextPriv().caps()->avoidStencilBuffers()); 215 } 216 auto* ccpr = context->contextPriv().drawingManager()->getCoverageCountingPathRenderer(); 217 218 GrReducedClip reducedClip(*fStack, devBounds, context->contextPriv().caps(), 219 maxWindowRectangles, maxAnalyticFPs, ccpr ? maxAnalyticFPs : 0); 220 if (InitialState::kAllOut == reducedClip.initialState() && 221 reducedClip.maskElements().isEmpty()) { 222 return false; 223 } 224 225 if (reducedClip.hasScissor() && !GrClip::IsInsideClip(reducedClip.scissor(), devBounds)) { 226 out->hardClip().addScissor(reducedClip.scissor(), bounds); 227 } 228 229 if (!reducedClip.windowRectangles().empty()) { 230 out->hardClip().addWindowRectangles(reducedClip.windowRectangles(), 231 GrWindowRectsState::Mode::kExclusive); 232 } 233 234 if (!reducedClip.maskElements().isEmpty()) { 235 if (!this->applyClipMask(context, renderTargetContext, reducedClip, hasUserStencilSettings, 236 out)) { 237 return false; 238 } 239 } 240 241 // The opList ID must not be looked up until AFTER producing the clip mask (if any). That step 242 // can cause a flush or otherwise change which opList our draw is going into. 243 uint32_t opListID = renderTargetContext->getOpList()->uniqueID(); 244 int rtWidth = renderTargetContext->width(), rtHeight = renderTargetContext->height(); 245 if (auto clipFPs = reducedClip.finishAndDetachAnalyticFPs(ccpr, opListID, rtWidth, rtHeight)) { 246 out->addCoverageFP(std::move(clipFPs)); 247 } 248 249 return true; 250 } 251 252 bool GrClipStackClip::applyClipMask(GrContext* context, GrRenderTargetContext* renderTargetContext, 253 const GrReducedClip& reducedClip, bool hasUserStencilSettings, 254 GrAppliedClip* out) const { 255 #ifdef SK_DEBUG 256 SkASSERT(reducedClip.hasScissor()); 257 SkIRect rtIBounds = SkIRect::MakeWH(renderTargetContext->width(), 258 renderTargetContext->height()); 259 const SkIRect& scissor = reducedClip.scissor(); 260 SkASSERT(rtIBounds.contains(scissor)); // Mask shouldn't be larger than the RT. 261 #endif 262 263 // If the stencil buffer is multisampled we can use it to do everything. 264 if ((GrFSAAType::kNone == renderTargetContext->fsaaType() && reducedClip.maskRequiresAA()) || 265 context->contextPriv().caps()->avoidStencilBuffers() || 266 renderTargetContext->wrapsVkSecondaryCB()) { 267 sk_sp<GrTextureProxy> result; 268 if (UseSWOnlyPath(context, hasUserStencilSettings, renderTargetContext, reducedClip)) { 269 // The clip geometry is complex enough that it will be more efficient to create it 270 // entirely in software 271 result = this->createSoftwareClipMask(context, reducedClip, renderTargetContext); 272 } else { 273 result = this->createAlphaClipMask(context, reducedClip); 274 } 275 276 if (result) { 277 // The mask's top left coord should be pinned to the rounded-out top left corner of 278 // the clip's device space bounds. 279 out->addCoverageFP(create_fp_for_mask(std::move(result), reducedClip.scissor())); 280 return true; 281 } 282 283 // If alpha or software clip mask creation fails, fall through to the stencil code paths, 284 // unless stencils are disallowed. 285 if (context->contextPriv().caps()->avoidStencilBuffers() || 286 renderTargetContext->wrapsVkSecondaryCB()) { 287 SkDebugf("WARNING: Clip mask requires stencil, but stencil unavailable. " 288 "Clip will be ignored.\n"); 289 return false; 290 } 291 } 292 293 renderTargetContext->setNeedsStencil(); 294 295 // This relies on the property that a reduced sub-rect of the last clip will contain all the 296 // relevant window rectangles that were in the last clip. This subtle requirement will go away 297 // after clipping is overhauled. 298 if (renderTargetContext->priv().mustRenderClip(reducedClip.maskGenID(), reducedClip.scissor(), 299 reducedClip.numAnalyticFPs())) { 300 reducedClip.drawStencilClipMask(context, renderTargetContext); 301 renderTargetContext->priv().setLastClip(reducedClip.maskGenID(), reducedClip.scissor(), 302 reducedClip.numAnalyticFPs()); 303 } 304 // GrAppliedClip doesn't need to figure numAnalyticFPs into its key (used by operator==) because 305 // it verifies the FPs are also equal. 306 out->hardClip().addStencilClip(reducedClip.maskGenID()); 307 return true; 308 } 309 310 //////////////////////////////////////////////////////////////////////////////// 311 // Create a 8-bit clip mask in alpha 312 313 static void create_clip_mask_key(uint32_t clipGenID, const SkIRect& bounds, int numAnalyticFPs, 314 GrUniqueKey* key) { 315 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 316 GrUniqueKey::Builder builder(key, kDomain, 4, GrClipStackClip::kMaskTestTag); 317 builder[0] = clipGenID; 318 // SkToS16 because image filters outset layers to a size indicated by the filter, which can 319 // sometimes result in negative coordinates from device space. 320 builder[1] = SkToS16(bounds.fLeft) | (SkToS16(bounds.fRight) << 16); 321 builder[2] = SkToS16(bounds.fTop) | (SkToS16(bounds.fBottom) << 16); 322 builder[3] = numAnalyticFPs; 323 } 324 325 static void add_invalidate_on_pop_message(GrContext* context, 326 const SkClipStack& stack, uint32_t clipGenID, 327 const GrUniqueKey& clipMaskKey) { 328 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 329 330 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 331 while (const Element* element = iter.prev()) { 332 if (element->getGenID() == clipGenID) { 333 element->addResourceInvalidationMessage(proxyProvider, clipMaskKey); 334 return; 335 } 336 } 337 SkDEBUGFAIL("Gen ID was not found in stack."); 338 } 339 340 sk_sp<GrTextureProxy> GrClipStackClip::createAlphaClipMask(GrContext* context, 341 const GrReducedClip& reducedClip) const { 342 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 343 GrUniqueKey key; 344 create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), 345 reducedClip.numAnalyticFPs(), &key); 346 347 sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey( 348 key, kTopLeft_GrSurfaceOrigin)); 349 if (proxy) { 350 return proxy; 351 } 352 353 GrBackendFormat format = 354 context->contextPriv().caps()->getBackendFormatFromColorType(kAlpha_8_SkColorType); 355 sk_sp<GrRenderTargetContext> rtc( 356 context->contextPriv().makeDeferredRenderTargetContextWithFallback( 357 format, 358 SkBackingFit::kApprox, 359 reducedClip.width(), 360 reducedClip.height(), 361 kAlpha_8_GrPixelConfig, 362 nullptr, 1, 363 GrMipMapped::kNo, 364 kTopLeft_GrSurfaceOrigin)); 365 if (!rtc) { 366 return nullptr; 367 } 368 369 if (!reducedClip.drawAlphaClipMask(rtc.get())) { 370 return nullptr; 371 } 372 373 sk_sp<GrTextureProxy> result(rtc->asTextureProxyRef()); 374 if (!result) { 375 return nullptr; 376 } 377 378 SkASSERT(result->origin() == kTopLeft_GrSurfaceOrigin); 379 proxyProvider->assignUniqueKeyToProxy(key, result.get()); 380 add_invalidate_on_pop_message(context, *fStack, reducedClip.maskGenID(), key); 381 382 return result; 383 } 384 385 namespace { 386 387 /** 388 * Payload class for use with GrTDeferredProxyUploader. The clip mask code renders multiple 389 * elements, each storing their own AA setting (and already transformed into device space). This 390 * stores all of the information needed by the worker thread to draw all clip elements (see below, 391 * in createSoftwareClipMask). 392 */ 393 class ClipMaskData { 394 public: 395 ClipMaskData(const GrReducedClip& reducedClip) 396 : fScissor(reducedClip.scissor()) 397 , fInitialState(reducedClip.initialState()) { 398 for (ElementList::Iter iter(reducedClip.maskElements()); iter.get(); iter.next()) { 399 fElements.addToTail(*iter.get()); 400 } 401 } 402 403 const SkIRect& scissor() const { return fScissor; } 404 InitialState initialState() const { return fInitialState; } 405 const ElementList& elements() const { return fElements; } 406 407 private: 408 SkIRect fScissor; 409 InitialState fInitialState; 410 ElementList fElements; 411 }; 412 413 } 414 415 static void draw_clip_elements_to_mask_helper(GrSWMaskHelper& helper, const ElementList& elements, 416 const SkIRect& scissor, InitialState initialState) { 417 // Set the matrix so that rendered clip elements are transformed to mask space from clip space. 418 SkMatrix translate; 419 translate.setTranslate(SkIntToScalar(-scissor.left()), SkIntToScalar(-scissor.top())); 420 421 helper.clear(InitialState::kAllIn == initialState ? 0xFF : 0x00); 422 423 for (ElementList::Iter iter(elements); iter.get(); iter.next()) { 424 const Element* element = iter.get(); 425 SkClipOp op = element->getOp(); 426 GrAA aa = GrAA(element->isAA()); 427 428 if (kIntersect_SkClipOp == op || kReverseDifference_SkClipOp == op) { 429 // Intersect and reverse difference require modifying pixels outside of the geometry 430 // that is being "drawn". In both cases we erase all the pixels outside of the geometry 431 // but leave the pixels inside the geometry alone. For reverse difference we invert all 432 // the pixels before clearing the ones outside the geometry. 433 if (kReverseDifference_SkClipOp == op) { 434 SkRect temp = SkRect::Make(scissor); 435 // invert the entire scene 436 helper.drawRect(temp, translate, SkRegion::kXOR_Op, GrAA::kNo, 0xFF); 437 } 438 SkPath clipPath; 439 element->asDeviceSpacePath(&clipPath); 440 clipPath.toggleInverseFillType(); 441 GrShape shape(clipPath, GrStyle::SimpleFill()); 442 helper.drawShape(shape, translate, SkRegion::kReplace_Op, aa, 0x00); 443 continue; 444 } 445 446 // The other ops (union, xor, diff) only affect pixels inside 447 // the geometry so they can just be drawn normally 448 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) { 449 helper.drawRect(element->getDeviceSpaceRect(), translate, (SkRegion::Op)op, aa, 0xFF); 450 } else { 451 SkPath path; 452 element->asDeviceSpacePath(&path); 453 GrShape shape(path, GrStyle::SimpleFill()); 454 helper.drawShape(shape, translate, (SkRegion::Op)op, aa, 0xFF); 455 } 456 } 457 } 458 459 sk_sp<GrTextureProxy> GrClipStackClip::createSoftwareClipMask( 460 GrContext* context, const GrReducedClip& reducedClip, 461 GrRenderTargetContext* renderTargetContext) const { 462 GrUniqueKey key; 463 create_clip_mask_key(reducedClip.maskGenID(), reducedClip.scissor(), 464 reducedClip.numAnalyticFPs(), &key); 465 466 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); 467 468 sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey( 469 key, kTopLeft_GrSurfaceOrigin)); 470 if (proxy) { 471 return proxy; 472 } 473 474 // The mask texture may be larger than necessary. We round out the clip bounds and pin the top 475 // left corner of the resulting rect to the top left of the texture. 476 SkIRect maskSpaceIBounds = SkIRect::MakeWH(reducedClip.width(), reducedClip.height()); 477 478 SkTaskGroup* taskGroup = context->contextPriv().getTaskGroup(); 479 if (taskGroup && renderTargetContext) { 480 // Create our texture proxy 481 GrSurfaceDesc desc; 482 desc.fWidth = maskSpaceIBounds.width(); 483 desc.fHeight = maskSpaceIBounds.height(); 484 desc.fConfig = kAlpha_8_GrPixelConfig; 485 486 GrBackendFormat format = 487 context->contextPriv().caps()->getBackendFormatFromColorType(kAlpha_8_SkColorType); 488 489 // MDB TODO: We're going to fill this proxy with an ASAP upload (which is out of order wrt 490 // to ops), so it can't have any pending IO. 491 proxy = proxyProvider->createProxy(format, desc, kTopLeft_GrSurfaceOrigin, 492 SkBackingFit::kApprox, SkBudgeted::kYes, 493 GrInternalSurfaceFlags::kNoPendingIO); 494 495 auto uploader = skstd::make_unique<GrTDeferredProxyUploader<ClipMaskData>>(reducedClip); 496 GrTDeferredProxyUploader<ClipMaskData>* uploaderRaw = uploader.get(); 497 auto drawAndUploadMask = [uploaderRaw, maskSpaceIBounds] { 498 TRACE_EVENT0("skia", "Threaded SW Clip Mask Render"); 499 GrSWMaskHelper helper(uploaderRaw->getPixels()); 500 if (helper.init(maskSpaceIBounds)) { 501 draw_clip_elements_to_mask_helper(helper, uploaderRaw->data().elements(), 502 uploaderRaw->data().scissor(), 503 uploaderRaw->data().initialState()); 504 } else { 505 SkDEBUGFAIL("Unable to allocate SW clip mask."); 506 } 507 uploaderRaw->signalAndFreeData(); 508 }; 509 510 taskGroup->add(std::move(drawAndUploadMask)); 511 proxy->texPriv().setDeferredUploader(std::move(uploader)); 512 } else { 513 GrSWMaskHelper helper; 514 if (!helper.init(maskSpaceIBounds)) { 515 return nullptr; 516 } 517 518 draw_clip_elements_to_mask_helper(helper, reducedClip.maskElements(), reducedClip.scissor(), 519 reducedClip.initialState()); 520 521 proxy = helper.toTextureProxy(context, SkBackingFit::kApprox); 522 } 523 524 SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin); 525 proxyProvider->assignUniqueKeyToProxy(key, proxy.get()); 526 add_invalidate_on_pop_message(context, *fStack, reducedClip.maskGenID(), key); 527 return proxy; 528 } 529