1/*
2 * Copyright 2018 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 "GrProxyProvider.h"
9
10#include "GrCaps.h"
11#include "GrRenderTarget.h"
12#include "GrResourceKey.h"
13#include "GrResourceProvider.h"
14#include "GrSurfaceProxy.h"
15#include "GrSurfaceProxyPriv.h"
16#include "GrTexture.h"
17#include "GrTextureProxyCacheAccess.h"
18#include "GrTextureRenderTargetProxy.h"
19#include "../private/GrSingleOwner.h"
20#include "SkGr.h"
21#include "SkImage.h"
22#include "SkImage_Base.h"
23#include "SkMipMap.h"
24
25#define ASSERT_SINGLE_OWNER \
26    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
27
28GrProxyProvider::GrProxyProvider(GrResourceProvider* resourceProvider,
29                                 GrResourceCache* resourceCache,
30                                 sk_sp<const GrCaps> caps,
31                                 GrSingleOwner* owner)
32        : fResourceProvider(resourceProvider)
33        , fResourceCache(resourceCache)
34        , fAbandoned(false)
35        , fCaps(caps)
36#ifdef SK_DEBUG
37        , fSingleOwner(owner)
38#endif
39{
40
41}
42
43GrProxyProvider::~GrProxyProvider() {
44    SkASSERT(!fUniquelyKeyedProxies.count());
45}
46
47bool GrProxyProvider::assignUniqueKeyToProxy(const GrUniqueKey& key, GrTextureProxy* proxy) {
48    ASSERT_SINGLE_OWNER
49    SkASSERT(key.isValid());
50    if (this->isAbandoned() || !proxy) {
51        return false;
52    }
53
54    // If there is already a GrResource with this key then the caller has violated the normal
55    // usage pattern of uniquely keyed resources (e.g., they have created one w/o first seeing
56    // if it already existed in the cache).
57    SkASSERT(!fResourceCache || !fResourceCache->findAndRefUniqueResource(key));
58
59    // Uncached resources can never have a unique key, unless they're wrapped resources. Wrapped
60    // resources are a special case: the unique keys give us a weak ref so that we can reuse the
61    // same resource (rather than re-wrapping). When a wrapped resource is no longer referenced,
62    // it will always be released - it is never converted to a scratch resource.
63    if (SkBudgeted::kNo == proxy->isBudgeted() &&
64                    (!proxy->priv().isInstantiated() ||
65                     !proxy->priv().peekSurface()->resourcePriv().refsWrappedObjects())) {
66        return false;
67    }
68
69    SkASSERT(!fUniquelyKeyedProxies.find(key));     // multiple proxies can't get the same key
70
71    proxy->cacheAccess().setUniqueKey(this, key);
72    SkASSERT(proxy->getUniqueKey() == key);
73    fUniquelyKeyedProxies.add(proxy);
74    return true;
75}
76
77void GrProxyProvider::adoptUniqueKeyFromSurface(GrTextureProxy* proxy, const GrSurface* surf) {
78    SkASSERT(surf->getUniqueKey().isValid());
79    proxy->cacheAccess().setUniqueKey(this, surf->getUniqueKey());
80    SkASSERT(proxy->getUniqueKey() == surf->getUniqueKey());
81    // multiple proxies can't get the same key
82    SkASSERT(!fUniquelyKeyedProxies.find(surf->getUniqueKey()));
83    fUniquelyKeyedProxies.add(proxy);
84}
85
86void GrProxyProvider::removeUniqueKeyFromProxy(const GrUniqueKey& key, GrTextureProxy* proxy) {
87    ASSERT_SINGLE_OWNER
88    if (this->isAbandoned() || !proxy) {
89        return;
90    }
91    this->processInvalidProxyUniqueKey(key, proxy, true);
92}
93
94sk_sp<GrTextureProxy> GrProxyProvider::findProxyByUniqueKey(const GrUniqueKey& key,
95                                                            GrSurfaceOrigin origin) {
96    ASSERT_SINGLE_OWNER
97
98    if (this->isAbandoned()) {
99        return nullptr;
100    }
101
102    sk_sp<GrTextureProxy> result = sk_ref_sp(fUniquelyKeyedProxies.find(key));
103    if (result) {
104        SkASSERT(result->origin() == origin);
105    }
106    return result;
107}
108
109sk_sp<GrTextureProxy> GrProxyProvider::createWrapped(sk_sp<GrTexture> tex, GrSurfaceOrigin origin) {
110#ifdef SK_DEBUG
111    if (tex->getUniqueKey().isValid()) {
112        SkASSERT(!this->findProxyByUniqueKey(tex->getUniqueKey(), origin));
113    }
114#endif
115
116    if (tex->asRenderTarget()) {
117        return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(std::move(tex), origin));
118    } else {
119        return sk_sp<GrTextureProxy>(new GrTextureProxy(std::move(tex), origin));
120    }
121}
122
123sk_sp<GrTextureProxy> GrProxyProvider::findOrCreateProxyByUniqueKey(const GrUniqueKey& key,
124                                                                    GrSurfaceOrigin origin) {
125    ASSERT_SINGLE_OWNER
126
127    if (this->isAbandoned()) {
128        return nullptr;
129    }
130
131    sk_sp<GrTextureProxy> result = this->findProxyByUniqueKey(key, origin);
132    if (result) {
133        return result;
134    }
135
136    if (!fResourceCache) {
137        return nullptr;
138    }
139
140    GrGpuResource* resource = fResourceCache->findAndRefUniqueResource(key);
141    if (!resource) {
142        return nullptr;
143    }
144
145    sk_sp<GrTexture> texture(static_cast<GrSurface*>(resource)->asTexture());
146    SkASSERT(texture);
147
148    result = this->createWrapped(std::move(texture), origin);
149    SkASSERT(result->getUniqueKey() == key);
150    // createWrapped should've added this for us
151    SkASSERT(fUniquelyKeyedProxies.find(key));
152    return result;
153}
154
155sk_sp<GrTextureProxy> GrProxyProvider::createInstantiatedProxy(const GrSurfaceDesc& desc,
156                                                               SkBackingFit fit,
157                                                               SkBudgeted budgeted,
158                                                               uint32_t flags) {
159    sk_sp<GrTexture> tex;
160
161    if (SkBackingFit::kApprox == fit) {
162        tex = fResourceProvider->createApproxTexture(desc, flags);
163    } else {
164        tex = fResourceProvider->createTexture(desc, budgeted, flags);
165    }
166    if (!tex) {
167        return nullptr;
168    }
169
170    return this->createWrapped(std::move(tex), desc.fOrigin);
171}
172
173sk_sp<GrTextureProxy> GrProxyProvider::createTextureProxy(const GrSurfaceDesc& desc,
174                                                          SkBudgeted budgeted,
175                                                          const void* srcData, size_t rowBytes) {
176    ASSERT_SINGLE_OWNER
177
178    if (this->isAbandoned()) {
179        return nullptr;
180    }
181
182    if (srcData) {
183        GrMipLevel mipLevel = { srcData, rowBytes };
184
185        sk_sp<GrTexture> tex = fResourceProvider->createTexture(desc, budgeted, mipLevel);
186        if (!tex) {
187            return nullptr;
188        }
189
190        return this->createWrapped(std::move(tex), desc.fOrigin);
191    }
192
193    return this->createProxy(desc, SkBackingFit::kExact, budgeted);
194}
195
196sk_sp<GrTextureProxy> GrProxyProvider::createTextureProxy(sk_sp<SkImage> srcImage,
197                                                          GrSurfaceFlags flags,
198                                                          GrSurfaceOrigin origin,
199                                                          int sampleCnt,
200                                                          SkBudgeted budgeted) {
201    ASSERT_SINGLE_OWNER
202    SkASSERT(srcImage);
203
204    if (this->isAbandoned()) {
205        return nullptr;
206    }
207
208    GrSurfaceDesc desc;
209    desc.fWidth = srcImage->width();
210    desc.fHeight = srcImage->height();
211    desc.fFlags = flags;
212    desc.fOrigin = origin;
213    desc.fSampleCnt = sampleCnt;
214    desc.fConfig = SkImageInfo2GrPixelConfig(as_IB(srcImage)->onImageInfo(), *this->caps());
215
216    sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
217            [desc, budgeted, srcImage]
218            (GrResourceProvider* resourceProvider, GrSurfaceOrigin* /*outOrigin*/) {
219                if (!resourceProvider) {
220                    // Nothing to clean up here. Once the proxy (and thus lambda) is deleted the ref
221                    // on srcImage will be released.
222                    return sk_sp<GrTexture>();
223                }
224                SkPixmap pixMap;
225                SkAssertResult(srcImage->peekPixels(&pixMap));
226                GrMipLevel mipLevel = { pixMap.addr(), pixMap.rowBytes() };
227
228                return resourceProvider->createTexture(desc, budgeted, mipLevel);
229            }, desc, GrMipMapped::kNo, SkBackingFit::kExact, budgeted);
230
231    if (fResourceProvider) {
232        // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however
233        // we're better off instantiating the proxy immediately here.
234        if (!proxy->priv().doLazyInstantiation(fResourceProvider)) {
235            return nullptr;
236        }
237    }
238    return proxy;
239}
240
241sk_sp<GrTextureProxy> GrProxyProvider::createMipMapProxy(
242                                                    const GrSurfaceDesc& desc, SkBudgeted budgeted,
243                                                    const GrMipLevel texels[], int mipLevelCount,
244                                                    SkDestinationSurfaceColorMode mipColorMode) {
245    ASSERT_SINGLE_OWNER
246
247    if (this->isAbandoned()) {
248        return nullptr;
249    }
250
251    if (!mipLevelCount) {
252        if (texels) {
253            return nullptr;
254        }
255        return this->createProxy(desc, SkBackingFit::kExact, budgeted);
256    }
257    if (!texels) {
258        return nullptr;
259    }
260
261    if (1 == mipLevelCount) {
262        return this->createTextureProxy(desc, budgeted, texels[0].fPixels, texels[0].fRowBytes);
263    }
264
265#ifdef SK_DEBUG
266    // There are only three states we want to be in when uploading data to a mipped surface.
267    // 1) We have data to upload to all layers
268    // 2) We are not uploading data to any layers
269    // 3) We are only uploading data to the base layer
270    // We check here to make sure we do not have any other state.
271    bool firstLevelHasData = SkToBool(texels[0].fPixels);
272    bool allOtherLevelsHaveData = true, allOtherLevelsLackData = true;
273    for  (int i = 1; i < mipLevelCount; ++i) {
274        if (texels[i].fPixels) {
275            allOtherLevelsLackData = false;
276        } else {
277            allOtherLevelsHaveData = false;
278        }
279    }
280    SkASSERT((firstLevelHasData && allOtherLevelsHaveData) || allOtherLevelsLackData);
281#endif
282
283    sk_sp<GrTexture> tex(fResourceProvider->createTexture(desc, budgeted,
284                                                          texels, mipLevelCount,
285                                                          mipColorMode));
286    if (!tex) {
287        return nullptr;
288    }
289
290    return this->createWrapped(std::move(tex), desc.fOrigin);
291}
292
293sk_sp<GrTextureProxy> GrProxyProvider::createMipMapProxy(const GrSurfaceDesc& desc,
294                                                         SkBudgeted budgeted) {
295    // SkMipMap doesn't include the base level in the level count so we have to add 1
296    int mipCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1;
297
298    std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipCount]);
299
300    // We don't want to upload any texel data
301    for (int i = 0; i < mipCount; i++) {
302        texels[i].fPixels = nullptr;
303        texels[i].fRowBytes = 0;
304    }
305
306    return this->createMipMapProxy(desc, budgeted, texels.get(), mipCount,
307                                   SkDestinationSurfaceColorMode::kLegacy);
308}
309
310sk_sp<GrTextureProxy> GrProxyProvider::createProxy(const GrSurfaceDesc& desc,
311                                                   SkBackingFit fit,
312                                                   SkBudgeted budgeted,
313                                                   uint32_t flags) {
314    SkASSERT(0 == flags || GrResourceProvider::kNoPendingIO_Flag == flags);
315
316    const GrCaps* caps = this->caps();
317
318    // TODO: move this logic into GrResourceProvider!
319    // TODO: share this testing code with check_texture_creation_params
320    if (!caps->isConfigTexturable(desc.fConfig)) {
321        return nullptr;
322    }
323
324    bool willBeRT = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
325    if (willBeRT && !caps->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 1)) {
326        return nullptr;
327    }
328
329    // We currently do not support multisampled textures
330    if (!willBeRT && desc.fSampleCnt > 1) {
331        return nullptr;
332    }
333
334    if (willBeRT && !caps->getSampleCount(desc.fSampleCnt, desc.fConfig)) {
335        return nullptr;
336    }
337
338    int maxSize;
339    if (willBeRT) {
340        maxSize = caps->maxRenderTargetSize();
341    } else {
342        maxSize = caps->maxTextureSize();
343    }
344
345    if (desc.fWidth > maxSize || desc.fHeight > maxSize || desc.fWidth <= 0 || desc.fHeight <= 0) {
346        return nullptr;
347    }
348
349    GrSurfaceDesc copyDesc = desc;
350    copyDesc.fSampleCnt = caps->getSampleCount(desc.fSampleCnt, desc.fConfig);
351
352#ifdef SK_DISABLE_DEFERRED_PROXIES
353    // Temporarily force instantiation for crbug.com/769760 and crbug.com/769898
354    sk_sp<GrTexture> tex;
355
356    if (SkBackingFit::kApprox == fit) {
357        tex = resourceProvider->createApproxTexture(copyDesc, flags);
358    } else {
359        tex = resourceProvider->createTexture(copyDesc, budgeted, flags);
360    }
361
362    if (!tex) {
363        return nullptr;
364    }
365
366    return GrSurfaceProxy::MakeWrapped(std::move(tex), copyDesc.fOrigin);
367#else
368    if (willBeRT) {
369        // We know anything we instantiate later from this deferred path will be
370        // both texturable and renderable
371        return sk_sp<GrTextureProxy>(new GrTextureRenderTargetProxy(*caps, copyDesc, fit,
372                                                                    budgeted, flags));
373    }
374
375    return sk_sp<GrTextureProxy>(new GrTextureProxy(copyDesc, fit, budgeted, nullptr, 0, flags));
376#endif
377}
378
379sk_sp<GrTextureProxy> GrProxyProvider::createWrappedTextureProxy(
380                                                         const GrBackendTexture& backendTex,
381                                                         GrSurfaceOrigin origin,
382                                                         GrWrapOwnership ownership,
383                                                         ReleaseProc releaseProc,
384                                                         ReleaseContext releaseCtx) {
385    if (this->isAbandoned()) {
386        return nullptr;
387    }
388
389    GrSurfaceDesc desc;
390    desc.fOrigin = origin;
391    desc.fWidth = backendTex.width();
392    desc.fHeight = backendTex.height();
393    desc.fConfig = backendTex.config();
394    GrMipMapped mipMapped = backendTex.hasMipMaps() ? GrMipMapped::kYes : GrMipMapped::kNo;
395
396    sk_sp<GrReleaseProcHelper> releaseHelper;
397    if (releaseProc) {
398        releaseHelper.reset(new GrReleaseProcHelper(releaseProc, releaseCtx));
399    }
400
401    sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
402            [backendTex, ownership, releaseHelper]
403            (GrResourceProvider* resourceProvider, GrSurfaceOrigin* /*outOrigin*/) {
404                if (!resourceProvider) {
405                    // If this had a releaseHelper it will get unrefed when we delete this lambda
406                    // and will call the release proc so that the client knows they can free the
407                    // underlying backend object.
408                    return sk_sp<GrTexture>();
409                }
410
411                sk_sp<GrTexture> tex = resourceProvider->wrapBackendTexture(backendTex,
412                                                                            ownership);
413                if (!tex) {
414                    return sk_sp<GrTexture>();
415                }
416                if (releaseHelper) {
417                    // This gives the texture a ref on the releaseHelper
418                    tex->setRelease(releaseHelper);
419                }
420                SkASSERT(!tex->asRenderTarget());   // Strictly a GrTexture
421                // Make sure we match how we created the proxy with SkBudgeted::kNo
422                SkASSERT(SkBudgeted::kNo == tex->resourcePriv().isBudgeted());
423
424                return tex;
425            }, desc, mipMapped, SkBackingFit::kExact, SkBudgeted::kNo);
426
427    if (fResourceProvider) {
428        // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however,
429        // we're better off instantiating the proxy immediately here.
430        if (!proxy->priv().doLazyInstantiation(fResourceProvider)) {
431            return nullptr;
432        }
433    }
434    return proxy;
435}
436
437sk_sp<GrTextureProxy> GrProxyProvider::createWrappedTextureProxy(const GrBackendTexture& tex,
438                                                                 GrSurfaceOrigin origin,
439                                                                 int sampleCnt) {
440    if (this->isAbandoned()) {
441        return nullptr;
442    }
443
444    sk_sp<GrTexture> texture(fResourceProvider->wrapRenderableBackendTexture(tex, sampleCnt));
445    if (!texture) {
446        return nullptr;
447    }
448    SkASSERT(texture->asRenderTarget());  // A GrTextureRenderTarget
449
450    return this->createWrapped(std::move(texture), origin);
451}
452
453sk_sp<GrSurfaceProxy> GrProxyProvider::createWrappedRenderTargetProxy(
454                                                             const GrBackendRenderTarget& backendRT,
455                                                             GrSurfaceOrigin origin) {
456    if (this->isAbandoned()) {
457        return nullptr;
458    }
459
460    sk_sp<GrRenderTarget> rt(fResourceProvider->wrapBackendRenderTarget(backendRT));
461    if (!rt) {
462        return nullptr;
463    }
464    SkASSERT(!rt->asTexture()); // Strictly a GrRenderTarget
465    SkASSERT(!rt->getUniqueKey().isValid());
466
467    return sk_sp<GrSurfaceProxy>(new GrRenderTargetProxy(std::move(rt), origin));
468}
469
470sk_sp<GrSurfaceProxy> GrProxyProvider::createWrappedRenderTargetProxy(const GrBackendTexture& tex,
471                                                                      GrSurfaceOrigin origin,
472                                                                      int sampleCnt) {
473    if (this->isAbandoned()) {
474        return nullptr;
475    }
476
477    sk_sp<GrRenderTarget> rt(fResourceProvider->wrapBackendTextureAsRenderTarget(tex, sampleCnt));
478    if (!rt) {
479        return nullptr;
480    }
481    SkASSERT(!rt->asTexture()); // Strictly a GrRenderTarget
482    SkASSERT(!rt->getUniqueKey().isValid());
483
484    return sk_sp<GrSurfaceProxy>(new GrRenderTargetProxy(std::move(rt), origin));
485}
486
487sk_sp<GrTextureProxy> GrProxyProvider::createLazyProxy(LazyInstantiateCallback&& callback,
488                                                       const GrSurfaceDesc& desc,
489                                                       GrMipMapped mipMapped,
490                                                       SkBackingFit fit, SkBudgeted budgeted) {
491    SkASSERT((desc.fWidth <= 0 && desc.fHeight <= 0) ||
492             (desc.fWidth > 0 && desc.fHeight > 0));
493    uint32_t flags = GrResourceProvider::kNoPendingIO_Flag;
494    return sk_sp<GrTextureProxy>(SkToBool(kRenderTarget_GrSurfaceFlag & desc.fFlags) ?
495                                 new GrTextureRenderTargetProxy(std::move(callback), desc,
496                                                                mipMapped, fit, budgeted, flags) :
497                                 new GrTextureProxy(std::move(callback), desc, mipMapped, fit,
498                                                    budgeted, flags));
499}
500
501sk_sp<GrTextureProxy> GrProxyProvider::createFullyLazyProxy(LazyInstantiateCallback&& callback,
502                                                            Renderable renderable,
503                                                            GrPixelConfig config) {
504    GrSurfaceDesc desc;
505    if (Renderable::kYes == renderable) {
506        desc.fFlags = kRenderTarget_GrSurfaceFlag;
507    }
508    desc.fOrigin = kTopLeft_GrSurfaceOrigin;
509    desc.fWidth = -1;
510    desc.fHeight = -1;
511    desc.fConfig = config;
512    desc.fSampleCnt = 1;
513
514    return this->createLazyProxy(std::move(callback), desc, GrMipMapped::kNo,
515                                 SkBackingFit::kApprox, SkBudgeted::kYes);
516
517}
518
519bool GrProxyProvider::IsFunctionallyExact(GrSurfaceProxy* proxy) {
520    return proxy->priv().isExact() || (SkIsPow2(proxy->width()) && SkIsPow2(proxy->height()));
521}
522
523void GrProxyProvider::processInvalidProxyUniqueKey(const GrUniqueKey& key) {
524    // Note: this method is called for the whole variety of GrGpuResources so often 'key'
525    // will not be in 'fUniquelyKeyedProxies'.
526    GrTextureProxy* proxy = fUniquelyKeyedProxies.find(key);
527    if (proxy) {
528        this->processInvalidProxyUniqueKey(key, proxy, false);
529    }
530}
531
532void GrProxyProvider::processInvalidProxyUniqueKey(const GrUniqueKey& key, GrTextureProxy* proxy,
533                                                   bool invalidateSurface) {
534    SkASSERT(proxy);
535    SkASSERT(proxy->getUniqueKey().isValid());
536    SkASSERT(proxy->getUniqueKey() == key);
537
538    fUniquelyKeyedProxies.remove(key);
539    proxy->cacheAccess().clearUniqueKey();
540
541    if (invalidateSurface && proxy->priv().isInstantiated()) {
542        GrSurface* surface = proxy->priv().peekSurface();
543        if (surface) {
544            surface->resourcePriv().removeUniqueKey();
545        }
546    }
547}
548
549void GrProxyProvider::removeAllUniqueKeys() {
550    UniquelyKeyedProxyHash::Iter iter(&fUniquelyKeyedProxies);
551    for (UniquelyKeyedProxyHash::Iter iter(&fUniquelyKeyedProxies); !iter.done(); ++iter) {
552        GrTextureProxy& tmp = *iter;
553
554        this->processInvalidProxyUniqueKey(tmp.getUniqueKey(), &tmp, false);
555    }
556    SkASSERT(!fUniquelyKeyedProxies.count());
557}
558