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
is_valid_lazy(const SkISize & dimensions,SkBackingFit fit)29 static 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
is_valid_non_lazy(SkISize dimensions)38 static bool is_valid_non_lazy(SkISize dimensions) {
39 return dimensions.fWidth > 0 && dimensions.fHeight > 0;
40 }
41 #endif
42
43 // Deferred version
GrSurfaceProxy(const GrBackendFormat & format,SkISize dimensions,SkBackingFit fit,SkBudgeted budgeted,GrProtected isProtected,GrInternalSurfaceFlags surfaceFlags,UseAllocator useAllocator)44 GrSurfaceProxy::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
GrSurfaceProxy(LazyInstantiateCallback && callback,const GrBackendFormat & format,SkISize dimensions,SkBackingFit fit,SkBudgeted budgeted,GrProtected isProtected,GrInternalSurfaceFlags surfaceFlags,UseAllocator useAllocator)63 GrSurfaceProxy::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
GrSurfaceProxy(sk_sp<GrSurface> surface,SkBackingFit fit,UseAllocator useAllocator)85 GrSurfaceProxy::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
~GrSurfaceProxy()102 GrSurfaceProxy::~GrSurfaceProxy() {
103 }
104
createSurfaceImpl(GrResourceProvider * resourceProvider,int sampleCnt,GrRenderable renderable,GrMipmapped mipMapped) const105 sk_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
canSkipResourceAllocator() const141 bool 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
assign(sk_sp<GrSurface> surface)156 void 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
instantiateImpl(GrResourceProvider * resourceProvider,int sampleCnt,GrRenderable renderable,GrMipmapped mipMapped,const GrUniqueKey * uniqueKey)178 bool 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
deinstantiate()206 void GrSurfaceProxy::deinstantiate() {
207 SkASSERT(this->isInstantiated());
208 fTarget = nullptr;
209 }
210
computeScratchKey(const GrCaps & caps,GrScratchKey * key) const211 void 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
backingStoreDimensions() const230 SkISize 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
isFunctionallyExact() const242 bool GrSurfaceProxy::isFunctionallyExact() const {
243 SkASSERT(!this->isFullyLazy());
244 return fFit == SkBackingFit::kExact ||
245 fDimensions == GrResourceProvider::MakeApprox(fDimensions);
246 }
247
isFormatCompressed(const GrCaps * caps) const248 bool GrSurfaceProxy::isFormatCompressed(const GrCaps* caps) const {
249 return caps->isFormatCompressed(this->backendFormat());
250 }
251
252 #ifdef SK_DEBUG
validate(GrContext_Base * context) const253 void GrSurfaceProxy::validate(GrContext_Base* context) const {
254 if (fTarget) {
255 SkASSERT(fTarget->getContext()->priv().matches(context));
256 }
257 }
258 #endif
259
Copy(GrRecordingContext * rContext,sk_sp<GrSurfaceProxy> src,GrSurfaceOrigin origin,GrMipmapped mipMapped,SkIRect srcRect,SkBackingFit fit,SkBudgeted budgeted,RectsMustMatch rectsMustMatch,sk_sp<GrRenderTask> * outTask)260 sk_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
Copy(GrRecordingContext * context,sk_sp<GrSurfaceProxy> src,GrSurfaceOrigin origin,GrMipmapped mipMapped,SkBackingFit fit,SkBudgeted budgeted,sk_sp<GrRenderTask> * outTask)334 sk_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
testingOnly_getBackingRefCnt() const355 int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
356 if (fTarget) {
357 return fTarget->testingOnly_getRefCnt();
358 }
359
360 return -1; // no backing GrSurface
361 }
362
testingOnly_getFlags() const363 GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
364 return fSurfaceFlags;
365 }
366
dump() const367 SkString 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
exactify(bool allocatedCaseOnly)379 void 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
doLazyInstantiation(GrResourceProvider * resourceProvider)417 bool 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
validateSurface(const GrSurface * surface)476 void 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