1/* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "src/gpu/GrSurfaceProxy.h" 9#include "src/gpu/GrSurfaceProxyPriv.h" 10 11#include "include/gpu/GrRecordingContext.h" 12#include "src/core/SkMathPriv.h" 13#include "src/gpu/GrAttachment.h" 14#include "src/gpu/GrCaps.h" 15#include "src/gpu/GrGpuResourcePriv.h" 16#include "src/gpu/GrImageInfo.h" 17#include "src/gpu/GrRecordingContextPriv.h" 18#include "src/gpu/GrResourceProvider.h" 19#include "src/gpu/GrSurface.h" 20#include "src/gpu/GrTexture.h" 21#include "src/gpu/GrTextureRenderTargetProxy.h" 22#include "src/gpu/SurfaceFillContext.h" 23 24#ifdef SK_DEBUG 25#include "include/gpu/GrDirectContext.h" 26#include "src/gpu/GrDirectContextPriv.h" 27#include "src/gpu/GrRenderTarget.h" 28 29static bool is_valid_lazy(const SkISize& dimensions, SkBackingFit fit) { 30 // A "fully" lazy proxy's width and height are not known until instantiation time. 31 // So fully lazy proxies are created with width and height < 0. Regular lazy proxies must be 32 // created with positive widths and heights. The width and height are set to 0 only after a 33 // failed instantiation. The former must be "approximate" fit while the latter can be either. 34 return ((dimensions.fWidth < 0 && dimensions.fHeight < 0 && SkBackingFit::kApprox == fit) || 35 (dimensions.fWidth > 0 && dimensions.fHeight > 0)); 36} 37 38static bool is_valid_non_lazy(SkISize dimensions) { 39 return dimensions.fWidth > 0 && dimensions.fHeight > 0; 40} 41#endif 42 43// Deferred version 44GrSurfaceProxy::GrSurfaceProxy(const GrBackendFormat& format, 45 SkISize dimensions, 46 SkBackingFit fit, 47 SkBudgeted budgeted, 48 GrProtected isProtected, 49 GrInternalSurfaceFlags surfaceFlags, 50 UseAllocator useAllocator) 51 : fSurfaceFlags(surfaceFlags) 52 , fFormat(format) 53 , fDimensions(dimensions) 54 , fFit(fit) 55 , fBudgeted(budgeted) 56 , fUseAllocator(useAllocator) 57 , fIsProtected(isProtected) { 58 SkASSERT(fFormat.isValid()); 59 SkASSERT(is_valid_non_lazy(dimensions)); 60} 61 62// Lazy-callback version 63GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback, 64 const GrBackendFormat& format, 65 SkISize dimensions, 66 SkBackingFit fit, 67 SkBudgeted budgeted, 68 GrProtected isProtected, 69 GrInternalSurfaceFlags surfaceFlags, 70 UseAllocator useAllocator) 71 : fSurfaceFlags(surfaceFlags) 72 , fFormat(format) 73 , fDimensions(dimensions) 74 , fFit(fit) 75 , fBudgeted(budgeted) 76 , fUseAllocator(useAllocator) 77 , fLazyInstantiateCallback(std::move(callback)) 78 , fIsProtected(isProtected) { 79 SkASSERT(fFormat.isValid()); 80 SkASSERT(fLazyInstantiateCallback); 81 SkASSERT(is_valid_lazy(dimensions, fit)); 82} 83 84// Wrapped version 85GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, 86 SkBackingFit fit, 87 UseAllocator useAllocator) 88 : fTarget(std::move(surface)) 89 , fSurfaceFlags(fTarget->flags()) 90 , fFormat(fTarget->backendFormat()) 91 , fDimensions(fTarget->dimensions()) 92 , fFit(fit) 93 , fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted 94 ? SkBudgeted::kYes 95 : SkBudgeted::kNo) 96 , fUseAllocator(useAllocator) 97 , fUniqueID(fTarget->uniqueID()) // Note: converting from unique resource ID to a proxy ID! 98 , fIsProtected(fTarget->isProtected() ? GrProtected::kYes : GrProtected::kNo) { 99 SkASSERT(fFormat.isValid()); 100} 101 102GrSurfaceProxy::~GrSurfaceProxy() { 103} 104 105sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider, 106 int sampleCnt, 107 GrRenderable renderable, 108 GrMipmapped mipMapped) const { 109 SkASSERT(mipMapped == GrMipmapped::kNo || fFit == SkBackingFit::kExact); 110 SkASSERT(!this->isLazy()); 111 SkASSERT(!fTarget); 112 113 sk_sp<GrSurface> surface; 114 if (SkBackingFit::kApprox == fFit) { 115 surface = resourceProvider->createApproxTexture(fDimensions, 116 fFormat, 117 fFormat.textureType(), 118 renderable, 119 sampleCnt, 120 fIsProtected); 121 } else { 122 surface = resourceProvider->createTexture(fDimensions, 123 fFormat, 124 fFormat.textureType(), 125 renderable, 126 sampleCnt, 127 mipMapped, 128 fBudgeted, 129 fIsProtected); 130 } 131 if (!surface) { 132 return nullptr; 133 } 134 135 if (fGrProxyTag.isGrTagValid()) { 136 surface->setResourceTag(fGrProxyTag); 137 } 138 return surface; 139} 140 141bool GrSurfaceProxy::canSkipResourceAllocator() const { 142 if (fUseAllocator == UseAllocator::kNo) { 143 // Usually an atlas or onFlush proxy 144 return true; 145 } 146 147 auto peek = this->peekSurface(); 148 if (!peek) { 149 return false; 150 } 151 // If this resource is already allocated and not recyclable then the resource allocator does 152 // not need to do anything with it. 153 return !peek->resourcePriv().getScratchKey().isValid(); 154} 155 156void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) { 157 SkASSERT(!fTarget && surface); 158 159 SkDEBUGCODE(this->validateSurface(surface.get());) 160 161 fTarget = std::move(surface); 162 163#ifdef SK_DEBUG 164 if (this->asRenderTargetProxy()) { 165 SkASSERT(fTarget->asRenderTarget()); 166 } 167 168 // In order to give DDL users some flexibility in the destination of there DDLs, 169 // a DDL's target proxy can be more conservative (and thus require less memory) 170 // than the actual GrSurface used to fulfill it. 171 if (!this->isDDLTarget() && kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) { 172 // TODO(11373): Can this check be exact? 173 SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly()); 174 } 175#endif 176} 177 178bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt, 179 GrRenderable renderable, GrMipmapped mipMapped, 180 const GrUniqueKey* uniqueKey) { 181 SkASSERT(!this->isLazy()); 182 if (fTarget) { 183 if (uniqueKey && uniqueKey->isValid()) { 184 SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey); 185 } 186 return true; 187 } 188 189 sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, renderable, 190 mipMapped); 191 if (!surface) { 192 return false; 193 } 194 195 // If there was an invalidation message pending for this key, we might have just processed it, 196 // causing the key (stored on this proxy) to become invalid. 197 if (uniqueKey && uniqueKey->isValid()) { 198 resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get()); 199 } 200 201 this->assign(std::move(surface)); 202 203 return true; 204} 205 206void GrSurfaceProxy::deinstantiate() { 207 SkASSERT(this->isInstantiated()); 208 fTarget = nullptr; 209} 210 211void GrSurfaceProxy::computeScratchKey(const GrCaps& caps, GrScratchKey* key) const { 212 SkASSERT(!this->isFullyLazy()); 213 GrRenderable renderable = GrRenderable::kNo; 214 int sampleCount = 1; 215 if (const auto* rtp = this->asRenderTargetProxy()) { 216 renderable = GrRenderable::kYes; 217 sampleCount = rtp->numSamples(); 218 } 219 220 const GrTextureProxy* tp = this->asTextureProxy(); 221 GrMipmapped mipMapped = GrMipmapped::kNo; 222 if (tp) { 223 mipMapped = tp->mipmapped(); 224 } 225 226 GrTexture::ComputeScratchKey(caps, this->backendFormat(), this->backingStoreDimensions(), 227 renderable, sampleCount, mipMapped, fIsProtected, key); 228} 229 230SkISize GrSurfaceProxy::backingStoreDimensions() const { 231 SkASSERT(!this->isFullyLazy()); 232 if (fTarget) { 233 return fTarget->dimensions(); 234 } 235 236 if (SkBackingFit::kExact == fFit) { 237 return fDimensions; 238 } 239 return GrResourceProvider::MakeApprox(fDimensions); 240} 241 242bool GrSurfaceProxy::isFunctionallyExact() const { 243 SkASSERT(!this->isFullyLazy()); 244 return fFit == SkBackingFit::kExact || 245 fDimensions == GrResourceProvider::MakeApprox(fDimensions); 246} 247 248bool GrSurfaceProxy::isFormatCompressed(const GrCaps* caps) const { 249 return caps->isFormatCompressed(this->backendFormat()); 250} 251 252#ifdef SK_DEBUG 253void GrSurfaceProxy::validate(GrContext_Base* context) const { 254 if (fTarget) { 255 SkASSERT(fTarget->getContext()->priv().matches(context)); 256 } 257} 258#endif 259 260sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* rContext, 261 sk_sp<GrSurfaceProxy> src, 262 GrSurfaceOrigin origin, 263 GrMipmapped mipMapped, 264 SkIRect srcRect, 265 SkBackingFit fit, 266 SkBudgeted budgeted, 267 RectsMustMatch rectsMustMatch, 268 sk_sp<GrRenderTask>* outTask) { 269 SkASSERT(!src->isFullyLazy()); 270 int width; 271 int height; 272 273 SkIPoint dstPoint; 274 if (rectsMustMatch == RectsMustMatch::kYes) { 275 width = src->width(); 276 height = src->height(); 277 dstPoint = {srcRect.fLeft, srcRect.fTop}; 278 } else { 279 width = srcRect.width(); 280 height = srcRect.height(); 281 dstPoint = {0, 0}; 282 } 283 284 if (!srcRect.intersect(SkIRect::MakeSize(src->dimensions()))) { 285 return {}; 286 } 287 auto format = src->backendFormat().makeTexture2D(); 288 SkASSERT(format.isValid()); 289 290 if (src->backendFormat().textureType() != GrTextureType::kExternal) { 291 GrImageInfo info(GrColorType::kUnknown, kUnknown_SkAlphaType, nullptr, {width, height}); 292 auto dstContext = rContext->priv().makeSC(info, 293 format, 294 fit, 295 origin, 296 GrRenderable::kNo, 297 1, 298 mipMapped, 299 src->isProtected(), 300 budgeted); 301 sk_sp<GrRenderTask> copyTask; 302 if (dstContext && (copyTask = dstContext->copy(src, srcRect, dstPoint))) { 303 if (outTask) { 304 *outTask = std::move(copyTask); 305 } 306 return dstContext->asSurfaceProxyRef(); 307 } 308 } 309 if (src->asTextureProxy()) { 310 auto dstContext = rContext->priv().makeSFC(kUnknown_SkAlphaType, 311 nullptr, 312 {width, height}, 313 fit, 314 format, 315 1, 316 mipMapped, 317 src->isProtected(), 318 GrSwizzle::RGBA(), 319 GrSwizzle::RGBA(), 320 origin, 321 budgeted); 322 GrSurfaceProxyView view(std::move(src), origin, GrSwizzle::RGBA()); 323 if (dstContext && dstContext->blitTexture(std::move(view), srcRect, dstPoint)) { 324 if (outTask) { 325 *outTask = dstContext->refRenderTask(); 326 } 327 return dstContext->asSurfaceProxyRef(); 328 } 329 } 330 // Can't use backend copies or draws. 331 return nullptr; 332} 333 334sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* context, 335 sk_sp<GrSurfaceProxy> src, 336 GrSurfaceOrigin origin, 337 GrMipmapped mipMapped, 338 SkBackingFit fit, 339 SkBudgeted budgeted, 340 sk_sp<GrRenderTask>* outTask) { 341 SkASSERT(!src->isFullyLazy()); 342 auto rect = SkIRect::MakeSize(src->dimensions()); 343 return Copy(context, 344 std::move(src), 345 origin, 346 mipMapped, 347 rect, 348 fit, 349 budgeted, 350 RectsMustMatch::kNo, 351 outTask); 352} 353 354#if GR_TEST_UTILS 355int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const { 356 if (fTarget) { 357 return fTarget->testingOnly_getRefCnt(); 358 } 359 360 return -1; // no backing GrSurface 361} 362 363GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const { 364 return fSurfaceFlags; 365} 366 367SkString GrSurfaceProxy::dump() const { 368 SkString tmp; 369 370 tmp.appendf("proxyID: %d - surfaceID: %d", 371 this->uniqueID().asUInt(), 372 this->peekSurface() ? this->peekSurface()->uniqueID().asUInt() 373 : -1); 374 return tmp; 375} 376 377#endif 378 379void GrSurfaceProxyPriv::exactify(bool allocatedCaseOnly) { 380 SkASSERT(!fProxy->isFullyLazy()); 381 if (this->isExact()) { 382 return; 383 } 384 385 SkASSERT(SkBackingFit::kApprox == fProxy->fFit); 386 387 if (fProxy->fTarget) { 388 // The kApprox but already instantiated case. Setting the proxy's width & height to 389 // the instantiated width & height could have side-effects going forward, since we're 390 // obliterating the area of interest information. This call (exactify) only used 391 // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be 392 // used for additional draws. 393 fProxy->fDimensions = fProxy->fTarget->dimensions(); 394 return; 395 } 396 397#ifndef SK_CRIPPLE_TEXTURE_REUSE 398 // In the post-implicit-allocation world we can't convert this proxy to be exact fit 399 // at this point. With explicit allocation switching this to exact will result in a 400 // different allocation at flush time. With implicit allocation, allocation would occur 401 // at draw time (rather than flush time) so this pathway was encountered less often (if 402 // at all). 403 if (allocatedCaseOnly) { 404 return; 405 } 406#endif 407 408 // The kApprox uninstantiated case. Making this proxy be exact should be okay. 409 // It could mess things up if prior decisions were based on the approximate size. 410 fProxy->fFit = SkBackingFit::kExact; 411 // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has 412 // already been computed we want to leave it alone so that amount will be removed when 413 // the special image goes away. If it hasn't been computed yet it might as well compute the 414 // exact amount. 415} 416 417bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) { 418 SkASSERT(fProxy->isLazy()); 419 420 sk_sp<GrSurface> surface; 421 if (const auto& uniqueKey = fProxy->getUniqueKey(); uniqueKey.isValid()) { 422 // First try to reattach to a cached version if the proxy is uniquely keyed 423 surface = resourceProvider->findByUniqueKey<GrSurface>(uniqueKey); 424 } 425 426 bool syncKey = true; 427 bool releaseCallback = false; 428 if (!surface) { 429 auto result = fProxy->fLazyInstantiateCallback(resourceProvider, fProxy->callbackDesc()); 430 surface = std::move(result.fSurface); 431 syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced; 432 releaseCallback = surface && result.fReleaseCallback; 433 } 434 if (!surface) { 435 fProxy->fDimensions.setEmpty(); 436 return false; 437 } 438 439 if (fProxy->isFullyLazy()) { 440 // This was a fully lazy proxy. We need to fill in the width & height. For partially 441 // lazy proxies we must preserve the original width & height since that indicates 442 // the content area. 443 fProxy->fDimensions = surface->dimensions(); 444 } 445 446 SkASSERT(fProxy->width() <= surface->width()); 447 SkASSERT(fProxy->height() <= surface->height()); 448 449 if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) { 450 texProxy->setTargetKeySync(syncKey); 451 if (syncKey) { 452 const GrUniqueKey& key = texProxy->getUniqueKey(); 453 if (key.isValid()) { 454 if (!surface->asTexture()->getUniqueKey().isValid()) { 455 // If 'surface' is newly created, attach the unique key 456 resourceProvider->assignUniqueKeyToResource(key, surface.get()); 457 } else { 458 // otherwise we had better have reattached to a cached version 459 SkASSERT(surface->asTexture()->getUniqueKey() == key); 460 } 461 } else { 462 SkASSERT(!surface->getUniqueKey().isValid()); 463 } 464 } 465 } 466 467 this->assign(std::move(surface)); 468 if (releaseCallback) { 469 fProxy->fLazyInstantiateCallback = nullptr; 470 } 471 472 return true; 473} 474 475#ifdef SK_DEBUG 476void GrSurfaceProxy::validateSurface(const GrSurface* surface) { 477 SkASSERTF(surface->backendFormat() == fFormat, "%s != %s", 478 surface->backendFormat().toStr().c_str(), fFormat.toStr().c_str()); 479 480 this->onValidateSurface(surface); 481} 482#endif 483