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