Brian Salomon | 590f567 | 2020-12-16 11:44:47 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2020 Google LLC |
| 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/GrSurfaceFillContext.h" |
| 9 | |
| 10 | #include "include/private/GrImageContext.h" |
| 11 | #include "src/gpu/GrImageContextPriv.h" |
| 12 | #include "src/gpu/GrProxyProvider.h" |
| 13 | #include "src/gpu/GrSurfaceDrawContext.h" |
| 14 | #include "src/gpu/ops/GrClearOp.h" |
| 15 | #include "src/gpu/ops/GrFillRectOp.h" |
| 16 | |
| 17 | #define ASSERT_SINGLE_OWNER GR_ASSERT_SINGLE_OWNER(this->singleOwner()) |
| 18 | #define RETURN_IF_ABANDONED if (fContext->abandoned()) { return; } |
| 19 | |
| 20 | class AutoCheckFlush { |
| 21 | public: |
| 22 | AutoCheckFlush(GrDrawingManager* drawingManager) : fDrawingManager(drawingManager) { |
| 23 | SkASSERT(fDrawingManager); |
| 24 | } |
| 25 | ~AutoCheckFlush() { fDrawingManager->flushIfNecessary(); } |
| 26 | |
| 27 | private: |
| 28 | GrDrawingManager* fDrawingManager; |
| 29 | }; |
| 30 | |
| 31 | static inline GrColorType color_type_fallback(GrColorType ct) { |
| 32 | switch (ct) { |
| 33 | // kRGBA_8888 is our default fallback for many color types that may not have renderable |
| 34 | // backend formats. |
| 35 | case GrColorType::kAlpha_8: |
| 36 | case GrColorType::kBGR_565: |
| 37 | case GrColorType::kABGR_4444: |
| 38 | case GrColorType::kBGRA_8888: |
| 39 | case GrColorType::kRGBA_1010102: |
| 40 | case GrColorType::kBGRA_1010102: |
| 41 | case GrColorType::kRGBA_F16: |
| 42 | case GrColorType::kRGBA_F16_Clamped: |
| 43 | return GrColorType::kRGBA_8888; |
| 44 | case GrColorType::kAlpha_F16: |
| 45 | return GrColorType::kRGBA_F16; |
| 46 | case GrColorType::kGray_8: |
| 47 | return GrColorType::kRGB_888x; |
| 48 | default: |
| 49 | return GrColorType::kUnknown; |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | std::tuple<GrColorType, GrBackendFormat> GrSurfaceFillContext::GetFallbackColorTypeAndFormat( |
| 54 | GrImageContext* context, GrColorType colorType, int sampleCnt) { |
| 55 | auto caps = context->priv().caps(); |
| 56 | do { |
| 57 | auto format = caps->getDefaultBackendFormat(colorType, GrRenderable::kYes); |
| 58 | // We continue to the fallback color type if there no default renderable format or we |
| 59 | // requested msaa and the format doesn't support msaa. |
| 60 | if (format.isValid() && caps->isFormatRenderable(format, sampleCnt)) { |
| 61 | return {colorType, format}; |
| 62 | } |
| 63 | colorType = color_type_fallback(colorType); |
| 64 | } while (colorType != GrColorType::kUnknown); |
| 65 | return {GrColorType::kUnknown, {}}; |
| 66 | } |
| 67 | |
| 68 | std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::Make(GrRecordingContext* context, |
| 69 | SkAlphaType alphaType, |
| 70 | sk_sp<SkColorSpace> colorSpace, |
| 71 | SkISize dimensions, |
| 72 | SkBackingFit fit, |
| 73 | const GrBackendFormat& format, |
| 74 | int sampleCount, |
| 75 | GrMipmapped mipmapped, |
| 76 | GrProtected isProtected, |
| 77 | GrSwizzle readSwizzle, |
| 78 | GrSwizzle writeSwizzle, |
| 79 | GrSurfaceOrigin origin, |
| 80 | SkBudgeted budgeted) { |
| 81 | SkASSERT(context); |
| 82 | SkASSERT(!dimensions.isEmpty()); |
| 83 | SkASSERT(sampleCount >= 1); |
| 84 | SkASSERT(format.isValid() && format.backend() == context->backend()); |
| 85 | if (alphaType == kPremul_SkAlphaType || alphaType == kOpaque_SkAlphaType) { |
| 86 | return GrSurfaceDrawContext::Make(context, |
| 87 | std::move(colorSpace), |
| 88 | fit, |
| 89 | dimensions, |
| 90 | format, |
| 91 | sampleCount, |
| 92 | mipmapped, |
| 93 | isProtected, |
| 94 | readSwizzle, |
| 95 | writeSwizzle, |
| 96 | origin, |
| 97 | budgeted, |
| 98 | /* surface props*/ nullptr); |
| 99 | } |
| 100 | |
| 101 | sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy(format, |
| 102 | dimensions, |
| 103 | GrRenderable::kYes, |
| 104 | sampleCount, |
| 105 | mipmapped, |
| 106 | fit, |
| 107 | budgeted, |
| 108 | isProtected); |
| 109 | if (!proxy) { |
| 110 | return nullptr; |
| 111 | } |
| 112 | GrImageInfo info(GrColorType::kUnknown, alphaType, std::move(colorSpace), dimensions); |
| 113 | GrSurfaceProxyView readView( proxy, origin, readSwizzle); |
| 114 | GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle); |
| 115 | auto fillContext = std::make_unique<GrSurfaceFillContext>(context, |
| 116 | std::move(readView), |
| 117 | std::move(writeView), |
| 118 | info.colorInfo()); |
| 119 | fillContext->discard(); |
| 120 | return fillContext; |
| 121 | } |
| 122 | |
| 123 | std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::Make(GrRecordingContext* context, |
| 124 | GrImageInfo info, |
| 125 | SkBackingFit fit, |
| 126 | int sampleCount, |
| 127 | GrMipmapped mipmapped, |
| 128 | GrProtected isProtected, |
| 129 | GrSurfaceOrigin origin, |
| 130 | SkBudgeted budgeted) { |
| 131 | if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) { |
| 132 | return GrSurfaceDrawContext::Make(context, |
| 133 | info.colorType(), |
| 134 | info.refColorSpace(), |
| 135 | fit, |
| 136 | info.dimensions(), |
| 137 | sampleCount, |
| 138 | mipmapped, |
| 139 | isProtected, |
| 140 | origin, |
| 141 | budgeted, |
| 142 | nullptr); |
| 143 | } |
| 144 | GrBackendFormat format = context->priv().caps()->getDefaultBackendFormat(info.colorType(), |
| 145 | GrRenderable::kYes); |
| 146 | sk_sp<GrTextureProxy> proxy = context->priv().proxyProvider()->createProxy(format, |
| 147 | info.dimensions(), |
| 148 | GrRenderable::kYes, |
| 149 | sampleCount, |
| 150 | mipmapped, |
| 151 | fit, |
| 152 | budgeted, |
| 153 | isProtected); |
| 154 | if (!proxy) { |
| 155 | return nullptr; |
| 156 | } |
| 157 | GrSwizzle readSwizzle = context->priv().caps()->getReadSwizzle (format, info.colorType()); |
| 158 | GrSwizzle writeSwizzle = context->priv().caps()->getWriteSwizzle(format, info.colorType()); |
| 159 | |
| 160 | GrSurfaceProxyView readView( proxy, origin, readSwizzle); |
| 161 | GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle); |
| 162 | auto fillContext = std::make_unique<GrSurfaceFillContext>(context, |
| 163 | std::move(readView), |
| 164 | std::move(writeView), |
| 165 | info.colorInfo()); |
| 166 | fillContext->discard(); |
| 167 | return fillContext; |
| 168 | } |
| 169 | |
| 170 | std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::MakeWithFallback( |
| 171 | GrRecordingContext* context, |
| 172 | GrImageInfo info, |
| 173 | SkBackingFit fit, |
| 174 | int sampleCount, |
| 175 | GrMipmapped mipmapped, |
| 176 | GrProtected isProtected, |
| 177 | GrSurfaceOrigin origin, |
| 178 | SkBudgeted budgeted) { |
| 179 | if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) { |
| 180 | return GrSurfaceDrawContext::MakeWithFallback(context, |
| 181 | info.colorType(), |
| 182 | info.refColorSpace(), |
| 183 | fit, |
| 184 | info.dimensions(), |
| 185 | sampleCount, |
| 186 | mipmapped, |
| 187 | isProtected, |
| 188 | origin, |
| 189 | budgeted, |
| 190 | nullptr); |
| 191 | } |
| 192 | auto [ct, _] = GetFallbackColorTypeAndFormat(context, info.colorType(), sampleCount); |
| 193 | if (ct == GrColorType::kUnknown) { |
| 194 | return nullptr; |
| 195 | } |
| 196 | info = info.makeColorType(ct); |
| 197 | return GrSurfaceFillContext::Make(context, |
| 198 | info, |
| 199 | fit, |
| 200 | sampleCount, |
| 201 | mipmapped, |
| 202 | isProtected, |
| 203 | origin, |
| 204 | budgeted); |
| 205 | } |
| 206 | |
| 207 | std::unique_ptr<GrSurfaceFillContext> GrSurfaceFillContext::MakeFromBackendTexture( |
| 208 | GrRecordingContext* context, |
| 209 | GrColorInfo info, |
| 210 | const GrBackendTexture& tex, |
| 211 | int sampleCount, |
| 212 | GrSurfaceOrigin origin, |
| 213 | sk_sp<GrRefCntedCallback> releaseHelper) { |
| 214 | SkASSERT(sampleCount > 0); |
| 215 | |
| 216 | if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) { |
| 217 | return GrSurfaceDrawContext::MakeFromBackendTexture(context, |
| 218 | info.colorType(), |
| 219 | info.refColorSpace(), |
| 220 | tex, |
| 221 | sampleCount, |
| 222 | origin, |
| 223 | nullptr, |
| 224 | std::move(releaseHelper)); |
| 225 | } |
| 226 | const GrBackendFormat& format = tex.getBackendFormat(); |
| 227 | GrSwizzle readSwizzle, writeSwizzle; |
| 228 | if (info.colorType() != GrColorType::kUnknown) { |
| 229 | if (!context->priv().caps()->areColorTypeAndFormatCompatible(info.colorType(), format)) { |
| 230 | return nullptr; |
| 231 | } |
| 232 | readSwizzle = context->priv().caps()->getReadSwizzle (format, info.colorType()); |
| 233 | writeSwizzle = context->priv().caps()->getWriteSwizzle(format, info.colorType()); |
| 234 | } |
| 235 | |
| 236 | sk_sp<GrTextureProxy> proxy(context->priv().proxyProvider()->wrapRenderableBackendTexture( |
| 237 | tex, sampleCount, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, |
| 238 | std::move(releaseHelper))); |
| 239 | if (!proxy) { |
| 240 | return nullptr; |
| 241 | } |
| 242 | |
| 243 | GrSurfaceProxyView readView( proxy, origin, readSwizzle); |
| 244 | GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle); |
| 245 | |
| 246 | return std::make_unique<GrSurfaceFillContext>(context, |
| 247 | std::move(readView), |
| 248 | std::move(writeView), |
| 249 | std::move(info)); |
| 250 | |
| 251 | } |
| 252 | |
| 253 | // In MDB mode the reffing of the 'getLastOpsTask' call's result allows in-progress |
| 254 | // GrOpsTask to be picked up and added to by GrSurfaceFillContext lower in the call |
| 255 | // stack. When this occurs with a closed GrOpsTask, a new one will be allocated |
| 256 | // when the GrSurfaceFillContext attempts to use it (via getOpsTask). |
| 257 | GrSurfaceFillContext::GrSurfaceFillContext(GrRecordingContext* context, |
| 258 | GrSurfaceProxyView readView, |
| 259 | GrSurfaceProxyView writeView, |
| 260 | const GrColorInfo& colorInfo, |
| 261 | bool flushTimeOpsTask) |
| 262 | : GrSurfaceContext(context, std::move(readView), std::move(colorInfo)) |
| 263 | , fWriteView(std::move(writeView)) |
| 264 | , fFlushTimeOpsTask(flushTimeOpsTask) { |
| 265 | fOpsTask = sk_ref_sp(context->priv().drawingManager()->getLastOpsTask(this->asSurfaceProxy())); |
| 266 | SkASSERT(this->asSurfaceProxy() == fWriteView.proxy()); |
| 267 | SkASSERT(this->origin() == fWriteView.origin()); |
| 268 | |
| 269 | SkDEBUGCODE(this->validate();) |
| 270 | } |
| 271 | |
| 272 | void GrSurfaceFillContext::fillRectWithFP(const SkIRect& dstRect, |
| 273 | std::unique_ptr<GrFragmentProcessor> fp) { |
| 274 | ASSERT_SINGLE_OWNER |
| 275 | RETURN_IF_ABANDONED |
| 276 | SkDEBUGCODE(this->validate();) |
| 277 | GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceFillContext", "fillRectWithFP", fContext); |
| 278 | |
| 279 | AutoCheckFlush acf(this->drawingManager()); |
| 280 | |
| 281 | GrPaint paint; |
| 282 | paint.setColorFragmentProcessor(std::move(fp)); |
| 283 | paint.setPorterDuffXPFactory(SkBlendMode::kSrc); |
| 284 | auto op = GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(), |
| 285 | SkRect::Make(dstRect)); |
| 286 | this->addDrawOp(std::move(op)); |
| 287 | } |
| 288 | |
| 289 | void GrSurfaceFillContext::addDrawOp(GrOp::Owner owner) { |
| 290 | GrDrawOp* op = static_cast<GrDrawOp*>(owner.get()); |
| 291 | GrClampType clampType = GrColorTypeClampType(this->colorInfo().colorType()); |
| 292 | auto clip = GrAppliedClip::Disabled(); |
| 293 | const GrCaps& caps = *this->caps(); |
| 294 | GrProcessorSet::Analysis analysis = op->finalize(caps, |
| 295 | &clip, |
| 296 | /*mixed sample coverage*/ false, |
| 297 | clampType); |
| 298 | SkASSERT(!(op->fixedFunctionFlags() & GrDrawOp::FixedFunctionFlags::kUsesStencil)); |
| 299 | SkASSERT(!analysis.requiresDstTexture()); |
| 300 | SkRect bounds = owner->bounds(); |
| 301 | // We shouldn't have coverage AA or hairline draws in fill contexts. |
| 302 | SkASSERT(!op->hasAABloat() && !op->hasZeroArea()); |
| 303 | if (!bounds.intersect(this->asSurfaceProxy()->getBoundsRect())) { |
| 304 | return; |
| 305 | } |
| 306 | op->setClippedBounds(op->bounds()); |
| 307 | SkDEBUGCODE(op->fAddDrawOpCalled = true;) |
| 308 | |
| 309 | GrXferProcessor::DstProxyView dstProxyView; |
| 310 | this->getOpsTask()->addDrawOp(fContext->priv().drawingManager(), |
| 311 | std::move(owner), |
| 312 | analysis, |
| 313 | std::move(clip), |
| 314 | dstProxyView, |
| 315 | GrTextureResolveManager(this->drawingManager()), |
| 316 | caps); |
| 317 | } |
| 318 | |
| 319 | void GrSurfaceFillContext::ClearToGrPaint(std::array<float, 4> color, GrPaint* paint) { |
| 320 | paint->setColor4f({color[0], color[1], color[2], color[3]}); |
| 321 | if (color[3] == 1.f) { |
| 322 | // Can just rely on the src-over blend mode to do the right thing. |
| 323 | // This may improve batching. |
| 324 | paint->setPorterDuffXPFactory(SkBlendMode::kSrcOver); |
| 325 | } else { |
| 326 | // A clear overwrites the prior color, so even if it's transparent, it behaves as if it |
| 327 | // were src blended |
| 328 | paint->setPorterDuffXPFactory(SkBlendMode::kSrc); |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | void GrSurfaceFillContext::addOp(GrOp::Owner op) { |
| 333 | GrDrawingManager* drawingMgr = this->drawingManager(); |
| 334 | this->getOpsTask()->addOp(drawingMgr, |
| 335 | std::move(op), |
| 336 | GrTextureResolveManager(drawingMgr), |
| 337 | *this->caps()); |
| 338 | } |
| 339 | |
| 340 | GrOpsTask* GrSurfaceFillContext::getOpsTask() { |
| 341 | ASSERT_SINGLE_OWNER |
| 342 | SkDEBUGCODE(this->validate();) |
| 343 | |
| 344 | if (!fOpsTask || fOpsTask->isClosed()) { |
| 345 | sk_sp<GrOpsTask> newOpsTask = this->drawingManager()->newOpsTask(this->writeSurfaceView(), |
| 346 | fFlushTimeOpsTask); |
| 347 | if (fOpsTask) { |
| 348 | this->willReplaceOpsTask(fOpsTask.get(), newOpsTask.get()); |
| 349 | } |
| 350 | fOpsTask = std::move(newOpsTask); |
| 351 | } |
| 352 | SkASSERT(!fOpsTask->isClosed()); |
| 353 | return fOpsTask.get(); |
| 354 | } |
| 355 | |
| 356 | #ifdef SK_DEBUG |
| 357 | void GrSurfaceFillContext::onValidate() const { |
| 358 | if (fOpsTask && !fOpsTask->isClosed()) { |
| 359 | SkASSERT(this->drawingManager()->getLastRenderTask(fWriteView.proxy()) == fOpsTask.get()); |
| 360 | } |
| 361 | } |
| 362 | #endif |
| 363 | |
| 364 | void GrSurfaceFillContext::discard() { |
| 365 | ASSERT_SINGLE_OWNER |
| 366 | RETURN_IF_ABANDONED |
| 367 | SkDEBUGCODE(this->validate();) |
| 368 | GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceDrawContext", "discard", fContext); |
| 369 | |
| 370 | AutoCheckFlush acf(this->drawingManager()); |
| 371 | |
| 372 | this->getOpsTask()->discard(); |
| 373 | } |
| 374 | |
| 375 | void GrSurfaceFillContext::internalClear(const SkIRect* scissor, |
| 376 | std::array<float, 4> color, |
| 377 | bool upgradePartialToFull) { |
| 378 | ASSERT_SINGLE_OWNER |
| 379 | RETURN_IF_ABANDONED |
| 380 | SkDEBUGCODE(this->validate();) |
| 381 | GR_CREATE_TRACE_MARKER_CONTEXT("GrSurfaceDrawContext", "clear", fContext); |
| 382 | |
| 383 | // There are three ways clears are handled: load ops, native clears, and draws. Load ops are |
| 384 | // only for fullscreen clears; native clears can be fullscreen or with scissors if the backend |
| 385 | // supports then. Drawing an axis-aligned rect is the fallback path. |
| 386 | GrScissorState scissorState(this->asSurfaceProxy()->backingStoreDimensions()); |
| 387 | if (scissor && !scissorState.set(*scissor)) { |
| 388 | // The clear is offscreen, so skip it (normally this would be handled by addDrawOp, |
| 389 | // except clear ops are not draw ops). |
| 390 | return; |
| 391 | } |
| 392 | |
| 393 | // If we have a scissor but it's okay to clear beyond it for performance reasons, then disable |
| 394 | // the test. We only do this when the clear would be handled by a load op or natively. |
| 395 | if (scissorState.enabled() && !this->caps()->performColorClearsAsDraws()) { |
| 396 | if (upgradePartialToFull && (this->caps()->preferFullscreenClears() || |
| 397 | this->caps()->shouldInitializeTextures())) { |
| 398 | // TODO: wrt the shouldInitializeTextures path, it would be more performant to |
| 399 | // only clear the entire target if we knew it had not been cleared before. As |
| 400 | // is this could end up doing a lot of redundant clears. |
| 401 | scissorState.setDisabled(); |
| 402 | } else { |
| 403 | // Unlike with stencil clears, we also allow clears up to the logical dimensions of the |
| 404 | // render target to overflow into any approx-fit padding of the backing store dimensions |
| 405 | scissorState.relaxTest(this->dimensions()); |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | if (!scissorState.enabled()) { |
| 410 | // This is a fullscreen clear, so could be handled as a load op. Regardless, we can also |
| 411 | // discard all prior ops in the current task since the color buffer will be overwritten. |
| 412 | GrOpsTask* opsTask = this->getOpsTask(); |
| 413 | if (opsTask->resetForFullscreenClear(this->canDiscardPreviousOpsOnFullClear()) && |
| 414 | !this->caps()->performColorClearsAsDraws()) { |
| 415 | color = this->writeSurfaceView().swizzle().applyTo(color); |
| 416 | // The op list was emptied and native clears are allowed, so just use the load op |
| 417 | opsTask->setColorLoadOp(GrLoadOp::kClear, color); |
| 418 | return; |
| 419 | } else { |
| 420 | // Will use an op for the clear, reset the load op to discard since the op will |
| 421 | // blow away the color buffer contents |
| 422 | opsTask->setColorLoadOp(GrLoadOp::kDiscard); |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | // At this point we are either a partial clear or a fullscreen clear that couldn't be applied |
| 427 | // as a load op. |
| 428 | bool clearAsDraw = this->caps()->performColorClearsAsDraws() || |
| 429 | (scissorState.enabled() && this->caps()->performPartialClearsAsDraws()); |
| 430 | if (clearAsDraw) { |
| 431 | GrPaint paint; |
| 432 | ClearToGrPaint(color, &paint); |
| 433 | auto op = GrFillRectOp::MakeNonAARect(fContext, std::move(paint), SkMatrix::I(), |
| 434 | SkRect::Make(scissorState.rect())); |
| 435 | this->addDrawOp(std::move(op)); |
| 436 | } else { |
| 437 | color = this->writeSurfaceView().swizzle().applyTo(color); |
| 438 | this->addOp(GrClearOp::MakeColor(fContext, scissorState, color)); |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | bool GrSurfaceFillContext::blitTexture(GrSurfaceProxyView view, |
| 443 | const SkIRect& srcRect, |
| 444 | const SkIPoint& dstPoint) { |
| 445 | SkASSERT(view.asTextureProxy()); |
| 446 | SkIRect clippedSrcRect; |
| 447 | SkIPoint clippedDstPoint; |
| 448 | if (!GrClipSrcRectAndDstPoint(this->dimensions(), |
| 449 | view.dimensions(), |
| 450 | srcRect, |
| 451 | dstPoint, |
| 452 | &clippedSrcRect, |
| 453 | &clippedDstPoint)) { |
| 454 | return false; |
| 455 | } |
| 456 | |
| 457 | auto fp = GrTextureEffect::Make(std::move(view), kUnknown_SkAlphaType); |
| 458 | auto dstRect = SkIRect::MakePtSize(clippedDstPoint, clippedSrcRect.size()); |
| 459 | auto srcRectF = SkRect::Make(clippedSrcRect); |
| 460 | this->fillRectToRectWithFP(srcRectF, dstRect, std::move(fp)); |
| 461 | return true; |
| 462 | } |