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