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