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
144 // The explicit resource allocator requires that any resources it pulls out of the
145 // cache have no pending IO.
146 GrResourceProvider::Flags resourceProviderFlags = GrResourceProvider::Flags::kNoPendingIO;
147
148 sk_sp<GrSurface> surface;
149 if (GrMipMapped::kYes == mipMapped) {
150 SkASSERT(SkBackingFit::kExact == fFit);
151
152 // SkMipMap doesn't include the base level in the level count so we have to add 1
153 int mipCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1;
154 // We should have caught the case where mipCount == 1 when making the proxy and instead
155 // created a non-mipmapped proxy.
156 SkASSERT(mipCount > 1);
157 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipCount]);
158
159 // We don't want to upload any texel data
160 for (int i = 0; i < mipCount; i++) {
161 texels[i].fPixels = nullptr;
162 texels[i].fRowBytes = 0;
163 }
164 surface = resourceProvider->createTexture(desc, fFormat, renderable, sampleCnt, fBudgeted,
165 fIsProtected, texels.get(), mipCount);
166 #ifdef SK_DEBUG
167 if (surface) {
168 const GrTextureProxy* thisTexProxy = this->asTextureProxy();
169 SkASSERT(thisTexProxy);
170
171 GrTexture* texture = surface->asTexture();
172 SkASSERT(texture);
173
174 SkASSERT(GrMipMapped::kYes == texture->texturePriv().mipMapped());
175 SkASSERT(thisTexProxy->fInitialMipMapsStatus == texture->texturePriv().mipMapsStatus());
176 }
177 #endif
178 } else {
179 if (SkBackingFit::kApprox == fFit) {
180 surface = resourceProvider->createApproxTexture(desc, fFormat, renderable, sampleCnt,
181 fIsProtected, resourceProviderFlags);
182 } else {
183 surface =
184 resourceProvider->createTexture(desc, fFormat, renderable, sampleCnt, fBudgeted,
185 fIsProtected, resourceProviderFlags);
186 }
187 }
188 if (!surface) {
189 return nullptr;
190 }
191
192 if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(),
193 minStencilSampleCount)) {
194 return nullptr;
195 }
196
197 return surface;
198 }
199
canSkipResourceAllocator() const200 bool GrSurfaceProxy::canSkipResourceAllocator() const {
201 if (this->ignoredByResourceAllocator()) {
202 // Usually an atlas or onFlush proxy
203 return true;
204 }
205
206 auto peek = this->peekSurface();
207 if (!peek) {
208 return false;
209 }
210 // If this resource is already allocated and not recyclable then the resource allocator does
211 // not need to do anything with it.
212 return !peek->resourcePriv().getScratchKey().isValid();
213 }
214
assign(sk_sp<GrSurface> surface)215 void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
216 SkASSERT(!fTarget && surface);
217
218 SkDEBUGCODE(this->validateSurface(surface.get());)
219
220 fTarget = std::move(surface);
221
222 #ifdef SK_DEBUG
223 if (this->asRenderTargetProxy()) {
224 SkASSERT(fTarget->asRenderTarget());
225 if (int minStencilSampleCount = this->asRenderTargetProxy()->numStencilSamples()) {
226 auto* stencil = fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment();
227 SkASSERT(stencil);
228 SkASSERT(stencil->numSamples() >= minStencilSampleCount);
229 }
230 }
231
232 if (kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
233 SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
234 }
235 #endif
236 }
237
instantiateImpl(GrResourceProvider * resourceProvider,int sampleCnt,int minStencilSampleCount,GrRenderable renderable,GrMipMapped mipMapped,const GrUniqueKey * uniqueKey)238 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
239 int minStencilSampleCount, GrRenderable renderable,
240 GrMipMapped mipMapped, const GrUniqueKey* uniqueKey) {
241 SkASSERT(LazyState::kNot == this->lazyInstantiationState());
242 if (fTarget) {
243 if (uniqueKey && uniqueKey->isValid()) {
244 SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
245 }
246 return GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, fTarget.get(),
247 minStencilSampleCount);
248 }
249
250 sk_sp<GrSurface> surface = this->createSurfaceImpl(
251 resourceProvider, sampleCnt, minStencilSampleCount, renderable, mipMapped);
252 if (!surface) {
253 return false;
254 }
255
256 // If there was an invalidation message pending for this key, we might have just processed it,
257 // causing the key (stored on this proxy) to become invalid.
258 if (uniqueKey && uniqueKey->isValid()) {
259 resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
260 }
261
262 this->assign(std::move(surface));
263
264 return true;
265 }
266
deinstantiate()267 void GrSurfaceProxy::deinstantiate() {
268 SkASSERT(this->isInstantiated());
269 fTarget = nullptr;
270 }
271
computeScratchKey(GrScratchKey * key) const272 void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const {
273 SkASSERT(LazyState::kFully != this->lazyInstantiationState());
274 GrRenderable renderable = GrRenderable::kNo;
275 int sampleCount = 1;
276 if (const auto* rtp = this->asRenderTargetProxy()) {
277 renderable = GrRenderable::kYes;
278 sampleCount = rtp->numSamples();
279 }
280
281 const GrTextureProxy* tp = this->asTextureProxy();
282 GrMipMapped mipMapped = GrMipMapped::kNo;
283 if (tp) {
284 mipMapped = tp->mipMapped();
285 }
286
287 int width = this->worstCaseWidth();
288 int height = this->worstCaseHeight();
289
290 GrTexturePriv::ComputeScratchKey(this->config(), width, height, renderable, sampleCount,
291 mipMapped, key);
292 }
293
setLastRenderTask(GrRenderTask * renderTask)294 void GrSurfaceProxy::setLastRenderTask(GrRenderTask* renderTask) {
295 #ifdef SK_DEBUG
296 if (fLastRenderTask) {
297 SkASSERT(fLastRenderTask->isClosed());
298 }
299 #endif
300
301 // Un-reffed
302 fLastRenderTask = renderTask;
303 }
304
getLastRenderTargetOpList()305 GrRenderTargetOpList* GrSurfaceProxy::getLastRenderTargetOpList() {
306 return fLastRenderTask ? fLastRenderTask->asRenderTargetOpList() : nullptr;
307 }
308
getLastTextureOpList()309 GrTextureOpList* GrSurfaceProxy::getLastTextureOpList() {
310 return fLastRenderTask ? fLastRenderTask->asTextureOpList() : nullptr;
311 }
312
worstCaseWidth() const313 int GrSurfaceProxy::worstCaseWidth() const {
314 SkASSERT(LazyState::kFully != this->lazyInstantiationState());
315 if (fTarget) {
316 return fTarget->width();
317 }
318
319 if (SkBackingFit::kExact == fFit) {
320 return fWidth;
321 }
322 return GrResourceProvider::MakeApprox(fWidth);
323 }
324
worstCaseHeight() const325 int GrSurfaceProxy::worstCaseHeight() const {
326 SkASSERT(LazyState::kFully != this->lazyInstantiationState());
327 if (fTarget) {
328 return fTarget->height();
329 }
330
331 if (SkBackingFit::kExact == fFit) {
332 return fHeight;
333 }
334 return GrResourceProvider::MakeApprox(fHeight);
335 }
336
337 #ifdef SK_DEBUG
validate(GrContext_Base * context) const338 void GrSurfaceProxy::validate(GrContext_Base* context) const {
339 if (fTarget) {
340 SkASSERT(fTarget->getContext() == context);
341 }
342 }
343 #endif
344
Copy(GrRecordingContext * context,GrSurfaceProxy * src,GrMipMapped mipMapped,SkIRect srcRect,SkBackingFit fit,SkBudgeted budgeted,RectsMustMatch rectsMustMatch)345 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
346 GrSurfaceProxy* src,
347 GrMipMapped mipMapped,
348 SkIRect srcRect,
349 SkBackingFit fit,
350 SkBudgeted budgeted,
351 RectsMustMatch rectsMustMatch) {
352 SkASSERT(LazyState::kFully != src->lazyInstantiationState());
353 GrProtected isProtected = src->isProtected() ? GrProtected::kYes : GrProtected::kNo;
354 int width;
355 int height;
356
357 SkIPoint dstPoint;
358 if (rectsMustMatch == RectsMustMatch::kYes) {
359 width = src->width();
360 height = src->height();
361 dstPoint = {srcRect.fLeft, srcRect.fTop};
362 } else {
363 width = srcRect.width();
364 height = srcRect.height();
365 dstPoint = {0, 0};
366 }
367
368 if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) {
369 return nullptr;
370 }
371 auto colorType = GrPixelConfigToColorType(src->config());
372 if (src->backendFormat().textureType() != GrTextureType::kExternal) {
373 sk_sp<GrTextureContext> dstContext(context->priv().makeDeferredTextureContext(
374 fit, width, height, colorType, kUnknown_SkAlphaType, nullptr, mipMapped,
375 src->origin(), budgeted, isProtected));
376 if (!dstContext) {
377 return nullptr;
378 }
379 if (dstContext->copy(src, srcRect, dstPoint)) {
380 return dstContext->asTextureProxyRef();
381 }
382 }
383 if (src->asTextureProxy()) {
384 sk_sp<GrRenderTargetContext> dstContext = context->priv().makeDeferredRenderTargetContext(
385 fit, width, height, colorType, nullptr, 1, mipMapped, src->origin(), nullptr,
386 budgeted);
387
388 if (dstContext && dstContext->blitTexture(src->asTextureProxy(), srcRect, dstPoint)) {
389 return dstContext->asTextureProxyRef();
390 }
391 }
392 // Can't use backend copies or draws.
393 return nullptr;
394 }
395
Copy(GrRecordingContext * context,GrSurfaceProxy * src,GrMipMapped mipMapped,SkBackingFit fit,SkBudgeted budgeted)396 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context, GrSurfaceProxy* src,
397 GrMipMapped mipMapped, SkBackingFit fit,
398 SkBudgeted budgeted) {
399 SkASSERT(LazyState::kFully != src->lazyInstantiationState());
400 return Copy(context, src, mipMapped, SkIRect::MakeWH(src->width(), src->height()), fit,
401 budgeted);
402 }
403
404 #if GR_TEST_UTILS
testingOnly_getBackingRefCnt() const405 int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
406 if (fTarget) {
407 return fTarget->testingOnly_getRefCnt();
408 }
409
410 return -1; // no backing GrSurface
411 }
412
testingOnly_getFlags() const413 GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
414 return fSurfaceFlags;
415 }
416 #endif
417
exactify(bool allocatedCaseOnly)418 void GrSurfaceProxyPriv::exactify(bool allocatedCaseOnly) {
419 SkASSERT(GrSurfaceProxy::LazyState::kFully != fProxy->lazyInstantiationState());
420 if (this->isExact()) {
421 return;
422 }
423
424 SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
425
426 if (fProxy->fTarget) {
427 // The kApprox but already instantiated case. Setting the proxy's width & height to
428 // the instantiated width & height could have side-effects going forward, since we're
429 // obliterating the area of interest information. This call (exactify) only used
430 // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
431 // used for additional draws.
432 fProxy->fWidth = fProxy->fTarget->width();
433 fProxy->fHeight = fProxy->fTarget->height();
434 return;
435 }
436
437 #ifndef SK_CRIPPLE_TEXTURE_REUSE
438 // In the post-implicit-allocation world we can't convert this proxy to be exact fit
439 // at this point. With explicit allocation switching this to exact will result in a
440 // different allocation at flush time. With implicit allocation, allocation would occur
441 // at draw time (rather than flush time) so this pathway was encountered less often (if
442 // at all).
443 if (allocatedCaseOnly) {
444 return;
445 }
446 #endif
447
448 // The kApprox uninstantiated case. Making this proxy be exact should be okay.
449 // It could mess things up if prior decisions were based on the approximate size.
450 fProxy->fFit = SkBackingFit::kExact;
451 // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
452 // already been computed we want to leave it alone so that amount will be removed when
453 // the special image goes away. If it hasn't been computed yet it might as well compute the
454 // exact amount.
455 }
456
doLazyInstantiation(GrResourceProvider * resourceProvider)457 bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
458 SkASSERT(GrSurfaceProxy::LazyState::kNot != fProxy->lazyInstantiationState());
459
460 sk_sp<GrSurface> surface;
461 if (fProxy->asTextureProxy() && fProxy->asTextureProxy()->getUniqueKey().isValid()) {
462 // First try to reattach to a cached version if the proxy is uniquely keyed
463 surface = resourceProvider->findByUniqueKey<GrSurface>(
464 fProxy->asTextureProxy()->getUniqueKey());
465 }
466
467 bool syncKey = true;
468 if (!surface) {
469 auto result = fProxy->fLazyInstantiateCallback(resourceProvider);
470 surface = std::move(result.fSurface);
471 syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
472 }
473 if (GrSurfaceProxy::LazyInstantiationType::kSingleUse == fProxy->fLazyInstantiationType) {
474 fProxy->fLazyInstantiateCallback = nullptr;
475 }
476 if (!surface) {
477 fProxy->fWidth = 0;
478 fProxy->fHeight = 0;
479 return false;
480 }
481
482 if (fProxy->fWidth <= 0 || fProxy->fHeight <= 0) {
483 // This was a fully lazy proxy. We need to fill in the width & height. For partially
484 // lazy proxies we must preserve the original width & height since that indicates
485 // the content area.
486 SkASSERT(fProxy->fWidth <= 0 && fProxy->fHeight <= 0);
487 fProxy->fWidth = surface->width();
488 fProxy->fHeight = surface->height();
489 }
490
491 SkASSERT(fProxy->fWidth <= surface->width());
492 SkASSERT(fProxy->fHeight <= surface->height());
493
494 int minStencilSampleCount = (fProxy->asRenderTargetProxy())
495 ? fProxy->asRenderTargetProxy()->numSamples()
496 : 0;
497
498 if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
499 resourceProvider, surface.get(), minStencilSampleCount)) {
500 return false;
501 }
502
503 if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
504 texProxy->setTargetKeySync(syncKey);
505 if (syncKey) {
506 const GrUniqueKey& key = texProxy->getUniqueKey();
507 if (key.isValid()) {
508 if (!surface->asTexture()->getUniqueKey().isValid()) {
509 // If 'surface' is newly created, attach the unique key
510 resourceProvider->assignUniqueKeyToResource(key, surface.get());
511 } else {
512 // otherwise we had better have reattached to a cached version
513 SkASSERT(surface->asTexture()->getUniqueKey() == key);
514 }
515 } else {
516 SkASSERT(!surface->getUniqueKey().isValid());
517 }
518 }
519 }
520
521 this->assign(std::move(surface));
522 return true;
523 }
524
525 #ifdef SK_DEBUG
validateSurface(const GrSurface * surface)526 void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
527 SkASSERT(surface->config() == fConfig);
528
529 this->onValidateSurface(surface);
530 }
531 #endif
532