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 "GrSurfaceProxy.h"
9 #include "GrSurfaceProxyPriv.h"
10
11 #include "GrCaps.h"
12 #include "GrContext.h"
13 #include "GrContextPriv.h"
14 #include "GrGpuResourcePriv.h"
15 #include "GrOpList.h"
16 #include "GrProxyProvider.h"
17 #include "GrSurfaceContext.h"
18 #include "GrSurfacePriv.h"
19 #include "GrTexturePriv.h"
20 #include "GrTextureRenderTargetProxy.h"
21
22 #include "SkMathPriv.h"
23 #include "SkMipMap.h"
24
25 #ifdef SK_DEBUG
26 #include "GrRenderTarget.h"
27 #include "GrRenderTargetPriv.h"
28
is_valid_fully_lazy(const GrSurfaceDesc & desc,SkBackingFit fit)29 static bool is_valid_fully_lazy(const GrSurfaceDesc& desc, SkBackingFit fit) {
30 return desc.fWidth <= 0 &&
31 desc.fHeight <= 0 &&
32 desc.fConfig != kUnknown_GrPixelConfig &&
33 desc.fSampleCnt == 1 &&
34 SkBackingFit::kApprox == fit;
35 }
36
is_valid_partially_lazy(const GrSurfaceDesc & desc)37 static bool is_valid_partially_lazy(const GrSurfaceDesc& desc) {
38 return ((desc.fWidth > 0 && desc.fHeight > 0) ||
39 (desc.fWidth <= 0 && desc.fHeight <= 0)) &&
40 desc.fConfig != kUnknown_GrPixelConfig;
41 }
42
is_valid_non_lazy(const GrSurfaceDesc & desc)43 static bool is_valid_non_lazy(const GrSurfaceDesc& desc) {
44 return desc.fWidth > 0 &&
45 desc.fHeight > 0 &&
46 desc.fConfig != kUnknown_GrPixelConfig;
47 }
48 #endif
49
50 // Lazy-callback version
GrSurfaceProxy(LazyInstantiateCallback && callback,LazyInstantiationType lazyType,const GrBackendFormat & format,const GrSurfaceDesc & desc,GrSurfaceOrigin origin,SkBackingFit fit,SkBudgeted budgeted,GrInternalSurfaceFlags surfaceFlags)51 GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback, LazyInstantiationType lazyType,
52 const GrBackendFormat& format, const GrSurfaceDesc& desc,
53 GrSurfaceOrigin origin, SkBackingFit fit,
54 SkBudgeted budgeted, GrInternalSurfaceFlags surfaceFlags)
55 : fSurfaceFlags(surfaceFlags)
56 , fFormat(format)
57 , fConfig(desc.fConfig)
58 , fWidth(desc.fWidth)
59 , fHeight(desc.fHeight)
60 , fOrigin(origin)
61 , fFit(fit)
62 , fBudgeted(budgeted)
63 , fLazyInstantiateCallback(std::move(callback))
64 , fLazyInstantiationType(lazyType)
65 , fNeedsClear(SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag))
66 , fGpuMemorySize(kInvalidGpuMemorySize)
67 , fLastOpList(nullptr) {
68 SkASSERT(fFormat.isValid());
69 // NOTE: the default fUniqueID ctor pulls a value from the same pool as the GrGpuResources.
70 if (fLazyInstantiateCallback) {
71 SkASSERT(is_valid_fully_lazy(desc, fit) || is_valid_partially_lazy(desc));
72 } else {
73 SkASSERT(is_valid_non_lazy(desc));
74 }
75
76 if (GrPixelConfigIsCompressed(desc.fConfig)) {
77 SkASSERT(!SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag));
78 fSurfaceFlags |= GrInternalSurfaceFlags::kReadOnly;
79 }
80 }
81
82 // Wrapped version
GrSurfaceProxy(sk_sp<GrSurface> surface,GrSurfaceOrigin origin,SkBackingFit fit)83 GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, GrSurfaceOrigin origin, SkBackingFit fit)
84 : INHERITED(std::move(surface))
85 , fSurfaceFlags(fTarget->surfacePriv().flags())
86 , fFormat(fTarget->backendFormat())
87 , fConfig(fTarget->config())
88 , fWidth(fTarget->width())
89 , fHeight(fTarget->height())
90 , fOrigin(origin)
91 , fFit(fit)
92 , fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted
93 ? SkBudgeted::kYes
94 : SkBudgeted::kNo)
95 , fUniqueID(fTarget->uniqueID()) // Note: converting from unique resource ID to a proxy ID!
96 , fNeedsClear(false)
97 , fGpuMemorySize(kInvalidGpuMemorySize)
98 , fLastOpList(nullptr) {
99 SkASSERT(fFormat.isValid());
100 }
101
~GrSurfaceProxy()102 GrSurfaceProxy::~GrSurfaceProxy() {
103 if (fLazyInstantiateCallback) {
104 // We call the callback with a null GrResourceProvider to signal that the lambda should
105 // clean itself up if it is holding onto any captured objects.
106 this->fLazyInstantiateCallback(nullptr);
107 }
108 // For this to be deleted the opList that held a ref on it (if there was one) must have been
109 // deleted. Which would have cleared out this back pointer.
110 SkASSERT(!fLastOpList);
111 }
112
AttachStencilIfNeeded(GrResourceProvider * resourceProvider,GrSurface * surface,bool needsStencil)113 bool GrSurfaceProxyPriv::AttachStencilIfNeeded(GrResourceProvider* resourceProvider,
114 GrSurface* surface, bool needsStencil) {
115 if (needsStencil) {
116 GrRenderTarget* rt = surface->asRenderTarget();
117 if (!rt) {
118 SkASSERT(0);
119 return false;
120 }
121
122 if (!resourceProvider->attachStencilAttachment(rt)) {
123 return false;
124 }
125 }
126
127 return true;
128 }
129
createSurfaceImpl(GrResourceProvider * resourceProvider,int sampleCnt,bool needsStencil,GrSurfaceDescFlags descFlags,GrMipMapped mipMapped) const130 sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
131 int sampleCnt, bool needsStencil,
132 GrSurfaceDescFlags descFlags,
133 GrMipMapped mipMapped) const {
134 SkASSERT(GrSurfaceProxy::LazyState::kNot == this->lazyInstantiationState());
135 SkASSERT(!fTarget);
136 GrSurfaceDesc desc;
137 desc.fFlags = descFlags;
138 if (fNeedsClear) {
139 desc.fFlags |= kPerformInitialClear_GrSurfaceFlag;
140 }
141 desc.fWidth = fWidth;
142 desc.fHeight = fHeight;
143 desc.fConfig = fConfig;
144 desc.fSampleCnt = sampleCnt;
145
146 GrResourceProvider::Flags resourceProviderFlags = GrResourceProvider::Flags::kNone;
147 if ((fSurfaceFlags & GrInternalSurfaceFlags::kNoPendingIO) ||
148 resourceProvider->explicitlyAllocateGPUResources()) {
149 // The explicit resource allocator requires that any resources it pulls out of the
150 // cache have no pending IO.
151 resourceProviderFlags = GrResourceProvider::Flags::kNoPendingIO;
152 }
153
154 sk_sp<GrSurface> surface;
155 if (GrMipMapped::kYes == mipMapped) {
156 SkASSERT(SkBackingFit::kExact == fFit);
157
158 // SkMipMap doesn't include the base level in the level count so we have to add 1
159 int mipCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1;
160 // We should have caught the case where mipCount == 1 when making the proxy and instead
161 // created a non-mipmapped proxy.
162 SkASSERT(mipCount > 1);
163 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipCount]);
164
165 // We don't want to upload any texel data
166 for (int i = 0; i < mipCount; i++) {
167 texels[i].fPixels = nullptr;
168 texels[i].fRowBytes = 0;
169 }
170
171 surface = resourceProvider->createTexture(desc, fBudgeted, texels.get(), mipCount);
172 if (surface) {
173 SkASSERT(surface->asTexture());
174 SkASSERT(GrMipMapped::kYes == surface->asTexture()->texturePriv().mipMapped());
175 }
176 } else {
177 if (SkBackingFit::kApprox == fFit) {
178 surface = resourceProvider->createApproxTexture(desc, resourceProviderFlags);
179 } else {
180 surface = resourceProvider->createTexture(desc, fBudgeted, resourceProviderFlags);
181 }
182 }
183 if (!surface) {
184 return nullptr;
185 }
186
187 if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(), needsStencil)) {
188 return nullptr;
189 }
190
191 return surface;
192 }
193
canSkipResourceAllocator() const194 bool GrSurfaceProxy::canSkipResourceAllocator() const {
195 auto peek = this->peekSurface();
196 if (!peek) {
197 return false;
198 }
199 // If this resource is already allocated and not recyclable then the resource allocator does
200 // not need to do anything with it.
201 return !peek->resourcePriv().getScratchKey().isValid();
202 }
203
assign(sk_sp<GrSurface> surface)204 void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
205 SkASSERT(!fTarget && surface);
206
207 SkDEBUGCODE(this->validateSurface(surface.get());)
208
209 fTarget = surface.release();
210
211 this->INHERITED::transferRefs();
212
213 #ifdef SK_DEBUG
214 if (this->asRenderTargetProxy()) {
215 SkASSERT(fTarget->asRenderTarget());
216 if (this->asRenderTargetProxy()->needsStencil()) {
217 SkASSERT(fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment());
218 }
219 }
220
221 if (kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
222 SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
223 }
224 #endif
225 }
226
instantiateImpl(GrResourceProvider * resourceProvider,int sampleCnt,bool needsStencil,GrSurfaceDescFlags descFlags,GrMipMapped mipMapped,const GrUniqueKey * uniqueKey)227 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
228 bool needsStencil, GrSurfaceDescFlags descFlags,
229 GrMipMapped mipMapped, const GrUniqueKey* uniqueKey) {
230 SkASSERT(LazyState::kNot == this->lazyInstantiationState());
231 if (fTarget) {
232 if (uniqueKey && uniqueKey->isValid()) {
233 SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
234 }
235 return GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, fTarget, needsStencil);
236 }
237
238 sk_sp<GrSurface> surface = this->createSurfaceImpl(resourceProvider, sampleCnt, needsStencil,
239 descFlags, mipMapped);
240 if (!surface) {
241 return false;
242 }
243
244 // If there was an invalidation message pending for this key, we might have just processed it,
245 // causing the key (stored on this proxy) to become invalid.
246 if (uniqueKey && uniqueKey->isValid()) {
247 resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
248 }
249
250 this->assign(std::move(surface));
251
252 return true;
253 }
254
deinstantiate()255 void GrSurfaceProxy::deinstantiate() {
256 SkASSERT(this->isInstantiated());
257
258 this->release();
259 }
260
computeScratchKey(GrScratchKey * key) const261 void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const {
262 SkASSERT(LazyState::kFully != this->lazyInstantiationState());
263 const GrRenderTargetProxy* rtp = this->asRenderTargetProxy();
264 int sampleCount = 1;
265 if (rtp) {
266 sampleCount = rtp->numStencilSamples();
267 }
268
269 const GrTextureProxy* tp = this->asTextureProxy();
270 GrMipMapped mipMapped = GrMipMapped::kNo;
271 if (tp) {
272 mipMapped = tp->mipMapped();
273 }
274
275 int width = this->worstCaseWidth();
276 int height = this->worstCaseHeight();
277
278 GrTexturePriv::ComputeScratchKey(this->config(), width, height, SkToBool(rtp), sampleCount,
279 mipMapped, key);
280 }
281
setLastOpList(GrOpList * opList)282 void GrSurfaceProxy::setLastOpList(GrOpList* opList) {
283 #ifdef SK_DEBUG
284 if (fLastOpList) {
285 SkASSERT(fLastOpList->isClosed());
286 }
287 #endif
288
289 // Un-reffed
290 fLastOpList = opList;
291 }
292
getLastRenderTargetOpList()293 GrRenderTargetOpList* GrSurfaceProxy::getLastRenderTargetOpList() {
294 return fLastOpList ? fLastOpList->asRenderTargetOpList() : nullptr;
295 }
296
getLastTextureOpList()297 GrTextureOpList* GrSurfaceProxy::getLastTextureOpList() {
298 return fLastOpList ? fLastOpList->asTextureOpList() : nullptr;
299 }
300
worstCaseWidth() const301 int GrSurfaceProxy::worstCaseWidth() const {
302 SkASSERT(LazyState::kFully != this->lazyInstantiationState());
303 if (fTarget) {
304 return fTarget->width();
305 }
306
307 if (SkBackingFit::kExact == fFit) {
308 return fWidth;
309 }
310 return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fWidth));
311 }
312
worstCaseHeight() const313 int GrSurfaceProxy::worstCaseHeight() const {
314 SkASSERT(LazyState::kFully != this->lazyInstantiationState());
315 if (fTarget) {
316 return fTarget->height();
317 }
318
319 if (SkBackingFit::kExact == fFit) {
320 return fHeight;
321 }
322 return SkTMax(GrResourceProvider::kMinScratchTextureSize, GrNextPow2(fHeight));
323 }
324
325 #ifdef SK_DEBUG
validate(GrContext * context) const326 void GrSurfaceProxy::validate(GrContext* context) const {
327 if (fTarget) {
328 SkASSERT(fTarget->getContext() == context);
329 }
330
331 INHERITED::validate();
332 }
333 #endif
334
Copy(GrContext * context,GrSurfaceProxy * src,GrMipMapped mipMapped,SkIRect srcRect,SkBackingFit fit,SkBudgeted budgeted)335 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrContext* context,
336 GrSurfaceProxy* src,
337 GrMipMapped mipMapped,
338 SkIRect srcRect,
339 SkBackingFit fit,
340 SkBudgeted budgeted) {
341 SkASSERT(LazyState::kFully != src->lazyInstantiationState());
342 if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) {
343 return nullptr;
344 }
345
346 GrSurfaceDesc dstDesc;
347 dstDesc.fWidth = srcRect.width();
348 dstDesc.fHeight = srcRect.height();
349 dstDesc.fConfig = src->config();
350
351 GrBackendFormat format = src->backendFormat().makeTexture2D();
352 if (!format.isValid()) {
353 return nullptr;
354 }
355
356 sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(
357 format, dstDesc, src->origin(), mipMapped, fit, budgeted));
358 if (!dstContext) {
359 return nullptr;
360 }
361
362 if (!dstContext->copy(src, srcRect, SkIPoint::Make(0, 0))) {
363 return nullptr;
364 }
365
366 return dstContext->asTextureProxyRef();
367 }
368
Copy(GrContext * context,GrSurfaceProxy * src,GrMipMapped mipMapped,SkBackingFit fit,SkBudgeted budgeted)369 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrContext* context, GrSurfaceProxy* src,
370 GrMipMapped mipMapped, SkBackingFit fit,
371 SkBudgeted budgeted) {
372 SkASSERT(LazyState::kFully != src->lazyInstantiationState());
373 return Copy(context, src, mipMapped, SkIRect::MakeWH(src->width(), src->height()), fit,
374 budgeted);
375 }
376
TestCopy(GrContext * context,const GrSurfaceDesc & dstDesc,GrSurfaceOrigin origin,GrSurfaceProxy * srcProxy)377 sk_sp<GrSurfaceContext> GrSurfaceProxy::TestCopy(GrContext* context, const GrSurfaceDesc& dstDesc,
378 GrSurfaceOrigin origin, GrSurfaceProxy* srcProxy) {
379 SkASSERT(LazyState::kFully != srcProxy->lazyInstantiationState());
380
381 GrBackendFormat format = srcProxy->backendFormat().makeTexture2D();
382 if (!format.isValid()) {
383 return nullptr;
384 }
385
386 sk_sp<GrSurfaceContext> dstContext(context->contextPriv().makeDeferredSurfaceContext(
387 format, dstDesc, origin, GrMipMapped::kNo, SkBackingFit::kExact, SkBudgeted::kYes));
388 if (!dstContext) {
389 return nullptr;
390 }
391
392 if (!dstContext->copy(srcProxy)) {
393 return nullptr;
394 }
395
396 return dstContext;
397 }
398
exactify()399 void GrSurfaceProxyPriv::exactify() {
400 SkASSERT(GrSurfaceProxy::LazyState::kFully != fProxy->lazyInstantiationState());
401 if (this->isExact()) {
402 return;
403 }
404
405 SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
406
407 if (fProxy->fTarget) {
408 // The kApprox but already instantiated case. Setting the proxy's width & height to
409 // the instantiated width & height could have side-effects going forward, since we're
410 // obliterating the area of interest information. This call (exactify) only used
411 // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
412 // used for additional draws.
413 fProxy->fWidth = fProxy->fTarget->width();
414 fProxy->fHeight = fProxy->fTarget->height();
415 return;
416 }
417
418 // The kApprox uninstantiated case. Making this proxy be exact should be okay.
419 // It could mess things up if prior decisions were based on the approximate size.
420 fProxy->fFit = SkBackingFit::kExact;
421 // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
422 // already been computed we want to leave it alone so that amount will be removed when
423 // the special image goes away. If it hasn't been computed yet it might as well compute the
424 // exact amount.
425 }
426
doLazyInstantiation(GrResourceProvider * resourceProvider)427 bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
428 SkASSERT(GrSurfaceProxy::LazyState::kNot != fProxy->lazyInstantiationState());
429
430 sk_sp<GrSurface> surface;
431 if (fProxy->asTextureProxy() && fProxy->asTextureProxy()->getUniqueKey().isValid()) {
432 // First try to reattach to a cached version if the proxy is uniquely keyed
433 surface = resourceProvider->findByUniqueKey<GrSurface>(
434 fProxy->asTextureProxy()->getUniqueKey());
435 }
436
437 if (!surface) {
438 surface = fProxy->fLazyInstantiateCallback(resourceProvider);
439 }
440 if (GrSurfaceProxy::LazyInstantiationType::kSingleUse == fProxy->fLazyInstantiationType) {
441 fProxy->fLazyInstantiateCallback(nullptr);
442 fProxy->fLazyInstantiateCallback = nullptr;
443 }
444 if (!surface) {
445 fProxy->fWidth = 0;
446 fProxy->fHeight = 0;
447 return false;
448 }
449
450 if (fProxy->fWidth <= 0 || fProxy->fHeight <= 0) {
451 // This was a fully lazy proxy. We need to fill in the width & height. For partially
452 // lazy proxies we must preserve the original width & height since that indicates
453 // the content area.
454 SkASSERT(fProxy->fWidth <= 0 && fProxy->fHeight <= 0);
455 fProxy->fWidth = surface->width();
456 fProxy->fHeight = surface->height();
457 }
458
459 bool needsStencil = fProxy->asRenderTargetProxy()
460 ? fProxy->asRenderTargetProxy()->needsStencil()
461 : false;
462
463 if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(), needsStencil)) {
464 return false;
465 }
466
467 if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
468 const GrUniqueKey& key = texProxy->getUniqueKey();
469 if (key.isValid()) {
470 if (!surface->asTexture()->getUniqueKey().isValid()) {
471 // If 'surface' is newly created, attach the unique key
472 resourceProvider->assignUniqueKeyToResource(key, surface.get());
473 } else {
474 // otherwise we had better have reattached to a cached version
475 SkASSERT(surface->asTexture()->getUniqueKey() == key);
476 }
477 }
478 }
479
480 this->assign(std::move(surface));
481 return true;
482 }
483
484 #ifdef SK_DEBUG
validateSurface(const GrSurface * surface)485 void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
486 SkASSERT(surface->config() == fConfig);
487
488 // Assert the flags are the same except for kNoPendingIO which is not passed onto the GrSurface.
489 GrInternalSurfaceFlags proxyFlags = fSurfaceFlags & ~GrInternalSurfaceFlags::kNoPendingIO;
490 GrInternalSurfaceFlags surfaceFlags = surface->surfacePriv().flags();
491 SkASSERT((proxyFlags & GrInternalSurfaceFlags::kSurfaceMask) ==
492 (surfaceFlags & GrInternalSurfaceFlags::kSurfaceMask));
493 this->onValidateSurface(surface);
494 }
495 #endif
496