• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/image/SkImage_GpuBase.h"
9 
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkPixmap.h"
15 #include "include/core/SkPromiseImageTexture.h"
16 #include "include/core/SkSize.h"
17 #include "include/gpu/GpuTypes.h"
18 #include "include/gpu/GrBackendSurface.h"
19 #include "include/gpu/GrDirectContext.h"
20 #include "include/gpu/GrRecordingContext.h"
21 #include "include/private/base/SkAssert.h"
22 #include "include/private/gpu/ganesh/GrTypesPriv.h"
23 #include "src/core/SkBitmapCache.h"
24 #include "src/core/SkImageInfoPriv.h"
25 #include "src/gpu/RefCntedCallback.h"
26 #include "src/gpu/SkBackingFit.h"
27 #include "src/gpu/ganesh/GrCaps.h"
28 #include "src/gpu/ganesh/GrColorInfo.h"
29 #include "src/gpu/ganesh/GrDirectContextPriv.h"
30 #include "src/gpu/ganesh/GrImageContextPriv.h"
31 #include "src/gpu/ganesh/GrProxyProvider.h"
32 #include "src/gpu/ganesh/GrResourceCache.h"
33 #include "src/gpu/ganesh/GrResourceProvider.h"
34 #include "src/gpu/ganesh/GrSurface.h"
35 #include "src/gpu/ganesh/GrSurfaceProxy.h"
36 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
37 #include "src/gpu/ganesh/GrTexture.h"
38 #include "src/gpu/ganesh/GrTextureProxy.h"
39 #include "src/gpu/ganesh/SurfaceContext.h"
40 #include "src/image/SkImage_Gpu.h"
41 
42 #include <functional>
43 #include <memory>
44 #include <utility>
45 
46 class GrContextThreadSafeProxy;
47 class SkImage;
48 enum SkColorType : int;
49 struct SkIRect;
50 
51 #if defined(SK_GRAPHITE)
52 #include "src/gpu/graphite/Log.h"
53 #endif
54 
SkImage_GpuBase(sk_sp<GrImageContext> context,SkImageInfo info,uint32_t uniqueID)55 SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrImageContext> context, SkImageInfo info, uint32_t uniqueID)
56         : SkImage_Base(std::move(info), uniqueID)
57         , fContext(std::move(context)) {}
58 
59 //////////////////////////////////////////////////////////////////////////////////////////////////
60 
ValidateBackendTexture(const GrCaps * caps,const GrBackendTexture & tex,GrColorType grCT,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)61 bool SkImage_GpuBase::ValidateBackendTexture(const GrCaps* caps, const GrBackendTexture& tex,
62                                              GrColorType grCT, SkColorType ct, SkAlphaType at,
63                                              sk_sp<SkColorSpace> cs) {
64     if (!tex.isValid()) {
65         return false;
66     }
67     SkColorInfo info(ct, at, cs);
68     if (!SkColorInfoIsValid(info)) {
69         return false;
70     }
71     GrBackendFormat backendFormat = tex.getBackendFormat();
72     if (!backendFormat.isValid()) {
73         return false;
74     }
75 
76     return caps->areColorTypeAndFormatCompatible(grCT, backendFormat);
77 }
78 
ValidateCompressedBackendTexture(const GrCaps * caps,const GrBackendTexture & tex,SkAlphaType at)79 bool SkImage_GpuBase::ValidateCompressedBackendTexture(const GrCaps* caps,
80                                                        const GrBackendTexture& tex,
81                                                        SkAlphaType at) {
82     if (!tex.isValid() || tex.width() <= 0 || tex.height() <= 0) {
83         return false;
84     }
85 
86     if (tex.width() > caps->maxTextureSize() || tex.height() > caps->maxTextureSize()) {
87         return false;
88     }
89 
90     if (at == kUnknown_SkAlphaType) {
91         return false;
92     }
93 
94     GrBackendFormat backendFormat = tex.getBackendFormat();
95     if (!backendFormat.isValid()) {
96         return false;
97     }
98 
99     if (!caps->isFormatCompressed(backendFormat)) {
100         return false;
101     }
102 
103     return true;
104 }
105 
106 //////////////////////////////////////////////////////////////////////////////////////////////////
107 
getROPixels(GrDirectContext * dContext,SkBitmap * dst,CachingHint chint) const108 bool SkImage_GpuBase::getROPixels(GrDirectContext* dContext,
109                                   SkBitmap* dst,
110                                   CachingHint chint) const {
111     if (!fContext->priv().matches(dContext)) {
112         return false;
113     }
114 
115     const auto desc = SkBitmapCacheDesc::Make(this);
116     if (SkBitmapCache::Find(desc, dst)) {
117         SkASSERT(dst->isImmutable());
118         SkASSERT(dst->getPixels());
119         return true;
120     }
121 
122     SkBitmapCache::RecPtr rec = nullptr;
123     SkPixmap pmap;
124     if (kAllow_CachingHint == chint) {
125         rec = SkBitmapCache::Alloc(desc, this->imageInfo(), &pmap);
126         if (!rec) {
127             return false;
128         }
129     } else {
130         if (!dst->tryAllocPixels(this->imageInfo()) || !dst->peekPixels(&pmap)) {
131             return false;
132         }
133     }
134 
135     auto [view, ct] = this->asView(dContext, skgpu::Mipmapped::kNo);
136     if (!view) {
137         return false;
138     }
139 
140     GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
141     auto sContext = dContext->priv().makeSC(std::move(view), std::move(colorInfo));
142     if (!sContext) {
143         return false;
144     }
145 
146     if (!sContext->readPixels(dContext, pmap, {0, 0})) {
147         return false;
148     }
149 
150     if (rec) {
151         SkBitmapCache::Add(std::move(rec), dst);
152         this->notifyAddedToRasterCache();
153     }
154     return true;
155 }
156 
onMakeSubset(const SkIRect & subset,GrDirectContext * direct) const157 sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset,
158                                              GrDirectContext* direct) const {
159     if (!fContext->priv().matches(direct)) {
160         return nullptr;
161     }
162 
163     auto [view, ct] = this->asView(direct, skgpu::Mipmapped::kNo);
164     SkASSERT(view);
165     SkASSERT(ct == SkColorTypeToGrColorType(this->colorType()));
166 
167     skgpu::Budgeted isBudgeted = view.proxy()->isBudgeted();
168     auto copyView = GrSurfaceProxyView::Copy(direct,
169                                              std::move(view),
170                                              skgpu::Mipmapped::kNo,
171                                              subset,
172                                              SkBackingFit::kExact,
173                                              isBudgeted,
174                                              /*label=*/"ImageGpuBase_MakeSubset");
175 
176     if (!copyView) {
177         return nullptr;
178     }
179 
180     return sk_make_sp<SkImage_Gpu>(sk_ref_sp(direct),
181                                    kNeedNewImageUniqueID,
182                                    std::move(copyView),
183                                    this->imageInfo().colorInfo());
184 }
185 
186 #if defined(SK_GRAPHITE)
onMakeTextureImage(skgpu::graphite::Recorder *,SkImage::RequiredImageProperties) const187 sk_sp<SkImage> SkImage_GpuBase::onMakeTextureImage(skgpu::graphite::Recorder*,
188                                                    SkImage::RequiredImageProperties) const {
189     SKGPU_LOG_W("Cannot convert Ganesh-backed image to Graphite");
190     return nullptr;
191 }
192 
onMakeSubset(const SkIRect &,skgpu::graphite::Recorder *,RequiredImageProperties) const193 sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect&,
194                                              skgpu::graphite::Recorder*,
195                                              RequiredImageProperties) const {
196     SKGPU_LOG_W("Cannot convert Ganesh-backed image to Graphite");
197     return nullptr;
198 }
199 
onMakeColorTypeAndColorSpace(SkColorType,sk_sp<SkColorSpace>,skgpu::graphite::Recorder *,RequiredImageProperties) const200 sk_sp<SkImage> SkImage_GpuBase::onMakeColorTypeAndColorSpace(SkColorType,
201                                                              sk_sp<SkColorSpace>,
202                                                              skgpu::graphite::Recorder*,
203                                                              RequiredImageProperties) const {
204     SKGPU_LOG_W("Cannot convert Ganesh-backed image to Graphite");
205     return nullptr;
206 }
207 #endif
208 
onReadPixels(GrDirectContext * dContext,const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint) const209 bool SkImage_GpuBase::onReadPixels(GrDirectContext* dContext,
210                                    const SkImageInfo& dstInfo,
211                                    void* dstPixels,
212                                    size_t dstRB,
213                                    int srcX,
214                                    int srcY,
215                                    CachingHint) const {
216     if (!fContext->priv().matches(dContext) ||
217         !SkImageInfoValidConversion(dstInfo, this->imageInfo())) {
218         return false;
219     }
220 
221     auto [view, ct] = this->asView(dContext, skgpu::Mipmapped::kNo);
222     SkASSERT(view);
223 
224     GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
225     auto sContext = dContext->priv().makeSC(std::move(view), colorInfo);
226     if (!sContext) {
227         return false;
228     }
229 
230     return sContext->readPixels(dContext, {dstInfo, dstPixels, dstRB}, {srcX, srcY});
231 }
232 
onIsValid(GrRecordingContext * context) const233 bool SkImage_GpuBase::onIsValid(GrRecordingContext* context) const {
234     // The base class has already checked that 'context' isn't abandoned (if it's not nullptr)
235     if (fContext->priv().abandoned()) {
236         return false;
237     }
238 
239     if (context && !fContext->priv().matches(context)) {
240         return false;
241     }
242 
243     return true;
244 }
245 
MakePromiseImageLazyProxy(GrContextThreadSafeProxy * tsp,SkISize dimensions,GrBackendFormat backendFormat,skgpu::Mipmapped mipmapped,PromiseImageTextureFulfillProc fulfillProc,sk_sp<skgpu::RefCntedCallback> releaseHelper)246 sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
247         GrContextThreadSafeProxy* tsp,
248         SkISize dimensions,
249         GrBackendFormat backendFormat,
250         skgpu::Mipmapped mipmapped,
251         PromiseImageTextureFulfillProc fulfillProc,
252         sk_sp<skgpu::RefCntedCallback> releaseHelper) {
253     SkASSERT(tsp);
254     SkASSERT(!dimensions.isEmpty());
255     SkASSERT(releaseHelper);
256 
257     if (!fulfillProc) {
258         return nullptr;
259     }
260 
261     if (mipmapped == skgpu::Mipmapped::kYes &&
262         GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
263         // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
264         // well.
265         return nullptr;
266     }
267 
268     /**
269      * This class is the lazy instantiation callback for promise images. It manages calling the
270      * client's Fulfill and Release procs. It attempts to reuse a GrTexture instance in
271      * cases where the client provides the same SkPromiseImageTexture as Fulfill results for
272      * multiple SkImages. The created GrTexture is given a key based on a unique ID associated with
273      * the SkPromiseImageTexture.
274      *
275      * A key invalidation message is installed on the SkPromiseImageTexture so that the GrTexture
276      * is deleted once it can no longer be used to instantiate a proxy.
277      */
278     class PromiseLazyInstantiateCallback {
279     public:
280         PromiseLazyInstantiateCallback(PromiseImageTextureFulfillProc fulfillProc,
281                                        sk_sp<skgpu::RefCntedCallback> releaseHelper)
282                 : fFulfillProc(fulfillProc), fReleaseHelper(std::move(releaseHelper)) {}
283         PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
284         PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
285             // Because we get wrapped in std::function we must be copyable. But we should never
286             // be copied.
287             SkASSERT(false);
288         }
289         PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
290         PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
291             SkASSERT(false);
292             return *this;
293         }
294 
295         ~PromiseLazyInstantiateCallback() {
296             // Our destructor can run on any thread. We trigger the unref of fTexture by message.
297             if (fTexture) {
298                 GrResourceCache::ReturnResourceFromThread(std::move(fTexture), fTextureContextID);
299             }
300         }
301 
302         GrSurfaceProxy::LazyCallbackResult operator()(GrResourceProvider* resourceProvider,
303                                                       const GrSurfaceProxy::LazySurfaceDesc&) {
304             // We use the unique key in a way that is unrelated to the SkImage-based key that the
305             // proxy may receive, hence kUnsynced.
306             static constexpr auto kKeySyncMode =
307                     GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced;
308 
309             // In order to make the SkImage "thread safe" we rely on holding an extra ref to the
310             // texture in the callback and signalling the unref via a message to the resource cache.
311             // We need to extend the callback's lifetime to that of the proxy.
312             static constexpr auto kReleaseCallbackOnInstantiation = false;
313 
314             // Our proxy is getting instantiated for the second+ time. We are only allowed to call
315             // Fulfill once. So return our cached result.
316             if (fTexture) {
317                 return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
318             } else if (fFulfillProcFailed) {
319                 // We've already called fulfill and it failed. Our contract says that we should only
320                 // call each callback once.
321                 return {};
322             }
323 
324             PromiseImageTextureContext textureContext = fReleaseHelper->context();
325             sk_sp<SkPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
326 
327             if (!promiseTexture) {
328                 fFulfillProcFailed = true;
329                 return {};
330             }
331 
332             const GrBackendTexture& backendTexture = promiseTexture->backendTexture();
333             if (!backendTexture.isValid()) {
334                 return {};
335             }
336 
337             fTexture = resourceProvider->wrapBackendTexture(backendTexture,
338                                                             kBorrow_GrWrapOwnership,
339                                                             GrWrapCacheable::kNo,
340                                                             kRead_GrIOType);
341             if (!fTexture) {
342                 return {};
343             }
344             fTexture->setRelease(fReleaseHelper);
345             auto dContext = fTexture->getContext();
346             fTextureContextID = dContext->directContextID();
347             return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
348         }
349 
350     private:
351         PromiseImageTextureFulfillProc fFulfillProc;
352         sk_sp<skgpu::RefCntedCallback> fReleaseHelper;
353         sk_sp<GrTexture> fTexture;
354         GrDirectContext::DirectContextID fTextureContextID;
355         bool fFulfillProcFailed = false;
356     } callback(fulfillProc, std::move(releaseHelper));
357 
358     return GrProxyProvider::CreatePromiseProxy(tsp, std::move(callback), backendFormat, dimensions,
359                                                mipmapped);
360 }
361