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 return surface;
136 }
137
canSkipResourceAllocator() const138 bool GrSurfaceProxy::canSkipResourceAllocator() const {
139 if (fUseAllocator == UseAllocator::kNo) {
140 // Usually an atlas or onFlush proxy
141 return true;
142 }
143
144 auto peek = this->peekSurface();
145 if (!peek) {
146 return false;
147 }
148 // If this resource is already allocated and not recyclable then the resource allocator does
149 // not need to do anything with it.
150 return !peek->resourcePriv().getScratchKey().isValid();
151 }
152
assign(sk_sp<GrSurface> surface)153 void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
154 SkASSERT(!fTarget && surface);
155
156 SkDEBUGCODE(this->validateSurface(surface.get());)
157
158 fTarget = std::move(surface);
159
160 #ifdef SK_DEBUG
161 if (this->asRenderTargetProxy()) {
162 SkASSERT(fTarget->asRenderTarget());
163 }
164
165 // In order to give DDL users some flexibility in the destination of there DDLs,
166 // a DDL's target proxy can be more conservative (and thus require less memory)
167 // than the actual GrSurface used to fulfill it.
168 if (!this->isDDLTarget() && kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
169 // TODO(11373): Can this check be exact?
170 SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
171 }
172 #endif
173 }
174
instantiateImpl(GrResourceProvider * resourceProvider,int sampleCnt,GrRenderable renderable,GrMipmapped mipMapped,const GrUniqueKey * uniqueKey)175 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
176 GrRenderable renderable, GrMipmapped mipMapped,
177 const GrUniqueKey* uniqueKey) {
178 SkASSERT(!this->isLazy());
179 if (fTarget) {
180 if (uniqueKey && uniqueKey->isValid()) {
181 SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
182 }
183 return true;
184 }
185
186 sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, renderable,
187 mipMapped);
188 if (!surface) {
189 return false;
190 }
191
192 // If there was an invalidation message pending for this key, we might have just processed it,
193 // causing the key (stored on this proxy) to become invalid.
194 if (uniqueKey && uniqueKey->isValid()) {
195 resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
196 }
197
198 this->assign(std::move(surface));
199
200 return true;
201 }
202
deinstantiate()203 void GrSurfaceProxy::deinstantiate() {
204 SkASSERT(this->isInstantiated());
205 fTarget = nullptr;
206 }
207
computeScratchKey(const GrCaps & caps,GrScratchKey * key) const208 void GrSurfaceProxy::computeScratchKey(const GrCaps& caps, GrScratchKey* key) const {
209 SkASSERT(!this->isFullyLazy());
210 GrRenderable renderable = GrRenderable::kNo;
211 int sampleCount = 1;
212 if (const auto* rtp = this->asRenderTargetProxy()) {
213 renderable = GrRenderable::kYes;
214 sampleCount = rtp->numSamples();
215 }
216
217 const GrTextureProxy* tp = this->asTextureProxy();
218 GrMipmapped mipMapped = GrMipmapped::kNo;
219 if (tp) {
220 mipMapped = tp->mipmapped();
221 }
222
223 GrTexture::ComputeScratchKey(caps, this->backendFormat(), this->backingStoreDimensions(),
224 renderable, sampleCount, mipMapped, fIsProtected, key);
225 }
226
backingStoreDimensions() const227 SkISize GrSurfaceProxy::backingStoreDimensions() const {
228 SkASSERT(!this->isFullyLazy());
229 if (fTarget) {
230 return fTarget->dimensions();
231 }
232
233 if (SkBackingFit::kExact == fFit) {
234 return fDimensions;
235 }
236 return GrResourceProvider::MakeApprox(fDimensions);
237 }
238
isFunctionallyExact() const239 bool GrSurfaceProxy::isFunctionallyExact() const {
240 SkASSERT(!this->isFullyLazy());
241 return fFit == SkBackingFit::kExact ||
242 fDimensions == GrResourceProvider::MakeApprox(fDimensions);
243 }
244
isFormatCompressed(const GrCaps * caps) const245 bool GrSurfaceProxy::isFormatCompressed(const GrCaps* caps) const {
246 return caps->isFormatCompressed(this->backendFormat());
247 }
248
249 #ifdef SK_DEBUG
validate(GrContext_Base * context) const250 void GrSurfaceProxy::validate(GrContext_Base* context) const {
251 if (fTarget) {
252 SkASSERT(fTarget->getContext()->priv().matches(context));
253 }
254 }
255 #endif
256
Copy(GrRecordingContext * rContext,sk_sp<GrSurfaceProxy> src,GrSurfaceOrigin origin,GrMipmapped mipMapped,SkIRect srcRect,SkBackingFit fit,SkBudgeted budgeted,RectsMustMatch rectsMustMatch,sk_sp<GrRenderTask> * outTask)257 sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* rContext,
258 sk_sp<GrSurfaceProxy> src,
259 GrSurfaceOrigin origin,
260 GrMipmapped mipMapped,
261 SkIRect srcRect,
262 SkBackingFit fit,
263 SkBudgeted budgeted,
264 RectsMustMatch rectsMustMatch,
265 sk_sp<GrRenderTask>* outTask) {
266 SkASSERT(!src->isFullyLazy());
267 int width;
268 int height;
269
270 SkIPoint dstPoint;
271 if (rectsMustMatch == RectsMustMatch::kYes) {
272 width = src->width();
273 height = src->height();
274 dstPoint = {srcRect.fLeft, srcRect.fTop};
275 } else {
276 width = srcRect.width();
277 height = srcRect.height();
278 dstPoint = {0, 0};
279 }
280
281 if (!srcRect.intersect(SkIRect::MakeSize(src->dimensions()))) {
282 return {};
283 }
284 auto format = src->backendFormat().makeTexture2D();
285 SkASSERT(format.isValid());
286
287 if (src->backendFormat().textureType() != GrTextureType::kExternal) {
288 GrImageInfo info(GrColorType::kUnknown, kUnknown_SkAlphaType, nullptr, {width, height});
289 auto dstContext = rContext->priv().makeSC(info,
290 format,
291 fit,
292 origin,
293 GrRenderable::kNo,
294 1,
295 mipMapped,
296 src->isProtected(),
297 budgeted);
298 sk_sp<GrRenderTask> copyTask;
299 if (dstContext && (copyTask = dstContext->copy(src, srcRect, dstPoint))) {
300 if (outTask) {
301 *outTask = std::move(copyTask);
302 }
303 return dstContext->asSurfaceProxyRef();
304 }
305 }
306 if (src->asTextureProxy()) {
307 auto dstContext = rContext->priv().makeSFC(kUnknown_SkAlphaType,
308 nullptr,
309 {width, height},
310 fit,
311 format,
312 1,
313 mipMapped,
314 src->isProtected(),
315 GrSwizzle::RGBA(),
316 GrSwizzle::RGBA(),
317 origin,
318 budgeted);
319 GrSurfaceProxyView view(std::move(src), origin, GrSwizzle::RGBA());
320 if (dstContext && dstContext->blitTexture(std::move(view), srcRect, dstPoint)) {
321 if (outTask) {
322 *outTask = dstContext->refRenderTask();
323 }
324 return dstContext->asSurfaceProxyRef();
325 }
326 }
327 // Can't use backend copies or draws.
328 return nullptr;
329 }
330
Copy(GrRecordingContext * context,sk_sp<GrSurfaceProxy> src,GrSurfaceOrigin origin,GrMipmapped mipMapped,SkBackingFit fit,SkBudgeted budgeted,sk_sp<GrRenderTask> * outTask)331 sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
332 sk_sp<GrSurfaceProxy> src,
333 GrSurfaceOrigin origin,
334 GrMipmapped mipMapped,
335 SkBackingFit fit,
336 SkBudgeted budgeted,
337 sk_sp<GrRenderTask>* outTask) {
338 SkASSERT(!src->isFullyLazy());
339 auto rect = SkIRect::MakeSize(src->dimensions());
340 return Copy(context,
341 std::move(src),
342 origin,
343 mipMapped,
344 rect,
345 fit,
346 budgeted,
347 RectsMustMatch::kNo,
348 outTask);
349 }
350
351 #if GR_TEST_UTILS
testingOnly_getBackingRefCnt() const352 int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
353 if (fTarget) {
354 return fTarget->testingOnly_getRefCnt();
355 }
356
357 return -1; // no backing GrSurface
358 }
359
testingOnly_getFlags() const360 GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
361 return fSurfaceFlags;
362 }
363
dump() const364 SkString GrSurfaceProxy::dump() const {
365 SkString tmp;
366
367 tmp.appendf("proxyID: %d - surfaceID: %d",
368 this->uniqueID().asUInt(),
369 this->peekSurface() ? this->peekSurface()->uniqueID().asUInt()
370 : -1);
371 return tmp;
372 }
373
374 #endif
375
exactify(bool allocatedCaseOnly)376 void GrSurfaceProxyPriv::exactify(bool allocatedCaseOnly) {
377 SkASSERT(!fProxy->isFullyLazy());
378 if (this->isExact()) {
379 return;
380 }
381
382 SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
383
384 if (fProxy->fTarget) {
385 // The kApprox but already instantiated case. Setting the proxy's width & height to
386 // the instantiated width & height could have side-effects going forward, since we're
387 // obliterating the area of interest information. This call (exactify) only used
388 // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
389 // used for additional draws.
390 fProxy->fDimensions = fProxy->fTarget->dimensions();
391 return;
392 }
393
394 #ifndef SK_CRIPPLE_TEXTURE_REUSE
395 // In the post-implicit-allocation world we can't convert this proxy to be exact fit
396 // at this point. With explicit allocation switching this to exact will result in a
397 // different allocation at flush time. With implicit allocation, allocation would occur
398 // at draw time (rather than flush time) so this pathway was encountered less often (if
399 // at all).
400 if (allocatedCaseOnly) {
401 return;
402 }
403 #endif
404
405 // The kApprox uninstantiated case. Making this proxy be exact should be okay.
406 // It could mess things up if prior decisions were based on the approximate size.
407 fProxy->fFit = SkBackingFit::kExact;
408 // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
409 // already been computed we want to leave it alone so that amount will be removed when
410 // the special image goes away. If it hasn't been computed yet it might as well compute the
411 // exact amount.
412 }
413
doLazyInstantiation(GrResourceProvider * resourceProvider)414 bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
415 SkASSERT(fProxy->isLazy());
416
417 sk_sp<GrSurface> surface;
418 if (const auto& uniqueKey = fProxy->getUniqueKey(); uniqueKey.isValid()) {
419 // First try to reattach to a cached version if the proxy is uniquely keyed
420 surface = resourceProvider->findByUniqueKey<GrSurface>(uniqueKey);
421 }
422
423 bool syncKey = true;
424 bool releaseCallback = false;
425 if (!surface) {
426 auto result = fProxy->fLazyInstantiateCallback(resourceProvider, fProxy->callbackDesc());
427 surface = std::move(result.fSurface);
428 syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
429 releaseCallback = surface && result.fReleaseCallback;
430 }
431 if (!surface) {
432 fProxy->fDimensions.setEmpty();
433 return false;
434 }
435
436 if (fProxy->isFullyLazy()) {
437 // This was a fully lazy proxy. We need to fill in the width & height. For partially
438 // lazy proxies we must preserve the original width & height since that indicates
439 // the content area.
440 fProxy->fDimensions = surface->dimensions();
441 }
442
443 SkASSERT(fProxy->width() <= surface->width());
444 SkASSERT(fProxy->height() <= surface->height());
445
446 if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
447 texProxy->setTargetKeySync(syncKey);
448 if (syncKey) {
449 const GrUniqueKey& key = texProxy->getUniqueKey();
450 if (key.isValid()) {
451 if (!surface->asTexture()->getUniqueKey().isValid()) {
452 // If 'surface' is newly created, attach the unique key
453 resourceProvider->assignUniqueKeyToResource(key, surface.get());
454 } else {
455 // otherwise we had better have reattached to a cached version
456 SkASSERT(surface->asTexture()->getUniqueKey() == key);
457 }
458 } else {
459 SkASSERT(!surface->getUniqueKey().isValid());
460 }
461 }
462 }
463
464 this->assign(std::move(surface));
465 if (releaseCallback) {
466 fProxy->fLazyInstantiateCallback = nullptr;
467 }
468
469 return true;
470 }
471
472 #ifdef SK_DEBUG
validateSurface(const GrSurface * surface)473 void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
474 SkASSERTF(surface->backendFormat() == fFormat, "%s != %s",
475 surface->backendFormat().toStr().c_str(), fFormat.toStr().c_str());
476
477 this->onValidateSurface(surface);
478 }
479 #endif
480