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