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