1 /*
2 * Copyright 2023 Google LLC
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 "include/gpu/ganesh/SkImageGanesh.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkData.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkPixmap.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkSamplingOptions.h"
19 #include "include/core/SkSize.h"
20 #include "include/gpu/GpuTypes.h"
21 #include "include/gpu/GrBackendSurface.h"
22 #include "include/gpu/GrContextThreadSafeProxy.h"
23 #include "include/gpu/GrDirectContext.h"
24 #include "include/gpu/GrRecordingContext.h"
25 #include "include/gpu/GrTypes.h"
26 #include "include/gpu/ganesh/GrExternalTextureGenerator.h"
27 #include "include/private/base/SkAssert.h"
28 #include "include/private/chromium/SkImageChromium.h"
29 #include "include/private/gpu/ganesh/GrImageContext.h"
30 #include "include/private/gpu/ganesh/GrTextureGenerator.h"
31 #include "include/private/gpu/ganesh/GrTypesPriv.h"
32 #include "src/core/SkAutoPixmapStorage.h"
33 #include "src/core/SkImageInfoPriv.h"
34 #include "src/gpu/GpuTypesPriv.h"
35 #include "src/gpu/RefCntedCallback.h"
36 #include "src/gpu/Swizzle.h"
37 #include "src/gpu/ganesh/GrBackendTextureImageGenerator.h"
38 #include "src/gpu/ganesh/GrBackendUtils.h"
39 #include "src/gpu/ganesh/GrCaps.h"
40 #include "src/gpu/ganesh/GrContextThreadSafeProxyPriv.h"
41 #include "src/gpu/ganesh/GrDirectContextPriv.h"
42 #include "src/gpu/ganesh/GrGpu.h"
43 #include "src/gpu/ganesh/GrGpuResourcePriv.h"
44 #include "src/gpu/ganesh/GrImageContextPriv.h"
45 #include "src/gpu/ganesh/GrProxyProvider.h"
46 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
47 #include "src/gpu/ganesh/GrSemaphore.h"
48 #include "src/gpu/ganesh/GrSurfaceProxy.h"
49 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
50 #include "src/gpu/ganesh/GrTexture.h"
51 #include "src/gpu/ganesh/GrTextureProxy.h"
52 #include "src/gpu/ganesh/SkGr.h"
53 #include "src/gpu/ganesh/image/GrImageUtils.h"
54 #include "src/gpu/ganesh/image/SkImage_Ganesh.h"
55 #include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
56 #include "src/image/SkImage_Base.h"
57
58 #include <algorithm>
59 #include <memory>
60 #include <utility>
61
62 enum SkColorType : int;
63 enum class SkTextureCompressionType;
64
65 namespace SkImages {
66
MakeBackendTextureFromImage(GrDirectContext * direct,sk_sp<SkImage> image,GrBackendTexture * backendTexture,BackendTextureReleaseProc * releaseProc)67 bool MakeBackendTextureFromImage(GrDirectContext* direct,
68 sk_sp<SkImage> image,
69 GrBackendTexture* backendTexture,
70 BackendTextureReleaseProc* releaseProc) {
71 if (!image || !backendTexture || !releaseProc) {
72 return false;
73 }
74
75 auto [view, ct] = skgpu::ganesh::AsView(direct, image, skgpu::Mipmapped::kNo);
76 if (!view) {
77 return false;
78 }
79
80 // Flush any pending IO on the texture.
81 direct->priv().flushSurface(view.proxy());
82
83 GrTexture* texture = view.asTextureProxy()->peekTexture();
84 if (!texture) {
85 return false;
86 }
87 // We must make a copy of the image if the image is not unique, if the GrTexture owned by the
88 // image is not unique, or if the texture wraps an external object.
89 if (!image->unique() || !texture->unique() || texture->resourcePriv().refsWrappedObjects()) {
90 // onMakeSubset will always copy the image.
91 image = as_IB(image)->onMakeSubset(direct, image->bounds());
92 if (!image) {
93 return false;
94 }
95 return MakeBackendTextureFromImage(direct, std::move(image), backendTexture, releaseProc);
96 }
97
98 SkASSERT(!texture->resourcePriv().refsWrappedObjects());
99 SkASSERT(texture->unique());
100 SkASSERT(image->unique());
101
102 // Take a reference to the GrTexture and release the image.
103 sk_sp<GrTexture> textureRef = sk_ref_sp(texture);
104 view.reset();
105 image = nullptr;
106 SkASSERT(textureRef->unique());
107
108 // Steal the backend texture from the GrTexture, releasing the GrTexture in the process.
109 return GrTexture::StealBackendTexture(std::move(textureRef), backendTexture, releaseProc);
110 }
111
GetBackendTextureFromImage(const SkImage * img,GrBackendTexture * outTexture,bool flushPendingGrContextIO,GrSurfaceOrigin * origin)112 bool GetBackendTextureFromImage(const SkImage* img,
113 GrBackendTexture* outTexture,
114 bool flushPendingGrContextIO,
115 GrSurfaceOrigin* origin) {
116 if (!img) {
117 return false;
118 }
119 auto ib = as_IB(img);
120 if (ib->type() != SkImage_Base::Type::kGanesh) {
121 return false;
122 }
123 auto ig = static_cast<const SkImage_Ganesh*>(img);
124 return ig->getExistingBackendTexture(outTexture, flushPendingGrContextIO, origin);
125 }
126
TextureFromCompressedTexture(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin origin,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,TextureReleaseProc textureReleaseProc,ReleaseContext releaseContext)127 sk_sp<SkImage> TextureFromCompressedTexture(GrRecordingContext* context,
128 const GrBackendTexture& backendTexture,
129 GrSurfaceOrigin origin,
130 SkAlphaType alphaType,
131 sk_sp<SkColorSpace> colorSpace,
132 TextureReleaseProc textureReleaseProc,
133 ReleaseContext releaseContext) {
134 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
135
136 if (!context) {
137 return nullptr;
138 }
139
140 const GrCaps* caps = context->priv().caps();
141
142 if (!SkImage_GaneshBase::ValidateCompressedBackendTexture(caps, backendTexture, alphaType)) {
143 return nullptr;
144 }
145
146 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
147 sk_sp<GrTextureProxy> proxy =
148 proxyProvider->wrapCompressedBackendTexture(backendTexture,
149 kBorrow_GrWrapOwnership,
150 GrWrapCacheable::kNo,
151 std::move(releaseHelper));
152 if (!proxy) {
153 return nullptr;
154 }
155
156 SkTextureCompressionType type =
157 GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
158 SkColorType ct = skgpu::CompressionTypeToSkColorType(type);
159
160 GrSurfaceProxyView view(std::move(proxy), origin, skgpu::Swizzle::RGBA());
161 return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(context),
162 kNeedNewImageUniqueID,
163 std::move(view),
164 SkColorInfo(ct, alphaType, std::move(colorSpace)));
165 }
166
new_wrapped_texture_common(GrRecordingContext * rContext,const GrBackendTexture & backendTex,GrColorType colorType,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> colorSpace,GrWrapOwnership ownership,sk_sp<skgpu::RefCntedCallback> releaseHelper)167 static sk_sp<SkImage> new_wrapped_texture_common(GrRecordingContext* rContext,
168 const GrBackendTexture& backendTex,
169 GrColorType colorType,
170 GrSurfaceOrigin origin,
171 SkAlphaType at,
172 sk_sp<SkColorSpace> colorSpace,
173 GrWrapOwnership ownership,
174 sk_sp<skgpu::RefCntedCallback> releaseHelper) {
175 if (!backendTex.isValid() || backendTex.width() <= 0 || backendTex.height() <= 0) {
176 return nullptr;
177 }
178
179 GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
180 sk_sp<GrTextureProxy> proxy = proxyProvider->wrapBackendTexture(
181 backendTex, ownership, GrWrapCacheable::kNo, kRead_GrIOType, std::move(releaseHelper));
182 if (!proxy) {
183 return nullptr;
184 }
185
186 skgpu::Swizzle swizzle =
187 rContext->priv().caps()->getReadSwizzle(proxy->backendFormat(), colorType);
188 GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
189 SkColorInfo info(GrColorTypeToSkColorType(colorType), at, std::move(colorSpace));
190 return sk_make_sp<SkImage_Ganesh>(
191 sk_ref_sp(rContext), kNeedNewImageUniqueID, std::move(view), std::move(info));
192 }
193
BorrowTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin origin,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,TextureReleaseProc textureReleaseProc,ReleaseContext releaseContext)194 sk_sp<SkImage> BorrowTextureFrom(GrRecordingContext* context,
195 const GrBackendTexture& backendTexture,
196 GrSurfaceOrigin origin,
197 SkColorType colorType,
198 SkAlphaType alphaType,
199 sk_sp<SkColorSpace> colorSpace,
200 TextureReleaseProc textureReleaseProc,
201 ReleaseContext releaseContext) {
202 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
203
204 if (!context) {
205 return nullptr;
206 }
207
208 const GrCaps* caps = context->priv().caps();
209
210 GrColorType grColorType = SkColorTypeToGrColorType(colorType);
211 if (GrColorType::kUnknown == grColorType) {
212 return nullptr;
213 }
214
215 if (!SkImage_GaneshBase::ValidateBackendTexture(
216 caps, backendTexture, grColorType, colorType, alphaType, colorSpace)) {
217 return nullptr;
218 }
219
220 return new_wrapped_texture_common(context,
221 backendTexture,
222 grColorType,
223 origin,
224 alphaType,
225 std::move(colorSpace),
226 kBorrow_GrWrapOwnership,
227 std::move(releaseHelper));
228 }
229
AdoptTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin textureOrigin,SkColorType colorType)230 sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
231 const GrBackendTexture& backendTexture,
232 GrSurfaceOrigin textureOrigin,
233 SkColorType colorType) {
234 return AdoptTextureFrom(
235 context, backendTexture, textureOrigin, colorType, kPremul_SkAlphaType, nullptr);
236 }
237
AdoptTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin textureOrigin,SkColorType colorType,SkAlphaType alphaType)238 sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
239 const GrBackendTexture& backendTexture,
240 GrSurfaceOrigin textureOrigin,
241 SkColorType colorType,
242 SkAlphaType alphaType) {
243 return AdoptTextureFrom(context, backendTexture, textureOrigin, colorType, alphaType, nullptr);
244 }
245
AdoptTextureFrom(GrRecordingContext * context,const GrBackendTexture & backendTexture,GrSurfaceOrigin origin,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)246 sk_sp<SkImage> AdoptTextureFrom(GrRecordingContext* context,
247 const GrBackendTexture& backendTexture,
248 GrSurfaceOrigin origin,
249 SkColorType colorType,
250 SkAlphaType alphaType,
251 sk_sp<SkColorSpace> colorSpace) {
252 auto dContext = GrAsDirectContext(context);
253 if (!dContext) {
254 // We have a DDL context and we don't support adopted textures for them.
255 return nullptr;
256 }
257
258 const GrCaps* caps = dContext->priv().caps();
259
260 GrColorType grColorType = SkColorTypeToGrColorType(colorType);
261 if (GrColorType::kUnknown == grColorType) {
262 return nullptr;
263 }
264
265 if (!SkImage_GaneshBase::ValidateBackendTexture(
266 caps, backendTexture, grColorType, colorType, alphaType, colorSpace)) {
267 return nullptr;
268 }
269
270 return new_wrapped_texture_common(dContext,
271 backendTexture,
272 grColorType,
273 origin,
274 alphaType,
275 std::move(colorSpace),
276 kAdopt_GrWrapOwnership,
277 nullptr);
278 }
279
TextureFromCompressedTextureData(GrDirectContext * direct,sk_sp<SkData> data,int width,int height,SkTextureCompressionType type,skgpu::Mipmapped mipmapped,GrProtected isProtected)280 sk_sp<SkImage> TextureFromCompressedTextureData(GrDirectContext* direct,
281 sk_sp<SkData> data,
282 int width,
283 int height,
284 SkTextureCompressionType type,
285 skgpu::Mipmapped mipmapped,
286 GrProtected isProtected) {
287 if (!direct || !data) {
288 return nullptr;
289 }
290
291 GrBackendFormat beFormat = direct->compressedBackendFormat(type);
292 if (!beFormat.isValid()) {
293 sk_sp<SkImage> tmp = RasterFromCompressedTextureData(std::move(data), width, height, type);
294 if (!tmp) {
295 return nullptr;
296 }
297 return TextureFromImage(direct, tmp, mipmapped);
298 }
299
300 GrProxyProvider* proxyProvider = direct->priv().proxyProvider();
301 sk_sp<GrTextureProxy> proxy = proxyProvider->createCompressedTextureProxy(
302 {width, height}, skgpu::Budgeted::kYes, mipmapped, isProtected, type, std::move(data));
303 if (!proxy) {
304 return nullptr;
305 }
306 GrSurfaceProxyView view(std::move(proxy));
307
308 SkColorType colorType = skgpu::CompressionTypeToSkColorType(type);
309
310 return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(direct),
311 kNeedNewImageUniqueID,
312 std::move(view),
313 SkColorInfo(colorType, kOpaque_SkAlphaType, nullptr));
314 }
315
PromiseTextureFrom(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,const GrBackendFormat & backendFormat,SkISize dimensions,skgpu::Mipmapped mipmapped,GrSurfaceOrigin origin,SkColorType colorType,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace,PromiseImageTextureFulfillProc textureFulfillProc,PromiseImageTextureReleaseProc textureReleaseProc,PromiseImageTextureContext textureContext)316 sk_sp<SkImage> PromiseTextureFrom(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
317 const GrBackendFormat& backendFormat,
318 SkISize dimensions,
319 skgpu::Mipmapped mipmapped,
320 GrSurfaceOrigin origin,
321 SkColorType colorType,
322 SkAlphaType alphaType,
323 sk_sp<SkColorSpace> colorSpace,
324 PromiseImageTextureFulfillProc textureFulfillProc,
325 PromiseImageTextureReleaseProc textureReleaseProc,
326 PromiseImageTextureContext textureContext) {
327 // Our contract is that we will always call the release proc even on failure.
328 // We use the helper to convey the context, so we need to ensure make doesn't fail.
329 textureReleaseProc = textureReleaseProc ? textureReleaseProc : [](void*) {};
330 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, textureContext);
331 SkImageInfo info = SkImageInfo::Make(dimensions, colorType, alphaType, colorSpace);
332 if (!SkImageInfoIsValid(info)) {
333 return nullptr;
334 }
335
336 if (!threadSafeProxy) {
337 return nullptr;
338 }
339
340 if (dimensions.isEmpty()) {
341 return nullptr;
342 }
343
344 GrColorType grColorType = SkColorTypeToGrColorType(colorType);
345 if (GrColorType::kUnknown == grColorType) {
346 return nullptr;
347 }
348
349 if (!threadSafeProxy->priv().caps()->areColorTypeAndFormatCompatible(grColorType,
350 backendFormat)) {
351 return nullptr;
352 }
353
354 auto proxy = SkImage_GaneshBase::MakePromiseImageLazyProxy(threadSafeProxy.get(),
355 dimensions,
356 backendFormat,
357 mipmapped,
358 textureFulfillProc,
359 std::move(releaseHelper));
360 if (!proxy) {
361 return nullptr;
362 }
363 skgpu::Swizzle swizzle =
364 threadSafeProxy->priv().caps()->getReadSwizzle(backendFormat, grColorType);
365 GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
366 sk_sp<GrImageContext> ctx(GrImageContextPriv::MakeForPromiseImage(std::move(threadSafeProxy)));
367 return sk_make_sp<SkImage_Ganesh>(std::move(ctx),
368 kNeedNewImageUniqueID,
369 std::move(view),
370 SkColorInfo(colorType, alphaType, std::move(colorSpace)));
371 }
372
CrossContextTextureFromPixmap(GrDirectContext * dContext,const SkPixmap & originalPixmap,bool buildMips,bool limitToMaxTextureSize)373 sk_sp<SkImage> CrossContextTextureFromPixmap(GrDirectContext* dContext,
374 const SkPixmap& originalPixmap,
375 bool buildMips,
376 bool limitToMaxTextureSize) {
377 // Some backends or drivers don't support (safely) moving resources between contexts
378 if (!dContext || !dContext->priv().caps()->crossContextTextureSupport()) {
379 return RasterFromPixmapCopy(originalPixmap);
380 }
381
382 // If non-power-of-two mipmapping isn't supported, ignore the client's request
383 if (!dContext->priv().caps()->mipmapSupport()) {
384 buildMips = false;
385 }
386
387 const SkPixmap* pixmap = &originalPixmap;
388 SkAutoPixmapStorage resized;
389 int maxTextureSize = dContext->priv().caps()->maxTextureSize();
390 int maxDim = std::max(originalPixmap.width(), originalPixmap.height());
391 if (limitToMaxTextureSize && maxDim > maxTextureSize) {
392 float scale = static_cast<float>(maxTextureSize) / maxDim;
393 int newWidth = std::min(static_cast<int>(originalPixmap.width() * scale), maxTextureSize);
394 int newHeight = std::min(static_cast<int>(originalPixmap.height() * scale), maxTextureSize);
395 SkImageInfo info = originalPixmap.info().makeWH(newWidth, newHeight);
396 SkSamplingOptions sampling(SkFilterMode::kLinear);
397 if (!resized.tryAlloc(info) || !originalPixmap.scalePixels(resized, sampling)) {
398 return nullptr;
399 }
400 pixmap = &resized;
401 }
402 // Turn the pixmap into a GrTextureProxy
403 SkBitmap bmp;
404 bmp.installPixels(*pixmap);
405 skgpu::Mipmapped mipmapped = buildMips ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
406 auto [view, ct] = GrMakeUncachedBitmapProxyView(dContext, bmp, mipmapped);
407 if (!view) {
408 return RasterFromPixmapCopy(*pixmap);
409 }
410
411 sk_sp<GrTexture> texture = sk_ref_sp(view.proxy()->peekTexture());
412
413 // Flush any writes or uploads
414 dContext->priv().flushSurface(view.proxy());
415 GrGpu* gpu = dContext->priv().getGpu();
416
417 std::unique_ptr<GrSemaphore> sema = gpu->prepareTextureForCrossContextUsage(texture.get());
418
419 SkColorType skCT = GrColorTypeToSkColorType(ct);
420 auto gen = GrBackendTextureImageGenerator::Make(std::move(texture),
421 view.origin(),
422 std::move(sema),
423 skCT,
424 pixmap->alphaType(),
425 pixmap->info().refColorSpace());
426 return DeferredFromTextureGenerator(std::move(gen));
427 }
428
TextureFromImage(GrDirectContext * dContext,const SkImage * img,skgpu::Mipmapped mipmapped,skgpu::Budgeted budgeted)429 sk_sp<SkImage> TextureFromImage(GrDirectContext* dContext,
430 const SkImage* img,
431 skgpu::Mipmapped mipmapped,
432 skgpu::Budgeted budgeted) {
433 if (!dContext || !img) {
434 return nullptr;
435 }
436 auto ib = as_IB(img);
437 if (!dContext->priv().caps()->mipmapSupport() || ib->dimensions().area() <= 1) {
438 mipmapped = skgpu::Mipmapped::kNo;
439 }
440
441 if (ib->isGaneshBacked()) {
442 if (!ib->context()->priv().matches(dContext)) {
443 return nullptr;
444 }
445
446 if (mipmapped == skgpu::Mipmapped::kNo || ib->hasMipmaps()) {
447 return sk_ref_sp(const_cast<SkImage_Base*>(ib));
448 }
449 }
450 GrImageTexGenPolicy policy = budgeted == skgpu::Budgeted::kYes
451 ? GrImageTexGenPolicy::kNew_Uncached_Budgeted
452 : GrImageTexGenPolicy::kNew_Uncached_Unbudgeted;
453 // TODO: Don't flatten YUVA images here. Add mips to the planes instead.
454 auto [view, ct] = skgpu::ganesh::AsView(dContext, ib, mipmapped, policy);
455 if (!view) {
456 return nullptr;
457 }
458 SkASSERT(view.asTextureProxy());
459 SkASSERT(mipmapped == skgpu::Mipmapped::kNo ||
460 view.asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes);
461 SkColorInfo colorInfo(GrColorTypeToSkColorType(ct), ib->alphaType(), ib->refColorSpace());
462 return sk_make_sp<SkImage_Ganesh>(
463 sk_ref_sp(dContext), ib->uniqueID(), std::move(view), std::move(colorInfo));
464 }
465
466 } // namespace SkImages
467