1/* 2 * Copyright 2015 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 "src/gpu/v1/SurfaceDrawContext_v1.h" 9 10#include "include/core/SkDrawable.h" 11#include "include/core/SkLog.h" 12#include "include/core/SkVertices.h" 13#include "include/gpu/GrBackendSemaphore.h" 14#include "include/gpu/GrDirectContext.h" 15#include "include/gpu/GrRecordingContext.h" 16#include "include/private/GrImageContext.h" 17#include "include/private/SkShadowFlags.h" 18#include "include/private/SkVx.h" 19#include "include/utils/SkShadowUtils.h" 20#include "src/core/SkAutoPixmapStorage.h" 21#include "src/core/SkConvertPixels.h" 22#include "src/core/SkDrawProcs.h" 23#include "src/core/SkDrawShadowInfo.h" 24#include "src/core/SkGlyphRunPainter.h" 25#include "src/core/SkLatticeIter.h" 26#include "src/core/SkMatrixPriv.h" 27#include "src/core/SkMatrixProvider.h" 28#include "src/core/SkRRectPriv.h" 29#include "src/gpu/GrAppliedClip.h" 30#include "src/gpu/GrAttachment.h" 31#include "src/gpu/GrCaps.h" 32#include "src/gpu/GrClip.h" 33#include "src/gpu/GrColor.h" 34#include "src/gpu/GrDataUtils.h" 35#include "src/gpu/GrDirectContextPriv.h" 36#include "src/gpu/GrDrawingManager.h" 37#include "src/gpu/GrGpuResourcePriv.h" 38#include "src/gpu/GrImageContextPriv.h" 39#include "src/gpu/GrImageInfo.h" 40#include "src/gpu/GrMemoryPool.h" 41#include "src/gpu/GrProxyProvider.h" 42#include "src/gpu/GrRenderTarget.h" 43#include "src/gpu/GrResourceProvider.h" 44#include "src/gpu/GrSemaphore.h" 45#include "src/gpu/GrStencilSettings.h" 46#include "src/gpu/GrStyle.h" 47#include "src/gpu/GrTracing.h" 48#include "src/gpu/SkGr.h" 49#include "src/gpu/effects/GrBicubicEffect.h" 50#include "src/gpu/effects/GrBlendFragmentProcessor.h" 51#include "src/gpu/effects/GrDisableColorXP.h" 52#include "src/gpu/effects/GrRRectEffect.h" 53#include "src/gpu/effects/GrTextureEffect.h" 54#include "src/gpu/geometry/GrQuad.h" 55#include "src/gpu/geometry/GrQuadUtils.h" 56#include "src/gpu/geometry/GrStyledShape.h" 57#include "src/gpu/ops/BlurOp.h" 58#include "src/gpu/ops/ClearOp.h" 59#include "src/gpu/ops/DrawAtlasOp.h" 60#include "src/gpu/ops/DrawVerticesOp.h" 61#include "src/gpu/ops/DrawableOp.h" 62#include "src/gpu/ops/FillRRectOp.h" 63#include "src/gpu/ops/FillRectOp.h" 64#include "src/gpu/ops/GrDrawOp.h" 65#include "src/gpu/ops/GrOp.h" 66#include "src/gpu/ops/GrOvalOpFactory.h" 67#include "src/gpu/ops/LatticeOp.h" 68#include "src/gpu/ops/RegionOp.h" 69#include "src/gpu/ops/ShadowRRectOp.h" 70#include "src/gpu/ops/StrokeRectOp.h" 71#include "src/gpu/ops/TextureOp.h" 72#include "src/gpu/text/GrSDFTControl.h" 73#include "src/gpu/text/GrTextBlobCache.h" 74#include "src/gpu/v1/PathRenderer.h" 75 76#define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this->drawingManager()->getContext()) 77#define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(this->singleOwner()) 78#define RETURN_IF_ABANDONED if (fContext->abandoned()) { return; } 79#define RETURN_FALSE_IF_ABANDONED if (fContext->abandoned()) { return false; } 80 81////////////////////////////////////////////////////////////////////////////// 82 83namespace { 84 85void op_bounds(SkRect* bounds, const GrOp* op) { 86 *bounds = op->bounds(); 87 if (op->hasZeroArea()) { 88 if (op->hasAABloat()) { 89 bounds->outset(0.5f, 0.5f); 90 } else { 91 // We don't know which way the particular GPU will snap lines or points at integer 92 // coords. So we ensure that the bounds is large enough for either snap. 93 SkRect before = *bounds; 94 bounds->roundOut(bounds); 95 if (bounds->fLeft == before.fLeft) { 96 bounds->fLeft -= 1; 97 } 98 if (bounds->fTop == before.fTop) { 99 bounds->fTop -= 1; 100 } 101 if (bounds->fRight == before.fRight) { 102 bounds->fRight += 1; 103 } 104 if (bounds->fBottom == before.fBottom) { 105 bounds->fBottom += 1; 106 } 107 } 108 } 109} 110 111} // anonymous namespace 112 113namespace skgpu::v1 { 114 115using DoSimplify = GrStyledShape::DoSimplify; 116 117class AutoCheckFlush { 118public: 119 AutoCheckFlush(GrDrawingManager* drawingManager) : fDrawingManager(drawingManager) { 120 SkASSERT(fDrawingManager); 121 } 122 ~AutoCheckFlush() { fDrawingManager->flushIfNecessary(); } 123 124private: 125 GrDrawingManager* fDrawingManager; 126}; 127 128std::unique_ptr<SurfaceDrawContext> SurfaceDrawContext::Make(GrRecordingContext* rContext, 129 GrColorType colorType, 130 sk_sp<GrSurfaceProxy> proxy, 131 sk_sp<SkColorSpace> colorSpace, 132 GrSurfaceOrigin origin, 133 const SkSurfaceProps& surfaceProps, 134 bool flushTimeOpsTask) { 135 if (!rContext || !proxy || colorType == GrColorType::kUnknown) { 136 return nullptr; 137 } 138 139 const GrBackendFormat& format = proxy->backendFormat(); 140 GrSwizzle readSwizzle = rContext->priv().caps()->getReadSwizzle(format, colorType); 141 GrSwizzle writeSwizzle = rContext->priv().caps()->getWriteSwizzle(format, colorType); 142 143 GrSurfaceProxyView readView ( proxy, origin, readSwizzle); 144 GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle); 145 146 return std::make_unique<SurfaceDrawContext>(rContext, 147 std::move(readView), 148 std::move(writeView), 149 colorType, 150 std::move(colorSpace), 151 surfaceProps, 152 flushTimeOpsTask); 153} 154 155std::unique_ptr<SurfaceDrawContext> SurfaceDrawContext::Make( 156 GrRecordingContext* rContext, 157 sk_sp<SkColorSpace> colorSpace, 158 SkBackingFit fit, 159 SkISize dimensions, 160 const GrBackendFormat& format, 161 int sampleCnt, 162 GrMipmapped mipMapped, 163 GrProtected isProtected, 164 GrSwizzle readSwizzle, 165 GrSwizzle writeSwizzle, 166 GrSurfaceOrigin origin, 167 SkBudgeted budgeted, 168 const SkSurfaceProps& surfaceProps) { 169 // It is probably not necessary to check if the context is abandoned here since uses of the 170 // SurfaceDrawContext which need the context will mostly likely fail later on without an 171 // issue. However having this hear adds some reassurance in case there is a path doesn't handle 172 // an abandoned context correctly. It also lets us early out of some extra work. 173 if (rContext->abandoned()) { 174 return nullptr; 175 } 176 177 sk_sp<GrTextureProxy> proxy = rContext->priv().proxyProvider()->createProxy( 178 format, 179 dimensions, 180 GrRenderable::kYes, 181 sampleCnt, 182 mipMapped, 183 fit, 184 budgeted, 185 isProtected); 186 if (!proxy) { 187 return nullptr; 188 } 189 190 GrSurfaceProxyView readView ( proxy, origin, readSwizzle); 191 GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle); 192 193 auto sdc = std::make_unique<SurfaceDrawContext>(rContext, 194 std::move(readView), 195 std::move(writeView), 196 GrColorType::kUnknown, 197 std::move(colorSpace), 198 surfaceProps); 199 sdc->discard(); 200 return sdc; 201} 202 203std::unique_ptr<SurfaceDrawContext> SurfaceDrawContext::Make( 204 GrRecordingContext* rContext, 205 GrColorType colorType, 206 sk_sp<SkColorSpace> colorSpace, 207 SkBackingFit fit, 208 SkISize dimensions, 209 const SkSurfaceProps& surfaceProps, 210 int sampleCnt, 211 GrMipmapped mipMapped, 212 GrProtected isProtected, 213 GrSurfaceOrigin origin, 214 SkBudgeted budgeted) { 215 if (!rContext) { 216 return nullptr; 217 } 218 219 auto format = rContext->priv().caps()->getDefaultBackendFormat(colorType, GrRenderable::kYes); 220 if (!format.isValid()) { 221 return nullptr; 222 } 223 sk_sp<GrTextureProxy> proxy = rContext->priv().proxyProvider()->createProxy(format, 224 dimensions, 225 GrRenderable::kYes, 226 sampleCnt, 227 mipMapped, 228 fit, 229 budgeted, 230 isProtected); 231 if (!proxy) { 232 return nullptr; 233 } 234 235 return SurfaceDrawContext::Make(rContext, 236 colorType, 237 std::move(proxy), 238 std::move(colorSpace), 239 origin, 240 surfaceProps); 241} 242 243std::unique_ptr<SurfaceDrawContext> SurfaceDrawContext::MakeWithFallback( 244 GrRecordingContext* rContext, 245 GrColorType colorType, 246 sk_sp<SkColorSpace> colorSpace, 247 SkBackingFit fit, 248 SkISize dimensions, 249 const SkSurfaceProps& surfaceProps, 250 int sampleCnt, 251 GrMipmapped mipMapped, 252 GrProtected isProtected, 253 GrSurfaceOrigin origin, 254 SkBudgeted budgeted) { 255 const GrCaps* caps = rContext->priv().caps(); 256 auto [ct, _] = caps->getFallbackColorTypeAndFormat(colorType, sampleCnt); 257 if (ct == GrColorType::kUnknown) { 258 return nullptr; 259 } 260 return SurfaceDrawContext::Make(rContext, ct, colorSpace, fit, dimensions, surfaceProps, 261 sampleCnt, mipMapped, isProtected, origin, budgeted); 262} 263 264std::unique_ptr<SurfaceDrawContext> SurfaceDrawContext::MakeFromBackendTexture( 265 GrRecordingContext* rContext, 266 GrColorType colorType, 267 sk_sp<SkColorSpace> colorSpace, 268 const GrBackendTexture& tex, 269 int sampleCnt, 270 GrSurfaceOrigin origin, 271 const SkSurfaceProps& surfaceProps, 272 sk_sp<GrRefCntedCallback> releaseHelper) { 273 SkASSERT(sampleCnt > 0); 274 sk_sp<GrTextureProxy> proxy(rContext->priv().proxyProvider()->wrapRenderableBackendTexture( 275 tex, sampleCnt, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, 276 std::move(releaseHelper))); 277 if (!proxy) { 278 return nullptr; 279 } 280 281 return SurfaceDrawContext::Make(rContext, colorType, std::move(proxy), std::move(colorSpace), 282 origin, surfaceProps); 283} 284 285// In MDB mode the reffing of the 'getLastOpsTask' call's result allows in-progress 286// OpsTask to be picked up and added to by SurfaceDrawContexts lower in the call 287// stack. When this occurs with a closed OpsTask, a new one will be allocated 288// when the surfaceDrawContext attempts to use it (via getOpsTask). 289SurfaceDrawContext::SurfaceDrawContext(GrRecordingContext* rContext, 290 GrSurfaceProxyView readView, 291 GrSurfaceProxyView writeView, 292 GrColorType colorType, 293 sk_sp<SkColorSpace> colorSpace, 294 const SkSurfaceProps& surfaceProps, 295 bool flushTimeOpsTask) 296 : SurfaceFillContext(rContext, 297 std::move(readView), 298 std::move(writeView), 299 {colorType, kPremul_SkAlphaType, std::move(colorSpace)}, 300 flushTimeOpsTask) 301 , fSurfaceProps(surfaceProps) 302 , fCanUseDynamicMSAA( 303 (fSurfaceProps.flags() & SkSurfaceProps::kDynamicMSAA_Flag) && 304 rContext->priv().caps()->supportsDynamicMSAA(this->asRenderTargetProxy())) 305 , fGlyphPainter(*this) { 306 SkDEBUGCODE(this->validate();) 307} 308 309SurfaceDrawContext::~SurfaceDrawContext() { 310 ASSERT_SINGLE_OWNER 311} 312 313void SurfaceDrawContext::willReplaceOpsTask(OpsTask* prevTask, OpsTask* nextTask) { 314 if (prevTask && fNeedsStencil) { 315 // Store the stencil values in memory upon completion of fOpsTask. 316 prevTask->setMustPreserveStencil(); 317 // Reload the stencil buffer content at the beginning of newOpsTask. 318 // FIXME: Could the topo sort insert a task between these two that modifies the stencil 319 // values? 320 nextTask->setInitialStencilContent(OpsTask::StencilContent::kPreserved); 321 } 322#if GR_GPU_STATS && GR_TEST_UTILS 323 if (fCanUseDynamicMSAA) { 324 fContext->priv().dmsaaStats().fNumRenderPasses++; 325 } 326#endif 327} 328 329void SurfaceDrawContext::drawGlyphRunListNoCache(const GrClip* clip, 330 const SkMatrixProvider& viewMatrix, 331 const SkGlyphRunList& glyphRunList, 332 const SkPaint& paint) { 333 GrSDFTControl control = 334 fContext->priv().getSDFTControl(fSurfaceProps.isUseDeviceIndependentFonts()); 335 const SkPoint drawOrigin = glyphRunList.origin(); 336 SkMatrix drawMatrix = viewMatrix.localToDevice(); 337 drawMatrix.preTranslate(drawOrigin.x(), drawOrigin.y()); 338 GrSubRunAllocator* const alloc = this->subRunAlloc(); 339 340 GrSubRunNoCachePainter painter{this, alloc, clip, viewMatrix, glyphRunList, paint}; 341 for (auto& glyphRun : glyphRunList) { 342 // Make and add the text ops. 343 fGlyphPainter.processGlyphRun(glyphRun, 344 drawMatrix, 345 paint, 346 control, 347 &painter); 348 } 349} 350 351void SurfaceDrawContext::drawGlyphRunListWithCache(const GrClip* clip, 352 const SkMatrixProvider& viewMatrix, 353 const SkGlyphRunList& glyphRunList, 354 const SkPaint& paint) { 355 SkMatrix drawMatrix(viewMatrix.localToDevice()); 356 drawMatrix.preTranslate(glyphRunList.origin().x(), glyphRunList.origin().y()); 357 358 GrSDFTControl control = 359 this->recordingContext()->priv().getSDFTControl( 360 this->surfaceProps().isUseDeviceIndependentFonts()); 361 362 auto [canCache, key] = GrTextBlob::Key::Make(glyphRunList, 363 paint, 364 fSurfaceProps, 365 this->colorInfo(), 366 drawMatrix, 367 control); 368 369 sk_sp<GrTextBlob> blob; 370 GrTextBlobCache* textBlobCache = fContext->priv().getTextBlobCache(); 371 if (canCache) { 372 blob = textBlobCache->find(key); 373 } 374 375 if (blob == nullptr || !blob->canReuse(paint, drawMatrix)) { 376 if (blob != nullptr) { 377 // We have to remake the blob because changes may invalidate our masks. 378 // TODO we could probably get away with reuse most of the time if the pointer is unique, 379 // but we'd have to clear the SubRun information 380 textBlobCache->remove(blob.get()); 381 } 382 383 blob = GrTextBlob::Make(glyphRunList, paint, drawMatrix, control, &fGlyphPainter); 384 385 if (canCache) { 386 blob->addKey(key); 387 // The blob may already have been created on a different thread. Use the first one 388 // that was there. 389 blob = textBlobCache->addOrReturnExisting(glyphRunList, blob); 390 } 391 } 392 393 for (const GrSubRun& subRun : blob->subRunList()) { 394 subRun.draw(clip, viewMatrix, glyphRunList, paint, this); 395 } 396} 397 398// choose to use the GrTextBlob cache or not. 399bool gGrDrawTextNoCache = false; 400void SurfaceDrawContext::drawGlyphRunList(const GrClip* clip, 401 const SkMatrixProvider& viewMatrix, 402 const SkGlyphRunList& glyphRunList, 403 const SkPaint& paint) { 404 ASSERT_SINGLE_OWNER 405 RETURN_IF_ABANDONED 406 SkDEBUGCODE(this->validate();) 407 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawGlyphRunList", fContext); 408 409 // Drawing text can cause us to do inline uploads. This is not supported for wrapped vulkan 410 // secondary command buffers because it would require stopping and starting a render pass which 411 // we don't have access to. 412 if (this->wrapsVkSecondaryCB()) { 413 return; 414 } 415 416 if (gGrDrawTextNoCache || glyphRunList.blob() == nullptr) { 417 // If the glyphRunList does not have an associated text blob, then it was created by one of 418 // the direct draw APIs (drawGlyphs, etc.). There is no need to create a GrTextBlob just 419 // build the sub run directly and place it in the op. 420 this->drawGlyphRunListNoCache(clip, viewMatrix, glyphRunList, paint); 421 } else { 422 this->drawGlyphRunListWithCache(clip, viewMatrix, glyphRunList, paint); 423 } 424} 425 426void SurfaceDrawContext::drawPaint(const GrClip* clip, 427 GrPaint&& paint, 428 const SkMatrix& viewMatrix) { 429 // Start with the render target, since that is the maximum content we could possibly fill. 430 // drawFilledQuad() will automatically restrict it to clip bounds for us if possible. 431 if (!paint.numTotalFragmentProcessors()) { 432 // The paint is trivial so we won't need to use local coordinates, so skip calculating the 433 // inverse view matrix. 434 SkRect r = this->asSurfaceProxy()->getBoundsRect(); 435 this->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), r, r); 436 } else { 437 // Use the inverse view matrix to arrive at appropriate local coordinates for the paint. 438 SkMatrix localMatrix; 439 if (!viewMatrix.invert(&localMatrix)) { 440 return; 441 } 442 SkIRect bounds = SkIRect::MakeSize(this->asSurfaceProxy()->dimensions()); 443 this->fillPixelsWithLocalMatrix(clip, std::move(paint), bounds, localMatrix); 444 } 445} 446 447enum class SurfaceDrawContext::QuadOptimization { 448 // The rect to draw doesn't intersect clip or render target, so no draw op should be added 449 kDiscarded, 450 // The rect to draw was converted to some other op and appended to the oplist, so no additional 451 // op is necessary. Currently this can convert it to a clear op or a rrect op. Only valid if 452 // a constColor is provided. 453 kSubmitted, 454 // The clip was folded into the device quad, with updated edge flags and local coords, and 455 // caller is responsible for adding an appropriate op. 456 kClipApplied, 457 // No change to clip, but quad updated to better fit clip/render target, and caller is 458 // responsible for adding an appropriate op. 459 kCropped 460}; 461 462SurfaceDrawContext::QuadOptimization SurfaceDrawContext::attemptQuadOptimization( 463 const GrClip* clip, const GrUserStencilSettings* stencilSettings, GrAA* aa, DrawQuad* quad, 464 GrPaint* paint) { 465 // Optimization requirements: 466 // 1. kDiscard applies when clip bounds and quad bounds do not intersect 467 // 2a. kSubmitted applies when constColor and final geom is pixel aligned rect; 468 // pixel aligned rect requires rect clip and (rect quad or quad covers clip) OR 469 // 2b. kSubmitted applies when constColor and rrect clip and quad covers clip 470 // 4. kClipApplied applies when rect clip and (rect quad or quad covers clip) 471 // 5. kCropped in all other scenarios (although a crop may be a no-op) 472 const SkPMColor4f* constColor = nullptr; 473 SkPMColor4f paintColor; 474 if (!stencilSettings && paint && !paint->hasCoverageFragmentProcessor() && 475 paint->isConstantBlendedColor(&paintColor)) { 476 // Only consider clears/rrects when it's easy to guarantee 100% fill with single color 477 constColor = &paintColor; 478 } 479 480 // Save the old AA flags since CropToRect will modify 'quad' and if kCropped is returned, it's 481 // better to just keep the old flags instead of introducing mixed edge flags. 482 GrQuadAAFlags oldFlags = quad->fEdgeFlags; 483 484 // Use the logical size of the render target, which allows for "fullscreen" clears even if 485 // the render target has an approximate backing fit 486 SkRect rtRect = this->asSurfaceProxy()->getBoundsRect(); 487 488 // For historical reasons, we assume AA for exact bounds checking in IsOutsideClip. 489 // TODO(michaelludwig) - Hopefully that can be revisited when the clipping optimizations are 490 // refactored to work better with round rects and dmsaa. 491 SkRect drawBounds = quad->fDevice.bounds(); 492 if (!quad->fDevice.isFinite() || drawBounds.isEmpty() || 493 GrClip::IsOutsideClip(SkIRect::MakeSize(this->dimensions()), drawBounds, GrAA::kYes)) { 494 return QuadOptimization::kDiscarded; 495 } else if (GrQuadUtils::WillUseHairline(quad->fDevice, GrAAType::kCoverage, quad->fEdgeFlags)) { 496 // Don't try to apply the clip early if we know rendering will use hairline methods, as this 497 // has an effect on the op bounds not otherwise taken into account in this function. 498 return QuadOptimization::kCropped; 499 } 500 501 auto conservativeCrop = [&]() { 502 static constexpr int kLargeDrawLimit = 15000; 503 // Crop the quad to the render target. This doesn't change the visual results of drawing but 504 // is meant to help numerical stability for excessively large draws. 505 if (drawBounds.width() > kLargeDrawLimit || drawBounds.height() > kLargeDrawLimit) { 506 GrQuadUtils::CropToRect(rtRect, *aa, quad, /* compute local */ !constColor); 507 } 508 }; 509 510 bool simpleColor = !stencilSettings && constColor; 511 GrClip::PreClipResult result = clip ? clip->preApply(drawBounds, *aa) 512 : GrClip::PreClipResult(GrClip::Effect::kUnclipped); 513 switch(result.fEffect) { 514 case GrClip::Effect::kClippedOut: 515 return QuadOptimization::kDiscarded; 516 case GrClip::Effect::kUnclipped: 517 if (!simpleColor) { 518 conservativeCrop(); 519 return QuadOptimization::kClipApplied; 520 } else { 521 // Update result to store the render target bounds in order and then fall 522 // through to attempt the draw->native clear optimization 523 result = GrClip::PreClipResult(SkRRect::MakeRect(rtRect), *aa); 524 } 525 break; 526 case GrClip::Effect::kClipped: 527 if (!result.fIsRRect || (stencilSettings && result.fAA != *aa) || 528 (!result.fRRect.isRect() && !simpleColor)) { 529 // The clip and draw state are too complicated to try and reduce 530 conservativeCrop(); 531 return QuadOptimization::kCropped; 532 } // Else fall through to attempt to combine the draw and clip geometry together 533 break; 534 default: 535 SkUNREACHABLE; 536 } 537 538 // If we reached here, we know we're an axis-aligned clip that is either a rect or a round rect, 539 // so we can potentially combine it with the draw geometry so that no clipping is needed. 540 SkASSERT(result.fEffect == GrClip::Effect::kClipped && result.fIsRRect); 541 SkRect clippedBounds = result.fRRect.getBounds(); 542 clippedBounds.intersect(rtRect); 543 if (!drawBounds.intersect(clippedBounds)) { 544 // Our fractional bounds aren't actually inside the clip. GrClip::preApply() can sometimes 545 // think in terms of rounded-out bounds. Discard the draw. 546 return QuadOptimization::kDiscarded; 547 } 548 // Guard against the clipped draw turning into a hairline draw after intersection 549 if (drawBounds.width() < 1.f || drawBounds.height() < 1.f) { 550 return QuadOptimization::kCropped; 551 } 552 553 if (result.fRRect.isRect()) { 554 // No rounded corners, so we might be able to become a native clear or we might be able to 555 // modify geometry and edge flags to represent intersected shape of clip and draw. 556 if (GrQuadUtils::CropToRect(clippedBounds, result.fAA, quad, 557 /*compute local*/ !constColor)) { 558 if (simpleColor && quad->fDevice.quadType() == GrQuad::Type::kAxisAligned) { 559 // Clear optimization is possible 560 drawBounds = quad->fDevice.bounds(); 561 if (drawBounds.contains(rtRect)) { 562 // Fullscreen clear 563 this->clear(*constColor); 564 return QuadOptimization::kSubmitted; 565 } else if (GrClip::IsPixelAligned(drawBounds) && 566 drawBounds.width() > 256 && drawBounds.height() > 256) { 567 // Scissor + clear (round shouldn't do anything since we are pixel aligned) 568 SkIRect scissorRect; 569 drawBounds.round(&scissorRect); 570 this->clear(scissorRect, *constColor); 571 return QuadOptimization::kSubmitted; 572 } 573 } 574 575 // else the draw and clip were combined so just update the AA to reflect combination 576 if (*aa == GrAA::kNo && result.fAA == GrAA::kYes && 577 quad->fEdgeFlags != GrQuadAAFlags::kNone) { 578 // The clip was anti-aliased and now the draw needs to be upgraded to AA to 579 // properly reflect the smooth edge of the clip. 580 *aa = GrAA::kYes; 581 } 582 // We intentionally do not downgrade AA here because we don't know if we need to 583 // preserve MSAA (see GrQuadAAFlags docs). But later in the pipeline, the ops can 584 // use GrResolveAATypeForQuad() to turn off coverage AA when all flags are off. 585 // deviceQuad is exactly the intersection of original quad and clip, so it can be 586 // drawn with no clip (submitted by caller) 587 return QuadOptimization::kClipApplied; 588 } 589 } else { 590 // Rounded corners and constant filled color (limit ourselves to solid colors because 591 // there is no way to use custom local coordinates with drawRRect). 592 SkASSERT(simpleColor); 593 if (GrQuadUtils::CropToRect(clippedBounds, result.fAA, quad, 594 /* compute local */ false) && 595 quad->fDevice.quadType() == GrQuad::Type::kAxisAligned && 596 quad->fDevice.bounds().contains(clippedBounds)) { 597 // Since the cropped quad became a rectangle which covered the bounds of the rrect, 598 // we can draw the rrect directly and ignore the edge flags 599 this->drawRRect(nullptr, std::move(*paint), result.fAA, SkMatrix::I(), result.fRRect, 600 GrStyle::SimpleFill()); 601 return QuadOptimization::kSubmitted; 602 } 603 } 604 605 // The quads have been updated to better fit the clip bounds, but can't get rid of 606 // the clip entirely 607 quad->fEdgeFlags = oldFlags; 608 return QuadOptimization::kCropped; 609} 610 611void SurfaceDrawContext::drawFilledQuad(const GrClip* clip, 612 GrPaint&& paint, 613 GrAA aa, 614 DrawQuad* quad, 615 const GrUserStencilSettings* ss) { 616 ASSERT_SINGLE_OWNER 617 RETURN_IF_ABANDONED 618 SkDEBUGCODE(this->validate();) 619 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawFilledQuad", fContext); 620 621 AutoCheckFlush acf(this->drawingManager()); 622 623 QuadOptimization opt = this->attemptQuadOptimization(clip, ss, &aa, quad, &paint); 624 if (opt >= QuadOptimization::kClipApplied) { 625 // These optimizations require caller to add an op themselves 626 const GrClip* finalClip = opt == QuadOptimization::kClipApplied ? nullptr : clip; 627 GrAAType aaType; 628 if (ss) { 629 aaType = (aa == GrAA::kYes) ? GrAAType::kMSAA : GrAAType::kNone; 630 } else if (fCanUseDynamicMSAA && aa == GrAA::kNo) { 631 // The SkGpuDevice ensures GrAA is always kYes when using dmsaa. If the caller calls 632 // into here with GrAA::kNo, trust that they know what they're doing and that the 633 // rendering will be equal with or without msaa. 634 aaType = GrAAType::kNone; 635 } else { 636 aaType = this->chooseAAType(aa); 637 } 638 this->addDrawOp(finalClip, FillRectOp::Make(fContext, std::move(paint), aaType, 639 quad, ss)); 640 } 641 // All other optimization levels were completely handled inside attempt(), so no extra op needed 642} 643 644void SurfaceDrawContext::drawTexture(const GrClip* clip, 645 GrSurfaceProxyView view, 646 SkAlphaType srcAlphaType, 647 GrSamplerState::Filter filter, 648 GrSamplerState::MipmapMode mm, 649 SkBlendMode blendMode, 650 const SkPMColor4f& color, 651 const SkRect& srcRect, 652 const SkRect& dstRect, 653 GrAA aa, 654 GrQuadAAFlags edgeAA, 655 SkCanvas::SrcRectConstraint constraint, 656 const SkMatrix& viewMatrix, 657 sk_sp<GrColorSpaceXform> colorSpaceXform) { 658 // If we are using dmsaa then go through FillRRectOp (via fillRectToRect). 659 if ((this->alwaysAntialias() || this->caps()->reducedShaderMode()) && aa == GrAA::kYes) { 660 GrPaint paint; 661 paint.setColor4f(color); 662 std::unique_ptr<GrFragmentProcessor> fp; 663 if (constraint == SkCanvas::kStrict_SrcRectConstraint) { 664 fp = GrTextureEffect::MakeSubset(view, srcAlphaType, SkMatrix::I(), 665 GrSamplerState(filter, mm), srcRect, 666 *this->caps()); 667 } else { 668 fp = GrTextureEffect::Make(view, srcAlphaType, SkMatrix::I(), filter, mm); 669 } 670 if (colorSpaceXform) { 671 fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(colorSpaceXform)); 672 } 673 fp = GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kModulate); 674 paint.setColorFragmentProcessor(std::move(fp)); 675 if (blendMode != SkBlendMode::kSrcOver) { 676 paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode)); 677 } 678 this->fillRectToRect(clip, std::move(paint), GrAA::kYes, viewMatrix, dstRect, srcRect); 679 return; 680 } 681 682 const SkRect* subset = constraint == SkCanvas::kStrict_SrcRectConstraint ? 683 &srcRect : nullptr; 684 DrawQuad quad{GrQuad::MakeFromRect(dstRect, viewMatrix), GrQuad(srcRect), edgeAA}; 685 686 this->drawTexturedQuad(clip, std::move(view), srcAlphaType, std::move(colorSpaceXform), filter, 687 mm, color, blendMode, aa, &quad, subset); 688} 689 690void SurfaceDrawContext::drawTexturedQuad(const GrClip* clip, 691 GrSurfaceProxyView proxyView, 692 SkAlphaType srcAlphaType, 693 sk_sp<GrColorSpaceXform> textureXform, 694 GrSamplerState::Filter filter, 695 GrSamplerState::MipmapMode mm, 696 const SkPMColor4f& color, 697 SkBlendMode blendMode, 698 GrAA aa, 699 DrawQuad* quad, 700 const SkRect* subset) { 701 ASSERT_SINGLE_OWNER 702 RETURN_IF_ABANDONED 703 SkDEBUGCODE(this->validate();) 704 SkASSERT(proxyView.asTextureProxy()); 705 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawTexturedQuad", fContext); 706 707 AutoCheckFlush acf(this->drawingManager()); 708 709 // Functionally this is very similar to drawFilledQuad except that there's no constColor to 710 // enable the kSubmitted optimizations, no stencil settings support, and its a TextureOp. 711 QuadOptimization opt = this->attemptQuadOptimization(clip, nullptr/*stencil*/, &aa, quad, 712 nullptr/*paint*/); 713 714 SkASSERT(opt != QuadOptimization::kSubmitted); 715 if (opt != QuadOptimization::kDiscarded) { 716 // And the texture op if not discarded 717 const GrClip* finalClip = opt == QuadOptimization::kClipApplied ? nullptr : clip; 718 GrAAType aaType = this->chooseAAType(aa); 719 auto clampType = GrColorTypeClampType(this->colorInfo().colorType()); 720 auto saturate = clampType == GrClampType::kManual ? TextureOp::Saturate::kYes 721 : TextureOp::Saturate::kNo; 722 // Use the provided subset, although hypothetically we could detect that the cropped local 723 // quad is sufficiently inside the subset and the constraint could be dropped. 724 this->addDrawOp(finalClip, 725 TextureOp::Make(fContext, std::move(proxyView), srcAlphaType, 726 std::move(textureXform), filter, mm, color, saturate, 727 blendMode, aaType, quad, subset)); 728 } 729} 730 731void SurfaceDrawContext::drawRect(const GrClip* clip, 732 GrPaint&& paint, 733 GrAA aa, 734 const SkMatrix& viewMatrix, 735 const SkRect& rect, 736 const GrStyle* style) { 737 if (!style) { 738 style = &GrStyle::SimpleFill(); 739 } 740 ASSERT_SINGLE_OWNER 741 RETURN_IF_ABANDONED 742 SkDEBUGCODE(this->validate();) 743 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawRect", fContext); 744 745 // Path effects should've been devolved to a path in SkGpuDevice 746 SkASSERT(!style->pathEffect()); 747 748 AutoCheckFlush acf(this->drawingManager()); 749 750 const SkStrokeRec& stroke = style->strokeRec(); 751 if (stroke.getStyle() == SkStrokeRec::kFill_Style) { 752 // Fills the rect, using rect as its own local coordinates 753 this->fillRectToRect(clip, std::move(paint), aa, viewMatrix, rect, rect); 754 return; 755 } else if ((stroke.getStyle() == SkStrokeRec::kStroke_Style || 756 stroke.getStyle() == SkStrokeRec::kHairline_Style) && 757 rect.width() && 758 rect.height() && 759 !this->caps()->reducedShaderMode()) { 760 // Only use the StrokeRectOp for non-empty rectangles. Empty rectangles will be processed by 761 // GrStyledShape to handle stroke caps and dashing properly. 762 // 763 // http://skbug.com/12206 -- there is a double-blend issue with the bevel version of 764 // AAStrokeRectOp, and if we increase the AA bloat for MSAA it becomes more pronounced. 765 // Don't use the bevel version with DMSAA. 766 GrAAType aaType = (fCanUseDynamicMSAA && 767 stroke.getJoin() == SkPaint::kMiter_Join && 768 stroke.getMiter() >= SK_ScalarSqrt2) ? GrAAType::kCoverage 769 : this->chooseAAType(aa); 770 GrOp::Owner op = StrokeRectOp::Make(fContext, std::move(paint), aaType, viewMatrix, 771 rect, stroke); 772 // op may be null if the stroke is not supported or if using coverage aa and the view matrix 773 // does not preserve rectangles. 774 if (op) { 775 this->addDrawOp(clip, std::move(op)); 776 return; 777 } 778 } 779 assert_alive(paint); 780 this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, 781 GrStyledShape(rect, *style, DoSimplify::kNo)); 782} 783 784void SurfaceDrawContext::fillRectToRect(const GrClip* clip, 785 GrPaint&& paint, 786 GrAA aa, 787 const SkMatrix& viewMatrix, 788 const SkRect& rectToDraw, 789 const SkRect& localRect) { 790 DrawQuad quad{GrQuad::MakeFromRect(rectToDraw, viewMatrix), GrQuad(localRect), 791 aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone}; 792 793 // If we are using dmsaa then attempt to draw the rect with FillRRectOp. 794 if ((fContext->priv().caps()->reducedShaderMode() || this->alwaysAntialias()) && 795 this->caps()->drawInstancedSupport() && 796 aa == GrAA::kYes) { // If aa is kNo when using dmsaa, the rect is axis aligned. Don't use 797 // FillRRectOp because it might require dual source blending. 798 // http://skbug.com/11756 799 QuadOptimization opt = this->attemptQuadOptimization(clip, nullptr/*stencil*/, &aa, &quad, 800 &paint); 801 if (opt < QuadOptimization::kClipApplied) { 802 // The optimization was completely handled inside attempt(). 803 return; 804 } 805 806 SkRect croppedRect, croppedLocal{}; 807 const GrClip* optimizedClip = clip; 808 if (clip && viewMatrix.isScaleTranslate() && quad.fDevice.asRect(&croppedRect) && 809 (!paint.usesLocalCoords() || quad.fLocal.asRect(&croppedLocal))) { 810 // The cropped quad is still a rect, and our view matrix preserves rects. Map it back 811 // to pre-matrix space. 812 SkMatrix inverse; 813 if (!viewMatrix.invert(&inverse)) { 814 return; 815 } 816 SkASSERT(inverse.rectStaysRect()); 817 inverse.mapRect(&croppedRect); 818 if (opt == QuadOptimization::kClipApplied) { 819 optimizedClip = nullptr; 820 } 821 } else { 822 // Even if attemptQuadOptimization gave us an optimized quad, FillRRectOp needs a rect 823 // in pre-matrix space, so use the original rect. Also preserve the original clip. 824 croppedRect = rectToDraw; 825 croppedLocal = localRect; 826 } 827 828 if (auto op = FillRRectOp::Make(fContext, this->arenaAlloc(), std::move(paint), 829 viewMatrix, SkRRect::MakeRect(croppedRect), croppedLocal, 830 GrAA::kYes)) { 831 this->addDrawOp(optimizedClip, std::move(op)); 832 return; 833 } 834 } 835 836 assert_alive(paint); 837 this->drawFilledQuad(clip, std::move(paint), aa, &quad); 838} 839 840void SurfaceDrawContext::drawQuadSet(const GrClip* clip, 841 GrPaint&& paint, 842 GrAA aa, 843 const SkMatrix& viewMatrix, 844 const GrQuadSetEntry quads[], 845 int cnt) { 846 GrAAType aaType = this->chooseAAType(aa); 847 848 FillRectOp::AddFillRectOps(this, clip, fContext, std::move(paint), aaType, viewMatrix, 849 quads, cnt); 850} 851 852int SurfaceDrawContext::maxWindowRectangles() const { 853 return this->asRenderTargetProxy()->maxWindowRectangles(*this->caps()); 854} 855 856OpsTask::CanDiscardPreviousOps SurfaceDrawContext::canDiscardPreviousOpsOnFullClear() const { 857#if GR_TEST_UTILS 858 if (fPreserveOpsOnFullClear_TestingOnly) { 859 return OpsTask::CanDiscardPreviousOps::kNo; 860 } 861#endif 862 // Regardless of how the clear is implemented (native clear or a fullscreen quad), all prior ops 863 // would normally be overwritten. The one exception is if the render target context is marked as 864 // needing a stencil buffer then there may be a prior op that writes to the stencil buffer. 865 // Although the clear will ignore the stencil buffer, following draw ops may not so we can't get 866 // rid of all the preceding ops. Beware! If we ever add any ops that have a side effect beyond 867 // modifying the stencil buffer we will need a more elaborate tracking system (skbug.com/7002). 868 return OpsTask::CanDiscardPreviousOps(!fNeedsStencil); 869} 870 871void SurfaceDrawContext::setNeedsStencil() { 872 // Don't clear stencil until after we've set fNeedsStencil. This ensures we don't loop forever 873 // in the event that there are driver bugs and we need to clear as a draw. 874 bool hasInitializedStencil = fNeedsStencil; 875 fNeedsStencil = true; 876 if (!hasInitializedStencil) { 877 this->asRenderTargetProxy()->setNeedsStencil(); 878 if (this->caps()->performStencilClearsAsDraws()) { 879 // There is a driver bug with clearing stencil. We must use an op to manually clear the 880 // stencil buffer before the op that required 'setNeedsStencil'. 881 this->internalStencilClear(nullptr, /* inside mask */ false); 882 } else { 883 this->getOpsTask()->setInitialStencilContent( 884 OpsTask::StencilContent::kUserBitsCleared); 885 } 886 } 887} 888 889void SurfaceDrawContext::internalStencilClear(const SkIRect* scissor, bool insideStencilMask) { 890 this->setNeedsStencil(); 891 892 GrScissorState scissorState(this->asSurfaceProxy()->backingStoreDimensions()); 893 if (scissor && !scissorState.set(*scissor)) { 894 // The requested clear region is off screen, so nothing to do. 895 return; 896 } 897 898 bool clearWithDraw = this->caps()->performStencilClearsAsDraws() || 899 (scissorState.enabled() && this->caps()->performPartialClearsAsDraws()); 900 if (clearWithDraw) { 901 const GrUserStencilSettings* ss = GrStencilSettings::SetClipBitSettings(insideStencilMask); 902 903 // Configure the paint to have no impact on the color buffer 904 GrPaint paint; 905 paint.setXPFactory(GrDisableColorXPFactory::Get()); 906 this->addDrawOp(nullptr, 907 FillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(), 908 SkRect::Make(scissorState.rect()), ss)); 909 } else { 910 this->addOp(ClearOp::MakeStencilClip(fContext, scissorState, insideStencilMask)); 911 } 912} 913 914bool SurfaceDrawContext::stencilPath(const GrHardClip* clip, 915 GrAA doStencilMSAA, 916 const SkMatrix& viewMatrix, 917 const SkPath& path) { 918 SkIRect clipBounds = clip ? clip->getConservativeBounds() 919 : SkIRect::MakeSize(this->dimensions()); 920 GrStyledShape shape(path, GrStyledShape::DoSimplify::kNo); 921 922 PathRenderer::CanDrawPathArgs canDrawArgs; 923 canDrawArgs.fCaps = fContext->priv().caps(); 924 canDrawArgs.fProxy = this->asRenderTargetProxy(); 925 canDrawArgs.fClipConservativeBounds = &clipBounds; 926 canDrawArgs.fViewMatrix = &viewMatrix; 927 canDrawArgs.fShape = &shape; 928 canDrawArgs.fPaint = nullptr; 929 canDrawArgs.fSurfaceProps = &fSurfaceProps; 930 canDrawArgs.fAAType = (doStencilMSAA == GrAA::kYes) ? GrAAType::kMSAA : GrAAType::kNone; 931 canDrawArgs.fHasUserStencilSettings = false; 932 auto pr = this->drawingManager()->getPathRenderer(canDrawArgs, 933 false, 934 PathRendererChain::DrawType::kStencil); 935 if (!pr) { 936 SkDebugf("WARNING: No path renderer to stencil path.\n"); 937 return false; 938 } 939 940 PathRenderer::StencilPathArgs args; 941 args.fContext = fContext; 942 args.fSurfaceDrawContext = this; 943 args.fClip = clip; 944 args.fClipConservativeBounds = &clipBounds; 945 args.fViewMatrix = &viewMatrix; 946 args.fShape = &shape; 947 args.fDoStencilMSAA = doStencilMSAA; 948 pr->stencilPath(args); 949 return true; 950} 951 952void SurfaceDrawContext::drawTextureSet(const GrClip* clip, 953 GrTextureSetEntry set[], 954 int cnt, 955 int proxyRunCnt, 956 GrSamplerState::Filter filter, 957 GrSamplerState::MipmapMode mm, 958 SkBlendMode mode, 959 GrAA aa, 960 SkCanvas::SrcRectConstraint constraint, 961 const SkMatrix& viewMatrix, 962 sk_sp<GrColorSpaceXform> texXform) { 963 ASSERT_SINGLE_OWNER 964 RETURN_IF_ABANDONED 965 SkDEBUGCODE(this->validate();) 966 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawTextureSet", fContext); 967 968 // Create the minimum number of GrTextureOps needed to draw this set. Individual 969 // GrTextureOps can rebind the texture between draws thus avoiding GrPaint (re)creation. 970 AutoCheckFlush acf(this->drawingManager()); 971 GrAAType aaType = this->chooseAAType(aa); 972 auto clampType = GrColorTypeClampType(this->colorInfo().colorType()); 973 auto saturate = clampType == GrClampType::kManual ? TextureOp::Saturate::kYes 974 : TextureOp::Saturate::kNo; 975 TextureOp::AddTextureSetOps(this, clip, fContext, set, cnt, proxyRunCnt, filter, mm, saturate, 976 mode, aaType, constraint, viewMatrix, std::move(texXform)); 977} 978 979void SurfaceDrawContext::drawVertices(const GrClip* clip, 980 GrPaint&& paint, 981 const SkMatrixProvider& matrixProvider, 982 sk_sp<SkVertices> vertices, 983 GrPrimitiveType* overridePrimType) { 984 ASSERT_SINGLE_OWNER 985 RETURN_IF_ABANDONED 986 SkDEBUGCODE(this->validate();) 987 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawVertices", fContext); 988 989 AutoCheckFlush acf(this->drawingManager()); 990 991 SkASSERT(vertices); 992 GrAAType aaType = fCanUseDynamicMSAA ? GrAAType::kMSAA : this->chooseAAType(GrAA::kNo); 993 GrOp::Owner op = DrawVerticesOp::Make(fContext, 994 std::move(paint), 995 std::move(vertices), 996 matrixProvider, 997 aaType, 998 this->colorInfo().refColorSpaceXformFromSRGB(), 999 overridePrimType); 1000 this->addDrawOp(clip, std::move(op)); 1001} 1002 1003/////////////////////////////////////////////////////////////////////////////// 1004 1005void SurfaceDrawContext::drawAtlas(const GrClip* clip, 1006 GrPaint&& paint, 1007 const SkMatrix& viewMatrix, 1008 int spriteCount, 1009 const SkRSXform xform[], 1010 const SkRect texRect[], 1011 const SkColor colors[]) { 1012 ASSERT_SINGLE_OWNER 1013 RETURN_IF_ABANDONED 1014 SkDEBUGCODE(this->validate();) 1015 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawAtlas", fContext); 1016 1017 AutoCheckFlush acf(this->drawingManager()); 1018 1019 GrAAType aaType = this->chooseAAType(GrAA::kNo); 1020 GrOp::Owner op = DrawAtlasOp::Make(fContext, std::move(paint), viewMatrix, 1021 aaType, spriteCount, xform, texRect, colors); 1022 this->addDrawOp(clip, std::move(op)); 1023} 1024 1025/////////////////////////////////////////////////////////////////////////////// 1026 1027void SurfaceDrawContext::drawRRect(const GrClip* origClip, 1028 GrPaint&& paint, 1029 GrAA aa, 1030 const SkMatrix& viewMatrix, 1031 const SkRRect& rrect, 1032 const GrStyle& style) { 1033 ASSERT_SINGLE_OWNER 1034 RETURN_IF_ABANDONED 1035 SkDEBUGCODE(this->validate();) 1036 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawRRect", fContext); 1037 1038 SkASSERT(!style.pathEffect()); // this should've been devolved to a path in SkGpuDevice 1039 1040 const SkStrokeRec& stroke = style.strokeRec(); 1041 if (stroke.getStyle() == SkStrokeRec::kFill_Style && rrect.isEmpty()) { 1042 return; 1043 } 1044 1045 const GrClip* clip = origClip; 1046 // It is not uncommon to clip to a round rect and then draw that same round rect. Since our 1047 // lower level clip code works from op bounds, which are SkRects, it doesn't detect that the 1048 // clip can be ignored. The following test attempts to mitigate the stencil clip cost but only 1049 // works for axis-aligned round rects. This also only works for filled rrects since the stroke 1050 // width outsets beyond the rrect itself. 1051 // TODO: skbug.com/10462 - There was mixed performance wins and regressions when this 1052 // optimization was turned on outside of Android Framework. I (michaelludwig) believe this is 1053 // do to the overhead in determining if an SkClipStack is just a rrect. Once that is improved, 1054 // re-enable this and see if we avoid the regressions. 1055#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 1056 SkRRect devRRect; 1057 if (clip && stroke.getStyle() == SkStrokeRec::kFill_Style && 1058 rrect.transform(viewMatrix, &devRRect)) { 1059 GrClip::PreClipResult result = clip->preApply(devRRect.getBounds(), aa); 1060 switch(result.fEffect) { 1061 case GrClip::Effect::kClippedOut: 1062 return; 1063 case GrClip::Effect::kUnclipped: 1064 clip = nullptr; 1065 break; 1066 case GrClip::Effect::kClipped: 1067 // Currently there's no general-purpose rrect-to-rrect contains function, and if we 1068 // got here, we know the devRRect's bounds aren't fully contained by the clip. 1069 // Testing for equality between the two is a reasonable stop-gap for now. 1070 if (result.fIsRRect && result.fRRect == devRRect) { 1071 // NOTE: On the android framework, we allow this optimization even when the clip 1072 // is non-AA and the draw is AA. 1073 if (result.fAA == aa || (result.fAA == GrAA::kNo && aa == GrAA::kYes)) { 1074 clip = nullptr; 1075 } 1076 } 1077 break; 1078 default: 1079 SkUNREACHABLE; 1080 } 1081 } 1082#endif 1083 1084 AutoCheckFlush acf(this->drawingManager()); 1085 1086 GrAAType aaType = this->chooseAAType(aa); 1087 1088 GrOp::Owner op; 1089 if (aaType == GrAAType::kCoverage && 1090 !fCanUseDynamicMSAA && 1091 !this->caps()->reducedShaderMode() && 1092 rrect.isSimple() && 1093 rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY && 1094 viewMatrix.rectStaysRect() && viewMatrix.isSimilarity()) { 1095 // In specific cases we use a dedicated circular round rect op to try and get better perf. 1096 assert_alive(paint); 1097 op = GrOvalOpFactory::MakeCircularRRectOp(fContext, std::move(paint), viewMatrix, rrect, 1098 stroke, this->caps()->shaderCaps()); 1099 } 1100 if (!op && style.isSimpleFill()) { 1101 assert_alive(paint); 1102 op = FillRRectOp::Make(fContext, this->arenaAlloc(), std::move(paint), viewMatrix, rrect, 1103 rrect.rect(), GrAA(aaType != GrAAType::kNone)); 1104 } 1105 if (!op && (aaType == GrAAType::kCoverage || fCanUseDynamicMSAA)) { 1106 assert_alive(paint); 1107 op = GrOvalOpFactory::MakeRRectOp( 1108 fContext, std::move(paint), viewMatrix, rrect, stroke, this->caps()->shaderCaps()); 1109 } 1110 if (op) { 1111 this->addDrawOp(clip, std::move(op)); 1112 return; 1113 } 1114 1115 assert_alive(paint); 1116 this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, 1117 GrStyledShape(rrect, style, DoSimplify::kNo)); 1118} 1119 1120/////////////////////////////////////////////////////////////////////////////// 1121 1122bool SurfaceDrawContext::drawFastShadow(const GrClip* clip, 1123 const SkMatrix& viewMatrix, 1124 const SkPath& path, 1125 const SkDrawShadowRec& rec) { 1126 ASSERT_SINGLE_OWNER 1127 if (fContext->abandoned()) { 1128 return true; 1129 } 1130 SkDEBUGCODE(this->validate();) 1131 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawFastShadow", fContext); 1132 1133 // check z plane 1134 bool tiltZPlane = SkToBool(!SkScalarNearlyZero(rec.fZPlaneParams.fX) || 1135 !SkScalarNearlyZero(rec.fZPlaneParams.fY)); 1136 bool skipAnalytic = SkToBool(rec.fFlags & SkShadowFlags::kGeometricOnly_ShadowFlag); 1137 if (tiltZPlane || skipAnalytic || !viewMatrix.rectStaysRect() || !viewMatrix.isSimilarity()) { 1138 return false; 1139 } 1140 1141 SkRRect rrect; 1142 SkRect rect; 1143 // we can only handle rects, circles, and simple rrects with circular corners 1144 bool isRRect = path.isRRect(&rrect) && SkRRectPriv::IsNearlySimpleCircular(rrect) && 1145 rrect.getSimpleRadii().fX > SK_ScalarNearlyZero; 1146 if (!isRRect && 1147 path.isOval(&rect) && SkScalarNearlyEqual(rect.width(), rect.height()) && 1148 rect.width() > SK_ScalarNearlyZero) { 1149 rrect.setOval(rect); 1150 isRRect = true; 1151 } 1152 if (!isRRect && path.isRect(&rect)) { 1153 rrect.setRect(rect); 1154 isRRect = true; 1155 } 1156 1157 if (!isRRect) { 1158 return false; 1159 } 1160 1161 if (rrect.isEmpty()) { 1162 return true; 1163 } 1164 1165 AutoCheckFlush acf(this->drawingManager()); 1166 1167 SkPoint3 devLightPos = rec.fLightPos; 1168 bool directional = SkToBool(rec.fFlags & kDirectionalLight_ShadowFlag); 1169 if (!directional) { 1170 // transform light 1171 viewMatrix.mapPoints((SkPoint*)&devLightPos.fX, 1); 1172 } 1173 1174 // 1/scale 1175 SkScalar devToSrcScale = viewMatrix.isScaleTranslate() ? 1176 SkScalarInvert(SkScalarAbs(viewMatrix[SkMatrix::kMScaleX])) : 1177 sk_float_rsqrt(viewMatrix[SkMatrix::kMScaleX] * viewMatrix[SkMatrix::kMScaleX] + 1178 viewMatrix[SkMatrix::kMSkewX] * viewMatrix[SkMatrix::kMSkewX]); 1179 1180 SkScalar occluderHeight = rec.fZPlaneParams.fZ; 1181 bool transparent = SkToBool(rec.fFlags & SkShadowFlags::kTransparentOccluder_ShadowFlag); 1182 1183 if (SkColorGetA(rec.fAmbientColor) > 0) { 1184 SkScalar devSpaceInsetWidth = SkDrawShadowMetrics::AmbientBlurRadius(occluderHeight); 1185 const SkScalar umbraRecipAlpha = SkDrawShadowMetrics::AmbientRecipAlpha(occluderHeight); 1186 const SkScalar devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha; 1187 1188 // Outset the shadow rrect to the border of the penumbra 1189 SkScalar ambientPathOutset = devSpaceInsetWidth * devToSrcScale; 1190 SkRRect ambientRRect; 1191 SkRect outsetRect = rrect.rect().makeOutset(ambientPathOutset, ambientPathOutset); 1192 // If the rrect was an oval then its outset will also be one. 1193 // We set it explicitly to avoid errors. 1194 if (rrect.isOval()) { 1195 ambientRRect = SkRRect::MakeOval(outsetRect); 1196 } else { 1197 SkScalar outsetRad = SkRRectPriv::GetSimpleRadii(rrect).fX + ambientPathOutset; 1198 ambientRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad); 1199 } 1200 1201 GrColor ambientColor = SkColorToPremulGrColor(rec.fAmbientColor); 1202 if (transparent) { 1203 // set a large inset to force a fill 1204 devSpaceInsetWidth = ambientRRect.width(); 1205 } 1206 1207 GrOp::Owner op = ShadowRRectOp::Make(fContext, 1208 ambientColor, 1209 viewMatrix, 1210 ambientRRect, 1211 devSpaceAmbientBlur, 1212 devSpaceInsetWidth); 1213 if (op) { 1214 this->addDrawOp(clip, std::move(op)); 1215 } 1216 } 1217 1218 if (SkColorGetA(rec.fSpotColor) > 0) { 1219 SkScalar devSpaceSpotBlur; 1220 SkScalar spotScale; 1221 SkVector spotOffset; 1222 if (directional) { 1223 SkDrawShadowMetrics::GetDirectionalParams(occluderHeight, devLightPos.fX, 1224 devLightPos.fY, devLightPos.fZ, 1225 rec.fLightRadius, &devSpaceSpotBlur, 1226 &spotScale, &spotOffset); 1227 } else { 1228 SkDrawShadowMetrics::GetSpotParams(occluderHeight, devLightPos.fX, devLightPos.fY, 1229 devLightPos.fZ, rec.fLightRadius, 1230 &devSpaceSpotBlur, &spotScale, &spotOffset, rec.isLimitElevation); 1231 } 1232 // handle scale of radius due to CTM 1233 const SkScalar srcSpaceSpotBlur = devSpaceSpotBlur * devToSrcScale; 1234 1235 // Adjust translate for the effect of the scale. 1236 spotOffset.fX += spotScale*viewMatrix[SkMatrix::kMTransX]; 1237 spotOffset.fY += spotScale*viewMatrix[SkMatrix::kMTransY]; 1238 // This offset is in dev space, need to transform it into source space. 1239 SkMatrix ctmInverse; 1240 if (viewMatrix.invert(&ctmInverse)) { 1241 ctmInverse.mapPoints(&spotOffset, 1); 1242 } else { 1243 // Since the matrix is a similarity, this should never happen, but just in case... 1244 SkDebugf("Matrix is degenerate. Will not render spot shadow correctly!\n"); 1245 SkASSERT(false); 1246 } 1247 1248 // Compute the transformed shadow rrect 1249 SkRRect spotShadowRRect; 1250 SkMatrix shadowTransform; 1251 shadowTransform.setScaleTranslate(spotScale, spotScale, spotOffset.fX, spotOffset.fY); 1252 rrect.transform(shadowTransform, &spotShadowRRect); 1253 SkScalar spotRadius = spotShadowRRect.getSimpleRadii().fX; 1254 1255 // Compute the insetWidth 1256 SkScalar blurOutset = srcSpaceSpotBlur; 1257 SkScalar insetWidth = blurOutset; 1258 if (transparent) { 1259 // If transparent, just do a fill 1260 insetWidth += spotShadowRRect.width(); 1261 } else { 1262 // For shadows, instead of using a stroke we specify an inset from the penumbra 1263 // border. We want to extend this inset area so that it meets up with the caster 1264 // geometry. The inset geometry will by default already be inset by the blur width. 1265 // 1266 // We compare the min and max corners inset by the radius between the original 1267 // rrect and the shadow rrect. The distance between the two plus the difference 1268 // between the scaled radius and the original radius gives the distance from the 1269 // transformed shadow shape to the original shape in that corner. The max 1270 // of these gives the maximum distance we need to cover. 1271 // 1272 // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to 1273 // that to get the full insetWidth. 1274 SkScalar maxOffset; 1275 if (rrect.isRect()) { 1276 // Manhattan distance works better for rects 1277 maxOffset = std::max(std::max(SkTAbs(spotShadowRRect.rect().fLeft - 1278 rrect.rect().fLeft), 1279 SkTAbs(spotShadowRRect.rect().fTop - 1280 rrect.rect().fTop)), 1281 std::max(SkTAbs(spotShadowRRect.rect().fRight - 1282 rrect.rect().fRight), 1283 SkTAbs(spotShadowRRect.rect().fBottom - 1284 rrect.rect().fBottom))); 1285 } else { 1286 SkScalar dr = spotRadius - SkRRectPriv::GetSimpleRadii(rrect).fX; 1287 SkPoint upperLeftOffset = SkPoint::Make(spotShadowRRect.rect().fLeft - 1288 rrect.rect().fLeft + dr, 1289 spotShadowRRect.rect().fTop - 1290 rrect.rect().fTop + dr); 1291 SkPoint lowerRightOffset = SkPoint::Make(spotShadowRRect.rect().fRight - 1292 rrect.rect().fRight - dr, 1293 spotShadowRRect.rect().fBottom - 1294 rrect.rect().fBottom - dr); 1295 maxOffset = SkScalarSqrt(std::max(SkPointPriv::LengthSqd(upperLeftOffset), 1296 SkPointPriv::LengthSqd(lowerRightOffset))) + dr; 1297 } 1298 insetWidth += std::max(blurOutset, maxOffset); 1299 } 1300 1301 // Outset the shadow rrect to the border of the penumbra 1302 SkRect outsetRect = spotShadowRRect.rect().makeOutset(blurOutset, blurOutset); 1303 if (spotShadowRRect.isOval()) { 1304 spotShadowRRect = SkRRect::MakeOval(outsetRect); 1305 } else { 1306 SkScalar outsetRad = spotRadius + blurOutset; 1307 spotShadowRRect = SkRRect::MakeRectXY(outsetRect, outsetRad, outsetRad); 1308 } 1309 1310 GrColor spotColor = SkColorToPremulGrColor(rec.fSpotColor); 1311 1312 GrOp::Owner op = ShadowRRectOp::Make(fContext, 1313 spotColor, 1314 viewMatrix, 1315 spotShadowRRect, 1316 2.0f * devSpaceSpotBlur, 1317 insetWidth); 1318 if (op) { 1319 this->addDrawOp(clip, std::move(op)); 1320 } 1321 } 1322 1323 return true; 1324} 1325 1326/////////////////////////////////////////////////////////////////////////////// 1327 1328void SurfaceDrawContext::drawRegion(const GrClip* clip, 1329 GrPaint&& paint, 1330 GrAA aa, 1331 const SkMatrix& viewMatrix, 1332 const SkRegion& region, 1333 const GrStyle& style, 1334 const GrUserStencilSettings* ss) { 1335 ASSERT_SINGLE_OWNER 1336 RETURN_IF_ABANDONED 1337 SkDEBUGCODE(this->validate();) 1338 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawRegion", fContext); 1339 1340 if (GrAA::kYes == aa) { 1341 // GrRegionOp performs no antialiasing but is much faster, so here we check the matrix 1342 // to see whether aa is really required. 1343 if (!SkToBool(viewMatrix.getType() & ~(SkMatrix::kTranslate_Mask)) && 1344 SkScalarIsInt(viewMatrix.getTranslateX()) && 1345 SkScalarIsInt(viewMatrix.getTranslateY())) { 1346 aa = GrAA::kNo; 1347 } 1348 } 1349 bool complexStyle = !style.isSimpleFill(); 1350 if (complexStyle || GrAA::kYes == aa) { 1351 SkPath path; 1352 region.getBoundaryPath(&path); 1353 path.setIsVolatile(true); 1354 1355 return this->drawPath(clip, std::move(paint), aa, viewMatrix, path, style); 1356 } 1357 1358 GrAAType aaType = (this->numSamples() > 1) ? GrAAType::kMSAA : GrAAType::kNone; 1359 GrOp::Owner op = RegionOp::Make(fContext, std::move(paint), viewMatrix, region, aaType, ss); 1360 this->addDrawOp(clip, std::move(op)); 1361} 1362 1363void SurfaceDrawContext::drawOval(const GrClip* clip, 1364 GrPaint&& paint, 1365 GrAA aa, 1366 const SkMatrix& viewMatrix, 1367 const SkRect& oval, 1368 const GrStyle& style) { 1369 ASSERT_SINGLE_OWNER 1370 RETURN_IF_ABANDONED 1371 SkDEBUGCODE(this->validate();) 1372 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawOval", fContext); 1373 1374 const SkStrokeRec& stroke = style.strokeRec(); 1375 1376 if (oval.isEmpty() && !style.pathEffect()) { 1377 if (stroke.getStyle() == SkStrokeRec::kFill_Style) { 1378 return; 1379 } 1380 1381 this->drawRect(clip, std::move(paint), aa, viewMatrix, oval, &style); 1382 return; 1383 } 1384 1385 AutoCheckFlush acf(this->drawingManager()); 1386 1387 GrAAType aaType = this->chooseAAType(aa); 1388 1389 GrOp::Owner op; 1390 if (aaType == GrAAType::kCoverage && 1391 !fCanUseDynamicMSAA && 1392 !this->caps()->reducedShaderMode() && 1393 oval.width() > SK_ScalarNearlyZero && 1394 oval.width() == oval.height() && 1395 viewMatrix.isSimilarity()) { 1396 // In specific cases we use a dedicated circle op to try and get better perf. 1397 assert_alive(paint); 1398 op = GrOvalOpFactory::MakeCircleOp(fContext, std::move(paint), viewMatrix, oval, style, 1399 this->caps()->shaderCaps()); 1400 } 1401 if (!op && style.isSimpleFill()) { 1402 // FillRRectOp has special geometry and a fragment-shader branch to conditionally evaluate 1403 // the arc equation. This same special geometry and fragment branch also turn out to be a 1404 // substantial optimization for drawing ovals (namely, by not evaluating the arc equation 1405 // inside the oval's inner diamond). Given these optimizations, it's a clear win to draw 1406 // ovals the exact same way we do round rects. 1407 assert_alive(paint); 1408 op = FillRRectOp::Make(fContext, this->arenaAlloc(), std::move(paint), viewMatrix, 1409 SkRRect::MakeOval(oval), oval, GrAA(aaType != GrAAType::kNone)); 1410 } 1411 if (!op && (aaType == GrAAType::kCoverage || fCanUseDynamicMSAA)) { 1412 assert_alive(paint); 1413 op = GrOvalOpFactory::MakeOvalOp(fContext, std::move(paint), viewMatrix, oval, style, 1414 this->caps()->shaderCaps()); 1415 } 1416 if (op) { 1417 this->addDrawOp(clip, std::move(op)); 1418 return; 1419 } 1420 1421 assert_alive(paint); 1422 this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, 1423 GrStyledShape(SkRRect::MakeOval(oval), SkPathDirection::kCW, 2, 1424 false, style, DoSimplify::kNo)); 1425} 1426 1427void SurfaceDrawContext::drawArc(const GrClip* clip, 1428 GrPaint&& paint, 1429 GrAA aa, 1430 const SkMatrix& viewMatrix, 1431 const SkRect& oval, 1432 SkScalar startAngle, 1433 SkScalar sweepAngle, 1434 bool useCenter, 1435 const GrStyle& style) { 1436 ASSERT_SINGLE_OWNER 1437 RETURN_IF_ABANDONED 1438 SkDEBUGCODE(this->validate();) 1439 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawArc", fContext); 1440 1441 AutoCheckFlush acf(this->drawingManager()); 1442 1443 GrAAType aaType = this->chooseAAType(aa); 1444 if (aaType == GrAAType::kCoverage) { 1445 const GrShaderCaps* shaderCaps = this->caps()->shaderCaps(); 1446 GrOp::Owner op = GrOvalOpFactory::MakeArcOp(fContext, 1447 std::move(paint), 1448 viewMatrix, 1449 oval, 1450 startAngle, 1451 sweepAngle, 1452 useCenter, 1453 style, 1454 shaderCaps); 1455 if (op) { 1456 this->addDrawOp(clip, std::move(op)); 1457 return; 1458 } 1459 assert_alive(paint); 1460 } 1461 this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, 1462 GrStyledShape::MakeArc(oval, startAngle, sweepAngle, useCenter, 1463 style, DoSimplify::kNo)); 1464} 1465 1466void SurfaceDrawContext::drawImageLattice(const GrClip* clip, 1467 GrPaint&& paint, 1468 const SkMatrix& viewMatrix, 1469 GrSurfaceProxyView view, 1470 SkAlphaType alphaType, 1471 sk_sp<GrColorSpaceXform> csxf, 1472 GrSamplerState::Filter filter, 1473 std::unique_ptr<SkLatticeIter> iter, 1474 const SkRect& dst) { 1475 ASSERT_SINGLE_OWNER 1476 RETURN_IF_ABANDONED 1477 SkDEBUGCODE(this->validate();) 1478 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawImageLattice", fContext); 1479 1480 AutoCheckFlush acf(this->drawingManager()); 1481 1482 GrOp::Owner op = 1483 LatticeOp::MakeNonAA(fContext, std::move(paint), viewMatrix, std::move(view), 1484 alphaType, std::move(csxf), filter, std::move(iter), dst); 1485 this->addDrawOp(clip, std::move(op)); 1486} 1487 1488void SurfaceDrawContext::drawDrawable(std::unique_ptr<SkDrawable::GpuDrawHandler> drawable, 1489 const SkRect& bounds) { 1490 GrOp::Owner op(DrawableOp::Make(fContext, std::move(drawable), bounds)); 1491 SkASSERT(op); 1492 this->addOp(std::move(op)); 1493} 1494 1495void SurfaceDrawContext::setLastClip(uint32_t clipStackGenID, 1496 const SkIRect& devClipBounds, 1497 int numClipAnalyticElements) { 1498 auto opsTask = this->getOpsTask(); 1499 opsTask->fLastClipStackGenID = clipStackGenID; 1500 opsTask->fLastDevClipBounds = devClipBounds; 1501 opsTask->fLastClipNumAnalyticElements = numClipAnalyticElements; 1502} 1503 1504bool SurfaceDrawContext::mustRenderClip(uint32_t clipStackGenID, 1505 const SkIRect& devClipBounds, 1506 int numClipAnalyticElements) { 1507 auto opsTask = this->getOpsTask(); 1508 return opsTask->fLastClipStackGenID != clipStackGenID || 1509 !opsTask->fLastDevClipBounds.contains(devClipBounds) || 1510 opsTask->fLastClipNumAnalyticElements != numClipAnalyticElements; 1511} 1512 1513bool SurfaceDrawContext::waitOnSemaphores(int numSemaphores, 1514 const GrBackendSemaphore waitSemaphores[], 1515 bool deleteSemaphoresAfterWait) { 1516 ASSERT_SINGLE_OWNER 1517 RETURN_FALSE_IF_ABANDONED 1518 SkDEBUGCODE(this->validate();) 1519 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "waitOnSemaphores", fContext); 1520 1521 AutoCheckFlush acf(this->drawingManager()); 1522 1523 if (numSemaphores && !this->caps()->semaphoreSupport()) { 1524 return false; 1525 } 1526 1527 auto direct = fContext->asDirectContext(); 1528 if (!direct) { 1529 return false; 1530 } 1531 1532 auto resourceProvider = direct->priv().resourceProvider(); 1533 1534 GrWrapOwnership ownership = 1535 deleteSemaphoresAfterWait ? kAdopt_GrWrapOwnership : kBorrow_GrWrapOwnership; 1536 1537 std::unique_ptr<std::unique_ptr<GrSemaphore>[]> grSemaphores( 1538 new std::unique_ptr<GrSemaphore>[numSemaphores]); 1539 for (int i = 0; i < numSemaphores; ++i) { 1540 grSemaphores[i] = resourceProvider->wrapBackendSemaphore(waitSemaphores[i], 1541 GrSemaphoreWrapType::kWillWait, 1542 ownership); 1543 } 1544 this->drawingManager()->newWaitRenderTask(this->asSurfaceProxyRef(), std::move(grSemaphores), 1545 numSemaphores); 1546 return true; 1547} 1548 1549void SurfaceDrawContext::drawPath(const GrClip* clip, 1550 GrPaint&& paint, 1551 GrAA aa, 1552 const SkMatrix& viewMatrix, 1553 const SkPath& path, 1554 const GrStyle& style) { 1555 ASSERT_SINGLE_OWNER 1556 RETURN_IF_ABANDONED 1557 SkDEBUGCODE(this->validate();) 1558 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawPath", fContext); 1559 1560 GrStyledShape shape(path, style, DoSimplify::kNo); 1561 this->drawShape(clip, std::move(paint), aa, viewMatrix, std::move(shape)); 1562} 1563 1564void SurfaceDrawContext::drawShape(const GrClip* clip, 1565 GrPaint&& paint, 1566 GrAA aa, 1567 const SkMatrix& viewMatrix, 1568 GrStyledShape&& shape) { 1569 ASSERT_SINGLE_OWNER 1570 RETURN_IF_ABANDONED 1571 SkDEBUGCODE(this->validate();) 1572 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawShape", fContext); 1573 1574 if (shape.isEmpty()) { 1575 if (shape.inverseFilled()) { 1576 this->drawPaint(clip, std::move(paint), viewMatrix); 1577 } 1578 return; 1579 } 1580 1581 AutoCheckFlush acf(this->drawingManager()); 1582 1583 // If we get here in drawShape(), we definitely need to use path rendering 1584 this->drawShapeUsingPathRenderer(clip, std::move(paint), aa, viewMatrix, std::move(shape), 1585 /* attemptDrawSimple */ true); 1586} 1587 1588static SkIRect get_clip_bounds(const SurfaceDrawContext* sdc, const GrClip* clip) { 1589 return clip ? clip->getConservativeBounds() : SkIRect::MakeWH(sdc->width(), sdc->height()); 1590} 1591 1592bool SurfaceDrawContext::drawAndStencilPath(const GrHardClip* clip, 1593 const GrUserStencilSettings* ss, 1594 SkRegion::Op op, 1595 bool invert, 1596 GrAA aa, 1597 const SkMatrix& viewMatrix, 1598 const SkPath& path) { 1599 ASSERT_SINGLE_OWNER 1600 RETURN_FALSE_IF_ABANDONED 1601 SkDEBUGCODE(this->validate();) 1602 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "drawAndStencilPath", fContext); 1603 1604 if (path.isEmpty() && path.isInverseFillType()) { 1605 GrPaint paint; 1606 paint.setCoverageSetOpXPFactory(op, invert); 1607 this->stencilRect(clip, ss, std::move(paint), GrAA::kNo, SkMatrix::I(), 1608 SkRect::Make(this->dimensions())); 1609 return true; 1610 } 1611 1612 AutoCheckFlush acf(this->drawingManager()); 1613 1614 // An Assumption here is that path renderer would use some form of tweaking 1615 // the src color (either the input alpha or in the frag shader) to implement 1616 // aa. If we have some future driver-mojo path AA that can do the right 1617 // thing WRT to the blend then we'll need some query on the PR. 1618 GrAAType aaType = this->chooseAAType(aa); 1619 bool hasUserStencilSettings = !ss->isUnused(); 1620 1621 SkIRect clipConservativeBounds = get_clip_bounds(this, clip); 1622 1623 GrPaint paint; 1624 paint.setCoverageSetOpXPFactory(op, invert); 1625 1626 GrStyledShape shape(path, GrStyle::SimpleFill()); 1627 PathRenderer::CanDrawPathArgs canDrawArgs; 1628 canDrawArgs.fCaps = this->caps(); 1629 canDrawArgs.fProxy = this->asRenderTargetProxy(); 1630 canDrawArgs.fViewMatrix = &viewMatrix; 1631 canDrawArgs.fShape = &shape; 1632 canDrawArgs.fPaint = &paint; 1633 canDrawArgs.fSurfaceProps = &fSurfaceProps; 1634 canDrawArgs.fClipConservativeBounds = &clipConservativeBounds; 1635 canDrawArgs.fAAType = aaType; 1636 canDrawArgs.fHasUserStencilSettings = hasUserStencilSettings; 1637 1638 using DrawType = PathRendererChain::DrawType; 1639 1640 // Don't allow the SW renderer 1641 auto pr = this->drawingManager()->getPathRenderer(canDrawArgs, 1642 false, 1643 DrawType::kStencilAndColor); 1644 if (!pr) { 1645 return false; 1646 } 1647 1648 PathRenderer::DrawPathArgs args{this->drawingManager()->getContext(), 1649 std::move(paint), 1650 ss, 1651 this, 1652 clip, 1653 &clipConservativeBounds, 1654 &viewMatrix, 1655 &shape, 1656 aaType, 1657 this->colorInfo().isLinearlyBlended()}; 1658 pr->drawPath(args); 1659 return true; 1660} 1661 1662SkBudgeted SurfaceDrawContext::isBudgeted() const { 1663 ASSERT_SINGLE_OWNER 1664 1665 if (fContext->abandoned()) { 1666 return SkBudgeted::kNo; 1667 } 1668 1669 SkDEBUGCODE(this->validate();) 1670 1671 return this->asSurfaceProxy()->isBudgeted(); 1672} 1673 1674void SurfaceDrawContext::drawStrokedLine(const GrClip* clip, 1675 GrPaint&& paint, 1676 GrAA aa, 1677 const SkMatrix& viewMatrix, 1678 const SkPoint points[2], 1679 const SkStrokeRec& stroke) { 1680 ASSERT_SINGLE_OWNER 1681 1682 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style); 1683 SkASSERT(stroke.getWidth() > 0); 1684 // Adding support for round capping would require a 1685 // SurfaceDrawContext::fillRRectWithLocalMatrix entry point 1686 SkASSERT(SkPaint::kRound_Cap != stroke.getCap()); 1687 1688 const SkScalar halfWidth = 0.5f * stroke.getWidth(); 1689 if (halfWidth <= 0.f) { 1690 // Prevents underflow when stroke width is epsilon > 0 (so technically not a hairline). 1691 // The CTM would need to have a scale near 1/epsilon in order for this to have meaningful 1692 // coverage (although that would likely overflow elsewhere and cause the draw to drop due 1693 // to non-finite bounds). At any other scale, this line is so thin, it's coverage is 1694 // negligible, so discarding the draw is visually equivalent. 1695 return; 1696 } 1697 1698 SkVector parallel = points[1] - points[0]; 1699 1700 if (!SkPoint::Normalize(¶llel)) { 1701 parallel.fX = 1.0f; 1702 parallel.fY = 0.0f; 1703 } 1704 parallel *= halfWidth; 1705 1706 SkVector ortho = { parallel.fY, -parallel.fX }; 1707 if (SkPaint::kButt_Cap == stroke.getCap()) { 1708 // No extra extension for butt caps 1709 parallel = {0.f, 0.f}; 1710 } 1711 // Order is TL, TR, BR, BL where arbitrarily "down" is p0 to p1 and "right" is positive 1712 SkPoint corners[4] = { points[0] - ortho - parallel, 1713 points[0] + ortho - parallel, 1714 points[1] + ortho + parallel, 1715 points[1] - ortho + parallel }; 1716 1717 GrQuadAAFlags edgeAA = (aa == GrAA::kYes) ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone; 1718 this->fillQuadWithEdgeAA(clip, std::move(paint), aa, edgeAA, viewMatrix, corners, nullptr); 1719} 1720 1721bool SurfaceDrawContext::drawSimpleShape(const GrClip* clip, 1722 GrPaint* paint, 1723 GrAA aa, 1724 const SkMatrix& viewMatrix, 1725 const GrStyledShape& shape) { 1726 if (!shape.style().hasPathEffect()) { 1727 GrAAType aaType = this->chooseAAType(aa); 1728 SkPoint linePts[2]; 1729 SkRRect rrect; 1730 // We can ignore the starting point and direction since there is no path effect. 1731 bool inverted; 1732 if (shape.asLine(linePts, &inverted) && !inverted && 1733 shape.style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style && 1734 shape.style().strokeRec().getCap() != SkPaint::kRound_Cap) { 1735 // The stroked line is an oriented rectangle, which looks the same or better (if 1736 // perspective) compared to path rendering. The exception is subpixel/hairline lines 1737 // that are non-AA or MSAA, in which case the default path renderer achieves higher 1738 // quality. 1739 // FIXME(michaelludwig): If the fill rect op could take an external coverage, or checks 1740 // for and outsets thin non-aa rects to 1px, the path renderer could be skipped. 1741 SkScalar coverage; 1742 if (aaType == GrAAType::kCoverage || 1743 !SkDrawTreatAAStrokeAsHairline(shape.style().strokeRec().getWidth(), viewMatrix, 1744 &coverage)) { 1745 this->drawStrokedLine(clip, std::move(*paint), aa, viewMatrix, linePts, 1746 shape.style().strokeRec()); 1747 return true; 1748 } 1749 } else if (shape.asRRect(&rrect, nullptr, nullptr, &inverted) && !inverted) { 1750 if (rrect.isRect()) { 1751 this->drawRect(clip, std::move(*paint), aa, viewMatrix, rrect.rect(), 1752 &shape.style()); 1753 return true; 1754 } else if (rrect.isOval()) { 1755 this->drawOval(clip, std::move(*paint), aa, viewMatrix, rrect.rect(), 1756 shape.style()); 1757 return true; 1758 } 1759 this->drawRRect(clip, std::move(*paint), aa, viewMatrix, rrect, shape.style()); 1760 return true; 1761 } else if (GrAAType::kCoverage == aaType && 1762 shape.style().isSimpleFill() && 1763 viewMatrix.rectStaysRect() && 1764 !this->caps()->reducedShaderMode()) { 1765 // TODO: the rectStaysRect restriction could be lifted if we were willing to apply the 1766 // matrix to all the points individually rather than just to the rect 1767 SkRect rects[2]; 1768 if (shape.asNestedRects(rects)) { 1769 // Concave AA paths are expensive - try to avoid them for special cases 1770 GrOp::Owner op = StrokeRectOp::MakeNested(fContext, std::move(*paint), 1771 viewMatrix, rects); 1772 if (op) { 1773 this->addDrawOp(clip, std::move(op)); 1774 return true; 1775 } 1776 // Fall through to let path renderer handle subpixel nested rects with unequal 1777 // stroke widths along X/Y. 1778 } 1779 } 1780 } 1781 return false; 1782} 1783 1784void SurfaceDrawContext::drawShapeUsingPathRenderer(const GrClip* clip, 1785 GrPaint&& paint, 1786 GrAA aa, 1787 const SkMatrix& viewMatrix, 1788 GrStyledShape&& shape, 1789 bool attemptDrawSimple) { 1790 ASSERT_SINGLE_OWNER 1791 RETURN_IF_ABANDONED 1792 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "internalDrawPath", fContext); 1793 1794 if (!viewMatrix.isFinite() || !shape.bounds().isFinite()) { 1795 return; 1796 } 1797 1798 SkIRect clipConservativeBounds = get_clip_bounds(this, clip); 1799 1800 // Always allow paths to trigger DMSAA. 1801 GrAAType aaType = fCanUseDynamicMSAA ? GrAAType::kMSAA : this->chooseAAType(aa); 1802 1803 PathRenderer::CanDrawPathArgs canDrawArgs; 1804 canDrawArgs.fCaps = this->caps(); 1805 canDrawArgs.fProxy = this->asRenderTargetProxy(); 1806 canDrawArgs.fViewMatrix = &viewMatrix; 1807 canDrawArgs.fShape = &shape; 1808 canDrawArgs.fPaint = &paint; 1809 canDrawArgs.fSurfaceProps = &fSurfaceProps; 1810 canDrawArgs.fClipConservativeBounds = &clipConservativeBounds; 1811 canDrawArgs.fHasUserStencilSettings = false; 1812 canDrawArgs.fAAType = aaType; 1813 1814 constexpr static bool kDisallowSWPathRenderer = false; 1815 constexpr static bool kAllowSWPathRenderer = true; 1816 using DrawType = PathRendererChain::DrawType; 1817 1818 PathRenderer* pr = nullptr; 1819 1820 if (!shape.style().strokeRec().isFillStyle() && !shape.isEmpty()) { 1821 // Give the tessellation path renderer a chance to claim this stroke before we simplify it. 1822 PathRenderer* tess = this->drawingManager()->getTessellationPathRenderer(); 1823 if (tess && tess->canDrawPath(canDrawArgs) == PathRenderer::CanDrawPath::kYes) { 1824 pr = tess; 1825 } 1826 } 1827 1828 if (!pr) { 1829 // The shape isn't a stroke that can be drawn directly. Simplify if possible. 1830 shape.simplify(); 1831 1832 if (shape.isEmpty() && !shape.inverseFilled()) { 1833 return; 1834 } 1835 1836 if (attemptDrawSimple || shape.simplified()) { 1837 // Usually we enter drawShapeUsingPathRenderer() because the shape+style was too complex 1838 // for dedicated draw ops. However, if GrStyledShape was able to reduce something we 1839 // ought to try again instead of going right to path rendering. 1840 if (this->drawSimpleShape(clip, &paint, aa, viewMatrix, shape)) { 1841 return; 1842 } 1843 } 1844 1845 // Try a 1st time without applying any of the style to the geometry (and barring sw) 1846 pr = this->drawingManager()->getPathRenderer(canDrawArgs, kDisallowSWPathRenderer, 1847 DrawType::kColor); 1848 } 1849 1850 SkScalar styleScale = GrStyle::MatrixToScaleFactor(viewMatrix); 1851 if (styleScale == 0.0f) { 1852 return; 1853 } 1854 1855 if (!pr && shape.style().pathEffect()) { 1856 // It didn't work above, so try again with the path effect applied. 1857 shape = shape.applyStyle(GrStyle::Apply::kPathEffectOnly, styleScale); 1858 if (shape.isEmpty()) { 1859 return; 1860 } 1861 pr = this->drawingManager()->getPathRenderer(canDrawArgs, kDisallowSWPathRenderer, 1862 DrawType::kColor); 1863 } 1864 if (!pr) { 1865 if (shape.style().applies()) { 1866 shape = shape.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, styleScale); 1867 if (shape.isEmpty()) { 1868 return; 1869 } 1870 // This time, allow SW renderer 1871 pr = this->drawingManager()->getPathRenderer(canDrawArgs, kAllowSWPathRenderer, 1872 DrawType::kColor); 1873 } else { 1874 pr = this->drawingManager()->getSoftwarePathRenderer(); 1875#if GR_PATH_RENDERER_SPEW 1876 SkDebugf("falling back to: %s\n", pr->name()); 1877#endif 1878 } 1879 } 1880 1881 if (!pr) { 1882#ifdef SK_DEBUG 1883 SkDebugf("Unable to find path renderer compatible with path.\n"); 1884#endif 1885 return; 1886 } 1887 1888 PathRenderer::DrawPathArgs args{this->drawingManager()->getContext(), 1889 std::move(paint), 1890 &GrUserStencilSettings::kUnused, 1891 this, 1892 clip, 1893 &clipConservativeBounds, 1894 &viewMatrix, 1895 canDrawArgs.fShape, 1896 aaType, 1897 this->colorInfo().isLinearlyBlended()}; 1898 pr->drawPath(args); 1899} 1900 1901void SurfaceDrawContext::addDrawOp(const GrClip* clip, 1902 GrOp::Owner op, 1903 const std::function<WillAddOpFn>& willAddFn) { 1904 ASSERT_SINGLE_OWNER 1905 if (fContext->abandoned()) { 1906 return; 1907 } 1908 GrDrawOp* drawOp = (GrDrawOp*)op.get(); 1909 SkDEBUGCODE(this->validate();) 1910 SkDEBUGCODE(drawOp->fAddDrawOpCalled = true;) 1911 GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceDrawContext", "addDrawOp", fContext); 1912 1913 // Setup clip 1914 SkRect bounds; 1915 op_bounds(&bounds, op.get()); 1916 GrAppliedClip appliedClip(this->dimensions(), this->asSurfaceProxy()->backingStoreDimensions()); 1917 const bool opUsesMSAA = drawOp->usesMSAA(); 1918 bool skipDraw = false; 1919 if (clip) { 1920 // Have a complex clip, so defer to its early clip culling 1921 GrAAType aaType; 1922 if (opUsesMSAA) { 1923 aaType = GrAAType::kMSAA; 1924 } else { 1925 aaType = op->hasAABloat() ? GrAAType::kCoverage : GrAAType::kNone; 1926 } 1927 skipDraw = clip->apply(fContext, this, drawOp, aaType, 1928 &appliedClip, &bounds) == GrClip::Effect::kClippedOut; 1929 } else { 1930 // No clipping, so just clip the bounds against the logical render target dimensions 1931 skipDraw = !bounds.intersect(this->asSurfaceProxy()->getBoundsRect()); 1932 } 1933 1934 if (skipDraw) { 1935 return; 1936 } 1937 1938 GrClampType clampType = GrColorTypeClampType(this->colorInfo().colorType()); 1939 GrProcessorSet::Analysis analysis = drawOp->finalize(*this->caps(), &appliedClip, clampType); 1940 1941 const bool opUsesStencil = drawOp->usesStencil(); 1942 1943 // Always trigger DMSAA when there is stencil. This ensures stencil contents get properly 1944 // preserved between render passes, if needed. 1945 const bool drawNeedsMSAA = opUsesMSAA || (fCanUseDynamicMSAA && opUsesStencil); 1946 1947 // Must be called before setDstProxyView so that it sees the final bounds of the op. 1948 op->setClippedBounds(bounds); 1949 1950 // Determine if the Op will trigger the use of a separate DMSAA attachment that requires manual 1951 // resolves. 1952 // TODO: Currently usesAttachmentIfDMSAA checks if this is a textureProxy or not. This check is 1953 // really only for GL which uses a normal texture sampling when using barriers. For Vulkan it 1954 // is possible to use the msaa buffer as an input attachment even if this is not a texture. 1955 // However, support for that is not fully implemented yet in Vulkan. Once it is, this check 1956 // should change to a virtual caps check that returns whether we need to break up an OpsTask 1957 // if it has barriers and we are about to promote to MSAA. 1958 bool usesAttachmentIfDMSAA = 1959 fCanUseDynamicMSAA && 1960 (!this->caps()->msaaResolvesAutomatically() || !this->asTextureProxy()); 1961 bool opRequiresDMSAAAttachment = usesAttachmentIfDMSAA && drawNeedsMSAA; 1962 bool opTriggersDMSAAAttachment = 1963 opRequiresDMSAAAttachment && !this->getOpsTask()->usesMSAASurface(); 1964 if (opTriggersDMSAAAttachment) { 1965 // This will be the op that actually triggers use of a DMSAA attachment. Texture barriers 1966 // can't be moved to a DMSAA attachment, so if there already are any on the current opsTask 1967 // then we need to split. 1968 if (this->getOpsTask()->renderPassXferBarriers() & GrXferBarrierFlags::kTexture) { 1969 SkASSERT(!this->getOpsTask()->isColorNoOp()); 1970 this->replaceOpsTask()->setCannotMergeBackward(); 1971 } 1972 } 1973 1974 GrDstProxyView dstProxyView; 1975 if (analysis.requiresDstTexture()) { 1976 if (!this->setupDstProxyView(drawOp->bounds(), drawNeedsMSAA, &dstProxyView)) { 1977 return; 1978 } 1979#ifdef SK_DEBUG 1980 if (fCanUseDynamicMSAA && drawNeedsMSAA && !this->caps()->msaaResolvesAutomatically()) { 1981 // Since we aren't literally writing to the render target texture while using a DMSAA 1982 // attachment, we need to resolve that texture before sampling it. Ensure the current 1983 // opsTask got closed off in order to initiate an implicit resolve. 1984 SkASSERT(this->getOpsTask()->isEmpty()); 1985 } 1986#endif 1987 } 1988 1989 auto opsTask = this->getOpsTask(); 1990 if (willAddFn) { 1991 willAddFn(op.get(), opsTask->uniqueID()); 1992 } 1993 1994 // Note if the op needs stencil. Stencil clipping already called setNeedsStencil for itself, if 1995 // needed. 1996 if (opUsesStencil) { 1997 this->setNeedsStencil(); 1998 } 1999 2000#if GR_GPU_STATS && GR_TEST_UTILS 2001 if (fCanUseDynamicMSAA && drawNeedsMSAA) { 2002 if (!opsTask->usesMSAASurface()) { 2003 fContext->priv().dmsaaStats().fNumMultisampleRenderPasses++; 2004 } 2005 fContext->priv().dmsaaStats().fTriggerCounts[op->name()]++; 2006 } 2007#endif 2008 auto direct = fContext->priv().asDirectContext(); 2009 if (direct && op) { 2010 op->setGrOpTag(direct->getCurrentGrResourceTag()); 2011 } 2012 opsTask->addDrawOp(this->drawingManager(), std::move(op), drawNeedsMSAA, analysis, 2013 std::move(appliedClip), dstProxyView, 2014 GrTextureResolveManager(this->drawingManager()), *this->caps()); 2015 2016#ifdef SK_DEBUG 2017 if (fCanUseDynamicMSAA && drawNeedsMSAA) { 2018 SkASSERT(opsTask->usesMSAASurface()); 2019 } 2020#endif 2021} 2022 2023bool SurfaceDrawContext::setupDstProxyView(const SkRect& opBounds, 2024 bool opRequiresMSAA, 2025 GrDstProxyView* dstProxyView) { 2026 // If we are wrapping a vulkan secondary command buffer, we can't make a dst copy because we 2027 // don't actually have a VkImage to make a copy of. Additionally we don't have the power to 2028 // start and stop the render pass in order to make the copy. 2029 if (this->asRenderTargetProxy()->wrapsVkSecondaryCB()) { 2030 return false; 2031 } 2032 2033 // First get the dstSampleFlags as if we will put the draw into the current OpsTask 2034 auto dstSampleFlags = this->caps()->getDstSampleFlagsForProxy( 2035 this->asRenderTargetProxy(), this->getOpsTask()->usesMSAASurface() || opRequiresMSAA); 2036 2037 // If we don't have barriers for this draw then we will definitely be breaking up the OpsTask. 2038 // However, if using dynamic MSAA, the new OpsTask will not have MSAA already enabled on it 2039 // and that may allow us to use texture barriers. So we check if we can use barriers on the new 2040 // ops task and then break it up if so. 2041 if (!(dstSampleFlags & GrDstSampleFlags::kRequiresTextureBarrier) && 2042 fCanUseDynamicMSAA && this->getOpsTask()->usesMSAASurface() && !opRequiresMSAA) { 2043 auto newFlags = 2044 this->caps()->getDstSampleFlagsForProxy(this->asRenderTargetProxy(), 2045 false/*=opRequiresMSAA*/); 2046 if (newFlags & GrDstSampleFlags::kRequiresTextureBarrier) { 2047 // We can't have an empty ops task if we are in DMSAA and the ops task already returns 2048 // true for usesMSAASurface. 2049 SkASSERT(!this->getOpsTask()->isColorNoOp()); 2050 this->replaceOpsTask()->setCannotMergeBackward(); 2051 dstSampleFlags = newFlags; 2052 } 2053 } 2054 2055 if (dstSampleFlags & GrDstSampleFlags::kRequiresTextureBarrier) { 2056 // If we require a barrier to sample the dst it means we are sampling the RT itself 2057 // either as a texture or input attachment. In this case we don't need to break up the 2058 // OpsTask. 2059 dstProxyView->setProxyView(this->readSurfaceView()); 2060 dstProxyView->setOffset(0, 0); 2061 dstProxyView->setDstSampleFlags(dstSampleFlags); 2062 return true; 2063 } 2064 SkASSERT(dstSampleFlags == GrDstSampleFlags::kNone); 2065 2066 // We are using a different surface from the main color attachment to sample the dst from. If we 2067 // are in DMSAA we can just use the single sampled RT texture itself. Otherwise, we must do a 2068 // copy. 2069 // We do have to check if we ended up here becasue we don't have texture barriers but do have 2070 // msaaResolvesAutomatically (i.e. render-to-msaa-texture). In that case there will be no op or 2071 // barrier between draws to flush the render target before being used as a texture in the next 2072 // draw. So in that case we just fall through to doing a copy. 2073 if (fCanUseDynamicMSAA && opRequiresMSAA && this->asTextureProxy() && 2074 !this->caps()->msaaResolvesAutomatically() && 2075 this->caps()->dmsaaResolveCanBeUsedAsTextureInSameRenderPass()) { 2076 this->replaceOpsTaskIfModifiesColor()->setCannotMergeBackward(); 2077 dstProxyView->setProxyView(this->readSurfaceView()); 2078 dstProxyView->setOffset(0, 0); 2079 dstProxyView->setDstSampleFlags(dstSampleFlags); 2080 return true; 2081 } 2082 2083 // Now we fallback to doing a copy. 2084 2085 GrColorType colorType = this->colorInfo().colorType(); 2086 // MSAA consideration: When there is support for reading MSAA samples in the shader we could 2087 // have per-sample dst values by making the copy multisampled. 2088 GrCaps::DstCopyRestrictions restrictions = this->caps()->getDstCopyRestrictions( 2089 this->asRenderTargetProxy(), colorType); 2090 2091 SkIRect copyRect = SkIRect::MakeSize(this->asSurfaceProxy()->backingStoreDimensions()); 2092 if (!restrictions.fMustCopyWholeSrc) { 2093 // If we don't need the whole source, restrict to the op's bounds. We add an extra pixel 2094 // of padding to account for AA bloat and the unpredictable rounding of coords near pixel 2095 // centers during rasterization. 2096 SkIRect conservativeDrawBounds = opBounds.roundOut(); 2097 conservativeDrawBounds.outset(1, 1); 2098 SkAssertResult(copyRect.intersect(conservativeDrawBounds)); 2099 } 2100 2101 SkIPoint dstOffset; 2102 SkBackingFit fit; 2103 if (restrictions.fRectsMustMatch == GrSurfaceProxy::RectsMustMatch::kYes) { 2104 dstOffset = {0, 0}; 2105 fit = SkBackingFit::kExact; 2106 } else { 2107 dstOffset = {copyRect.fLeft, copyRect.fTop}; 2108 fit = SkBackingFit::kApprox; 2109 } 2110 auto copy = GrSurfaceProxy::Copy(fContext, 2111 this->asSurfaceProxyRef(), 2112 this->origin(), 2113 GrMipmapped::kNo, 2114 copyRect, 2115 fit, 2116 SkBudgeted::kYes, 2117 restrictions.fRectsMustMatch); 2118 SkASSERT(copy); 2119 2120 dstProxyView->setProxyView({std::move(copy), this->origin(), this->readSwizzle()}); 2121 dstProxyView->setOffset(dstOffset); 2122 dstProxyView->setDstSampleFlags(dstSampleFlags); 2123 return true; 2124} 2125 2126OpsTask* SurfaceDrawContext::replaceOpsTaskIfModifiesColor() { 2127 if (!this->getOpsTask()->isColorNoOp()) { 2128 this->replaceOpsTask(); 2129 } 2130 return this->getOpsTask(); 2131} 2132 2133bool SurfaceDrawContext::drawBlurImage(GrSurfaceProxyView proxyView, const SkBlurArg& blurArg) 2134{ 2135 if (!this->caps()->supportsHpsBlur(&proxyView)) { 2136 SK_LOGD("ERROR: check HpsBlur fail.\n"); 2137 return false; 2138 } 2139 this->addOp(GrOp::Make<BlurOp>(fContext, std::move(proxyView), blurArg)); 2140 return true; 2141} 2142 2143} // namespace skgpu::v1 2144