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