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