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