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