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
32static 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
41static bool is_valid_non_lazy(SkISize dimensions) {
42 return dimensions.fWidth > 0 && dimensions.fHeight > 0;
43}
44#endif
45
46// Deferred version
47GrSurfaceProxy::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
67GrSurfaceProxy::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
90GrSurfaceProxy::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
108GrSurfaceProxy::~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
114sk_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
137bool 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
152void 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
170bool 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
198void GrSurfaceProxy::deinstantiate() {
199 SkASSERT(this->isInstantiated());
200 fTarget = nullptr;
201}
202
203void 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
222void 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
233GrOpsTask* GrSurfaceProxy::getLastOpsTask() {
234 return fLastRenderTask ? fLastRenderTask->asOpsTask() : nullptr;
235}
236
237SkISize 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
249bool GrSurfaceProxy::isFunctionallyExact() const {
250 SkASSERT(!this->isFullyLazy());
251 return fFit == SkBackingFit::kExact ||
252 fDimensions == GrResourceProvider::MakeApprox(fDimensions);
253}
254
255bool GrSurfaceProxy::isFormatCompressed(const GrCaps* caps) const {
256 return caps->isFormatCompressed(this->backendFormat());
257}
258
259#ifdef SK_DEBUG
260void GrSurfaceProxy::validate(GrContext_Base* context) const {
261 if (fTarget) {
262 SkASSERT(fTarget->getContext() == context);
263 }
264}
265#endif
266
267sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
268 GrSurfaceProxy* src,
269 GrSurfaceOrigin origin,
270 GrMipMapped mipMapped,
271 SkIRect srcRect,
272 SkBackingFit fit,
273 SkBudgeted budgeted,
274 RectsMustMatch rectsMustMatch) {
275 SkASSERT(!src->isFullyLazy());
276 int width;
277 int height;
278
279 SkIPoint dstPoint;
280 if (rectsMustMatch == RectsMustMatch::kYes) {
281 width = src->width();
282 height = src->height();
283 dstPoint = {srcRect.fLeft, srcRect.fTop};
284 } else {
285 width = srcRect.width();
286 height = srcRect.height();
287 dstPoint = {0, 0};
288 }
289
290 if (!srcRect.intersect(SkIRect::MakeSize(src->dimensions()))) {
291 return {};
292 }
293 auto format = src->backendFormat().makeTexture2D();
294 SkASSERT(format.isValid());
295
296 if (src->backendFormat().textureType() != GrTextureType::kExternal) {
297 auto dstContext =
298 GrSurfaceContext::Make(context, {width, height}, format, GrRenderable::kNo, 1,
299 mipMapped, src->isProtected(), origin, GrColorType::kUnknown,
300 kUnknown_SkAlphaType, nullptr, fit, budgeted);
301 if (dstContext && dstContext->copy(src, srcRect, dstPoint)) {
302 return dstContext->asSurfaceProxyRef();
303 }
304 }
305 if (src->asTextureProxy()) {
306 auto dstContext = GrRenderTargetContext::Make(
307 context, GrColorType::kUnknown, nullptr, fit, {width, height}, format, 1, mipMapped,
308 src->isProtected(), origin, budgeted, nullptr);
309 GrSurfaceProxyView view(sk_ref_sp(src), origin, GrSwizzle("rgba"));
310 if (dstContext && dstContext->blitTexture(std::move(view), srcRect, dstPoint)) {
311 return dstContext->asSurfaceProxyRef();
312 }
313 }
314 // Can't use backend copies or draws.
315 return nullptr;
316}
317
318sk_sp<GrSurfaceProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
319 GrSurfaceProxy* src,
320 GrSurfaceOrigin origin,
321 GrMipMapped mipMapped,
322 SkBackingFit fit,
323 SkBudgeted budgeted) {
324 SkASSERT(!src->isFullyLazy());
325 return Copy(context, src, origin, mipMapped, SkIRect::MakeSize(src->dimensions()), fit,
326 budgeted);
327}
328
329#if GR_TEST_UTILS
330int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
331 if (fTarget) {
332 return fTarget->testingOnly_getRefCnt();
333 }
334
335 return -1; // no backing GrSurface
336}
337
338GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
339 return fSurfaceFlags;
340}
341#endif
342
343void GrSurfaceProxyPriv::exactify(bool allocatedCaseOnly) {
344 SkASSERT(!fProxy->isFullyLazy());
345 if (this->isExact()) {
346 return;
347 }
348
349 SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
350
351 if (fProxy->fTarget) {
352 // The kApprox but already instantiated case. Setting the proxy's width & height to
353 // the instantiated width & height could have side-effects going forward, since we're
354 // obliterating the area of interest information. This call (exactify) only used
355 // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
356 // used for additional draws.
357 fProxy->fDimensions = fProxy->fTarget->dimensions();
358 return;
359 }
360
361#ifndef SK_CRIPPLE_TEXTURE_REUSE
362 // In the post-implicit-allocation world we can't convert this proxy to be exact fit
363 // at this point. With explicit allocation switching this to exact will result in a
364 // different allocation at flush time. With implicit allocation, allocation would occur
365 // at draw time (rather than flush time) so this pathway was encountered less often (if
366 // at all).
367 if (allocatedCaseOnly) {
368 return;
369 }
370#endif
371
372 // The kApprox uninstantiated case. Making this proxy be exact should be okay.
373 // It could mess things up if prior decisions were based on the approximate size.
374 fProxy->fFit = SkBackingFit::kExact;
375 // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
376 // already been computed we want to leave it alone so that amount will be removed when
377 // the special image goes away. If it hasn't been computed yet it might as well compute the
378 // exact amount.
379}
380
381bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
382 SkASSERT(fProxy->isLazy());
383
384 sk_sp<GrSurface> surface;
385 if (fProxy->asTextureProxy() && fProxy->asTextureProxy()->getUniqueKey().isValid()) {
386 // First try to reattach to a cached version if the proxy is uniquely keyed
387 surface = resourceProvider->findByUniqueKey<GrSurface>(
388 fProxy->asTextureProxy()->getUniqueKey());
389 }
390
391 bool syncKey = true;
392 bool releaseCallback = false;
393 if (!surface) {
394 auto result = fProxy->fLazyInstantiateCallback(resourceProvider);
395 surface = std::move(result.fSurface);
396 syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
397 releaseCallback = surface && result.fReleaseCallback;
398 }
399 if (!surface) {
400 fProxy->fDimensions.setEmpty();
401 return false;
402 }
403
404 if (fProxy->isFullyLazy()) {
405 // This was a fully lazy proxy. We need to fill in the width & height. For partially
406 // lazy proxies we must preserve the original width & height since that indicates
407 // the content area.
408 fProxy->fDimensions = surface->dimensions();
409 }
410
411 SkASSERT(fProxy->width() <= surface->width());
412 SkASSERT(fProxy->height() <= surface->height());
413
414 if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
415 texProxy->setTargetKeySync(syncKey);
416 if (syncKey) {
417 const GrUniqueKey& key = texProxy->getUniqueKey();
418 if (key.isValid()) {
419 if (!surface->asTexture()->getUniqueKey().isValid()) {
420 // If 'surface' is newly created, attach the unique key
421 resourceProvider->assignUniqueKeyToResource(key, surface.get());
422 } else {
423 // otherwise we had better have reattached to a cached version
424 SkASSERT(surface->asTexture()->getUniqueKey() == key);
425 }
426 } else {
427 SkASSERT(!surface->getUniqueKey().isValid());
428 }
429 }
430 }
431
432 this->assign(std::move(surface));
433 if (releaseCallback) {
434 fProxy->fLazyInstantiateCallback = nullptr;
435 }
436
437 return true;
438}
439
440#ifdef SK_DEBUG
441void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
442 SkASSERT(surface->backendFormat() == fFormat);
443
444 this->onValidateSurface(surface);
445}
446#endif
447