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/gpu/ganesh/image/SkImage_GaneshBase.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkPixmap.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkSize.h"
19 #include "include/core/SkTypes.h"
20 #include "include/gpu/GpuTypes.h"
21 #include "include/gpu/GrBackendSurface.h"
22 #include "include/gpu/GrDirectContext.h"
23 #include "include/gpu/GrRecordingContext.h"
24 #include "include/gpu/GrTypes.h"
25 #include "include/gpu/ganesh/SkImageGanesh.h"
26 #include "include/private/chromium/GrPromiseImageTexture.h"
27 #include "include/private/chromium/SkImageChromium.h"
28 #include "include/private/gpu/ganesh/GrTypesPriv.h"
29 #include "src/core/SkBitmapCache.h"
30 #include "src/core/SkColorSpacePriv.h"
31 #include "src/core/SkImageFilterTypes.h"
32 #include "src/core/SkImageFilter_Base.h"
33 #include "src/core/SkImageInfoPriv.h"
34 #include "src/gpu/RefCntedCallback.h"
35 #include "src/gpu/SkBackingFit.h"
36 #include "src/gpu/ganesh/GrCaps.h"
37 #include "src/gpu/ganesh/GrColorInfo.h"
38 #include "src/gpu/ganesh/GrDirectContextPriv.h"
39 #include "src/gpu/ganesh/GrImageContextPriv.h"
40 #include "src/gpu/ganesh/GrProxyProvider.h"
41 #include "src/gpu/ganesh/GrResourceCache.h"
42 #include "src/gpu/ganesh/GrResourceProvider.h"
43 #include "src/gpu/ganesh/GrSurface.h"
44 #include "src/gpu/ganesh/GrSurfaceProxy.h"
45 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
46 #include "src/gpu/ganesh/GrTexture.h"
47 #include "src/gpu/ganesh/GrTextureProxy.h"
48 #include "src/gpu/ganesh/SurfaceContext.h"
49 #include "src/gpu/ganesh/image/GrImageUtils.h"
50 #include "src/gpu/ganesh/image/SkImage_Ganesh.h"
51 #include "src/image/SkImage_Base.h"
52
53 #include <functional>
54 #include <memory>
55 #include <utility>
56
57 class GrContextThreadSafeProxy;
58 class SkImageFilter;
59 struct SkIPoint;
60
SkImage_GaneshBase(sk_sp<GrImageContext> context,SkImageInfo info,uint32_t uniqueID)61 SkImage_GaneshBase::SkImage_GaneshBase(sk_sp<GrImageContext> context,
62 SkImageInfo info,
63 uint32_t uniqueID)
64 : SkImage_Base(std::move(info), uniqueID), fContext(std::move(context)) {}
65
66 //////////////////////////////////////////////////////////////////////////////////////////////////
67
ValidateBackendTexture(const GrCaps * caps,const GrBackendTexture & tex,GrColorType grCT,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)68 bool SkImage_GaneshBase::ValidateBackendTexture(const GrCaps* caps,
69 const GrBackendTexture& tex,
70 GrColorType grCT,
71 SkColorType ct,
72 SkAlphaType at,
73 sk_sp<SkColorSpace> cs) {
74 if (!tex.isValid()) {
75 return false;
76 }
77 SkColorInfo info(ct, at, cs);
78 if (!SkColorInfoIsValid(info)) {
79 return false;
80 }
81 GrBackendFormat backendFormat = tex.getBackendFormat();
82 if (!backendFormat.isValid()) {
83 return false;
84 }
85
86 return caps->areColorTypeAndFormatCompatible(grCT, backendFormat);
87 }
88
ValidateCompressedBackendTexture(const GrCaps * caps,const GrBackendTexture & tex,SkAlphaType at)89 bool SkImage_GaneshBase::ValidateCompressedBackendTexture(const GrCaps* caps,
90 const GrBackendTexture& tex,
91 SkAlphaType at) {
92 if (!tex.isValid() || tex.width() <= 0 || tex.height() <= 0) {
93 return false;
94 }
95
96 if (tex.width() > caps->maxTextureSize() || tex.height() > caps->maxTextureSize()) {
97 return false;
98 }
99
100 if (at == kUnknown_SkAlphaType) {
101 return false;
102 }
103
104 GrBackendFormat backendFormat = tex.getBackendFormat();
105 if (!backendFormat.isValid()) {
106 return false;
107 }
108
109 if (!caps->isFormatCompressed(backendFormat)) {
110 return false;
111 }
112
113 return true;
114 }
115
116 //////////////////////////////////////////////////////////////////////////////////////////////////
117
getROPixels(GrDirectContext * dContext,SkBitmap * dst,CachingHint chint) const118 bool SkImage_GaneshBase::getROPixels(GrDirectContext* dContext,
119 SkBitmap* dst,
120 CachingHint chint) const {
121 if (!fContext->priv().matches(dContext)) {
122 return false;
123 }
124
125 const auto desc = SkBitmapCacheDesc::Make(this);
126 if (SkBitmapCache::Find(desc, dst)) {
127 SkASSERT(dst->isImmutable());
128 SkASSERT(dst->getPixels());
129 return true;
130 }
131
132 SkBitmapCache::RecPtr rec = nullptr;
133 SkPixmap pmap;
134 if (kAllow_CachingHint == chint) {
135 rec = SkBitmapCache::Alloc(desc, this->imageInfo(), &pmap);
136 if (!rec) {
137 return false;
138 }
139 } else {
140 if (!dst->tryAllocPixels(this->imageInfo()) || !dst->peekPixels(&pmap)) {
141 return false;
142 }
143 }
144
145 auto [view, ct] = skgpu::ganesh::AsView(dContext, this, skgpu::Mipmapped::kNo);
146 if (!view) {
147 return false;
148 }
149
150 GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
151 auto sContext = dContext->priv().makeSC(std::move(view), std::move(colorInfo));
152 if (!sContext) {
153 return false;
154 }
155
156 if (!sContext->readPixels(dContext, pmap, {0, 0})) {
157 return false;
158 }
159
160 if (rec) {
161 SkBitmapCache::Add(std::move(rec), dst);
162 this->notifyAddedToRasterCache();
163 }
164 return true;
165 }
166
makeSubset(GrDirectContext * direct,const SkIRect & subset) const167 sk_sp<SkImage> SkImage_GaneshBase::makeSubset(GrDirectContext* direct,
168 const SkIRect& subset) const {
169 if (!fContext->priv().matches(direct)) {
170 return nullptr;
171 }
172
173 if (subset.isEmpty()) {
174 return nullptr;
175 }
176
177 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
178 if (!bounds.contains(subset)) {
179 return nullptr;
180 }
181
182 // optimization : return self if the subset == our bounds
183 if (bounds == subset) {
184 return sk_ref_sp(const_cast<SkImage_GaneshBase*>(this));
185 }
186
187 return this->onMakeSubset(direct, subset);
188 }
189
onMakeSubset(GrDirectContext * direct,const SkIRect & subset) const190 sk_sp<SkImage> SkImage_GaneshBase::onMakeSubset(GrDirectContext* direct,
191 const SkIRect& subset) const {
192 if (!fContext->priv().matches(direct)) {
193 return nullptr;
194 }
195 auto [view, ct] = skgpu::ganesh::AsView(direct, this, skgpu::Mipmapped::kNo);
196 SkASSERT(view);
197 SkASSERT(ct == SkColorTypeToGrColorType(this->colorType()));
198
199 skgpu::Budgeted isBudgeted = view.proxy()->isBudgeted();
200 auto copyView = GrSurfaceProxyView::Copy(direct,
201 std::move(view),
202 skgpu::Mipmapped::kNo,
203 subset,
204 SkBackingFit::kExact,
205 isBudgeted,
206 /*label=*/"ImageGpuBase_MakeSubset");
207
208 if (!copyView) {
209 return nullptr;
210 }
211
212 return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(direct),
213 kNeedNewImageUniqueID,
214 std::move(copyView),
215 this->imageInfo().colorInfo());
216 }
217
onMakeSubset(skgpu::graphite::Recorder *,const SkIRect &,RequiredProperties) const218 sk_sp<SkImage> SkImage_GaneshBase::onMakeSubset(skgpu::graphite::Recorder*,
219 const SkIRect&,
220 RequiredProperties) const {
221 SkDEBUGFAIL("Cannot convert Ganesh-backed image to Graphite");
222 return nullptr;
223 }
224
makeColorTypeAndColorSpace(skgpu::graphite::Recorder *,SkColorType,sk_sp<SkColorSpace>,RequiredProperties) const225 sk_sp<SkImage> SkImage_GaneshBase::makeColorTypeAndColorSpace(skgpu::graphite::Recorder*,
226 SkColorType,
227 sk_sp<SkColorSpace>,
228 RequiredProperties) const {
229 SkDEBUGFAIL("Cannot convert Ganesh-backed image to Graphite");
230 return nullptr;
231 }
232
onReadPixels(GrDirectContext * dContext,const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint) const233 bool SkImage_GaneshBase::onReadPixels(GrDirectContext* dContext,
234 const SkImageInfo& dstInfo,
235 void* dstPixels,
236 size_t dstRB,
237 int srcX,
238 int srcY,
239 CachingHint) const {
240 if (!fContext->priv().matches(dContext) ||
241 !SkImageInfoValidConversion(dstInfo, this->imageInfo())) {
242 return false;
243 }
244
245 auto [view, ct] = skgpu::ganesh::AsView(dContext, this, skgpu::Mipmapped::kNo);
246 SkASSERT(view);
247
248 GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
249 auto sContext = dContext->priv().makeSC(std::move(view), colorInfo);
250 if (!sContext) {
251 return false;
252 }
253
254 return sContext->readPixels(dContext, {dstInfo, dstPixels, dstRB}, {srcX, srcY});
255 }
256
isValid(GrRecordingContext * context) const257 bool SkImage_GaneshBase::isValid(GrRecordingContext* context) const {
258 if (context && context->abandoned()) {
259 return false;
260 }
261 if (fContext->priv().abandoned()) {
262 return false;
263 }
264 if (context && !fContext->priv().matches(context)) {
265 return false;
266 }
267 return true;
268 }
269
makeColorTypeAndColorSpace(GrDirectContext * dContext,SkColorType targetColorType,sk_sp<SkColorSpace> targetCS) const270 sk_sp<SkImage> SkImage_GaneshBase::makeColorTypeAndColorSpace(GrDirectContext* dContext,
271 SkColorType targetColorType,
272 sk_sp<SkColorSpace> targetCS) const {
273 if (kUnknown_SkColorType == targetColorType || !targetCS) {
274 return nullptr;
275 }
276
277 auto myContext = this->context();
278 // This check is also performed in the subclass, but we do it here for the short-circuit below.
279 if (!myContext || !myContext->priv().matches(dContext)) {
280 return nullptr;
281 }
282
283 SkColorType colorType = this->colorType();
284 SkColorSpace* colorSpace = this->colorSpace();
285 if (!colorSpace) {
286 colorSpace = sk_srgb_singleton();
287 }
288 if (colorType == targetColorType &&
289 (SkColorSpace::Equals(colorSpace, targetCS.get()) || this->isAlphaOnly())) {
290 return sk_ref_sp(const_cast<SkImage_GaneshBase*>(this));
291 }
292
293 return this->onMakeColorTypeAndColorSpace(targetColorType, std::move(targetCS), dContext);
294 }
295
MakePromiseImageLazyProxy(GrContextThreadSafeProxy * tsp,SkISize dimensions,const GrBackendFormat & backendFormat,skgpu::Mipmapped mipmapped,SkImages::PromiseImageTextureFulfillProc fulfillProc,sk_sp<skgpu::RefCntedCallback> releaseHelper)296 sk_sp<GrTextureProxy> SkImage_GaneshBase::MakePromiseImageLazyProxy(
297 GrContextThreadSafeProxy* tsp,
298 SkISize dimensions,
299 const GrBackendFormat& backendFormat,
300 skgpu::Mipmapped mipmapped,
301 SkImages::PromiseImageTextureFulfillProc fulfillProc,
302 sk_sp<skgpu::RefCntedCallback> releaseHelper) {
303 SkASSERT(tsp);
304 SkASSERT(!dimensions.isEmpty());
305 SkASSERT(releaseHelper);
306
307 if (!fulfillProc) {
308 return nullptr;
309 }
310
311 if (mipmapped == skgpu::Mipmapped::kYes &&
312 GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
313 // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
314 // well.
315 return nullptr;
316 }
317
318 /**
319 * This class is the lazy instantiation callback for promise images. It manages calling the
320 * client's Fulfill and Release procs. It attempts to reuse a GrTexture instance in
321 * cases where the client provides the same GrPromiseImageTexture as Fulfill results for
322 * multiple SkImages. The created GrTexture is given a key based on a unique ID associated with
323 * the GrPromiseImageTexture.
324 *
325 * A key invalidation message is installed on the GrPromiseImageTexture so that the GrTexture
326 * is deleted once it can no longer be used to instantiate a proxy.
327 */
328 class PromiseLazyInstantiateCallback {
329 public:
330 PromiseLazyInstantiateCallback(SkImages::PromiseImageTextureFulfillProc fulfillProc,
331 sk_sp<skgpu::RefCntedCallback> releaseHelper)
332 : fFulfillProc(fulfillProc), fReleaseHelper(std::move(releaseHelper)) {}
333 PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
334 PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
335 // Because we get wrapped in std::function we must be copyable. But we should never
336 // be copied.
337 SkASSERT(false);
338 }
339 PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
340 PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
341 SkASSERT(false);
342 return *this;
343 }
344
345 ~PromiseLazyInstantiateCallback() {
346 // Our destructor can run on any thread. We trigger the unref of fTexture by message.
347 if (fTexture) {
348 GrResourceCache::ReturnResourceFromThread(std::move(fTexture), fTextureContextID);
349 }
350 }
351
352 GrSurfaceProxy::LazyCallbackResult operator()(GrResourceProvider* resourceProvider,
353 const GrSurfaceProxy::LazySurfaceDesc&) {
354 // We use the unique key in a way that is unrelated to the SkImage-based key that the
355 // proxy may receive, hence kUnsynced.
356 static constexpr auto kKeySyncMode =
357 GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced;
358
359 // In order to make the SkImage "thread safe" we rely on holding an extra ref to the
360 // texture in the callback and signalling the unref via a message to the resource cache.
361 // We need to extend the callback's lifetime to that of the proxy.
362 static constexpr auto kReleaseCallbackOnInstantiation = false;
363
364 // Our proxy is getting instantiated for the second+ time. We are only allowed to call
365 // Fulfill once. So return our cached result.
366 if (fTexture) {
367 return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
368 } else if (fFulfillProcFailed) {
369 // We've already called fulfill and it failed. Our contract says that we should only
370 // call each callback once.
371 return {};
372 }
373
374 SkImages::PromiseImageTextureContext textureContext = fReleaseHelper->context();
375 sk_sp<GrPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
376
377 if (!promiseTexture) {
378 fFulfillProcFailed = true;
379 return {};
380 }
381
382 const GrBackendTexture& backendTexture = promiseTexture->backendTexture();
383 if (!backendTexture.isValid()) {
384 return {};
385 }
386
387 fTexture = resourceProvider->wrapBackendTexture(
388 backendTexture, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRead_GrIOType);
389 if (!fTexture) {
390 return {};
391 }
392 fTexture->setRelease(fReleaseHelper);
393 auto dContext = fTexture->getContext();
394 fTextureContextID = dContext->directContextID();
395 return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
396 }
397
398 private:
399 SkImages::PromiseImageTextureFulfillProc fFulfillProc;
400 sk_sp<skgpu::RefCntedCallback> fReleaseHelper;
401 sk_sp<GrTexture> fTexture;
402 GrDirectContext::DirectContextID fTextureContextID;
403 bool fFulfillProcFailed = false;
404 } callback(fulfillProc, std::move(releaseHelper));
405
406 return GrProxyProvider::CreatePromiseProxy(
407 tsp, std::move(callback), backendFormat, dimensions, mipmapped);
408 }
409
410 namespace SkImages {
SubsetTextureFrom(GrDirectContext * context,const SkImage * img,const SkIRect & subset)411 sk_sp<SkImage> SubsetTextureFrom(GrDirectContext* context,
412 const SkImage* img,
413 const SkIRect& subset) {
414 if (context == nullptr || img == nullptr) {
415 return nullptr;
416 }
417 auto subsetImg = img->makeSubset(context, subset);
418 return SkImages::TextureFromImage(context, subsetImg.get());
419 }
420
MakeWithFilter(GrRecordingContext * rContext,sk_sp<SkImage> src,const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset)421 sk_sp<SkImage> MakeWithFilter(GrRecordingContext* rContext,
422 sk_sp<SkImage> src,
423 const SkImageFilter* filter,
424 const SkIRect& subset,
425 const SkIRect& clipBounds,
426 SkIRect* outSubset,
427 SkIPoint* offset) {
428 if (!rContext || !src || !filter) {
429 return nullptr;
430 }
431
432 GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin;
433 if (as_IB(src)->isGaneshBacked()) {
434 SkImage_GaneshBase* base = static_cast<SkImage_GaneshBase*>(src.get());
435 origin = base->origin();
436 }
437
438 sk_sp<skif::Backend> backend =
439 skif::MakeGaneshBackend(sk_ref_sp(rContext), origin, {}, src->colorType());
440 return as_IFB(filter)->makeImageWithFilter(std::move(backend),
441 std::move(src),
442 subset,
443 clipBounds,
444 outSubset,
445 offset);
446 }
447
GetContext(const SkImage * src)448 GrDirectContext* GetContext(const SkImage* src) {
449 if (!src || !as_IB(src)->isGaneshBacked()) {
450 return nullptr;
451 }
452 return as_IB(src)->directContext();
453 }
454
455
456 } // namespace SkImages
457