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