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_GaneshYUVA.h"
9
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorType.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkPixmap.h"
16 #include "include/core/SkSize.h"
17 #include "include/core/SkSurface.h"
18 #include "include/core/SkYUVAInfo.h"
19 #include "include/core/SkYUVAPixmaps.h"
20 #include "include/gpu/GpuTypes.h"
21 #include "include/gpu/GrBackendSurface.h" // IWYU pragma: keep
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/GrYUVABackendTextures.h"
27 #include "include/gpu/ganesh/SkImageGanesh.h"
28 #include "include/private/base/SkAssert.h"
29 #include "include/private/base/SkDebug.h"
30 #include "include/private/chromium/SkImageChromium.h"
31 #include "include/private/gpu/ganesh/GrImageContext.h"
32 #include "include/private/gpu/ganesh/GrTypesPriv.h"
33 #include "src/core/SkImageInfoPriv.h"
34 #include "src/core/SkSamplingPriv.h"
35 #include "src/gpu/RefCntedCallback.h"
36 #include "src/gpu/SkBackingFit.h"
37 #include "src/gpu/Swizzle.h"
38 #include "src/gpu/ganesh/GrCaps.h"
39 #include "src/gpu/ganesh/GrColorInfo.h"
40 #include "src/gpu/ganesh/GrColorSpaceXform.h"
41 #include "src/gpu/ganesh/GrDirectContextPriv.h"
42 #include "src/gpu/ganesh/GrFragmentProcessor.h"
43 #include "src/gpu/ganesh/GrImageContextPriv.h"
44 #include "src/gpu/ganesh/GrImageInfo.h"
45 #include "src/gpu/ganesh/GrProxyProvider.h"
46 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
47 #include "src/gpu/ganesh/GrSamplerState.h"
48 #include "src/gpu/ganesh/GrSurfaceProxy.h"
49 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
50 #include "src/gpu/ganesh/GrTextureProxy.h"
51 #include "src/gpu/ganesh/SkGr.h"
52 #include "src/gpu/ganesh/SurfaceFillContext.h"
53 #include "src/gpu/ganesh/effects/GrBicubicEffect.h"
54 #include "src/gpu/ganesh/effects/GrYUVtoRGBEffect.h"
55 #include "src/image/SkImage_Base.h"
56
57 #include <algorithm>
58 #include <utility>
59
60 enum class SkTileMode;
61 struct SkRect;
62
63 static constexpr auto kAssumedColorType = kRGBA_8888_SkColorType;
64
SkImage_GaneshYUVA(sk_sp<GrImageContext> context,uint32_t uniqueID,GrYUVATextureProxies proxies,sk_sp<SkColorSpace> imageColorSpace)65 SkImage_GaneshYUVA::SkImage_GaneshYUVA(sk_sp<GrImageContext> context,
66 uint32_t uniqueID,
67 GrYUVATextureProxies proxies,
68 sk_sp<SkColorSpace> imageColorSpace)
69 : INHERITED(std::move(context),
70 SkImageInfo::Make(proxies.yuvaInfo().dimensions(),
71 kAssumedColorType,
72 // If an alpha channel is present we always use kPremul. This
73 // is because, although the planar data is always un-premul,
74 // the final interleaved RGBA sample produced in the shader
75 // is premul (and similar if flattened via asView).
76 proxies.yuvaInfo().hasAlpha() ? kPremul_SkAlphaType
77 : kOpaque_SkAlphaType,
78 std::move(imageColorSpace)),
79 uniqueID)
80 , fYUVAProxies(std::move(proxies)) {
81 // The caller should have checked this, just verifying.
82 SkASSERT(fYUVAProxies.isValid());
83 }
84
85 // For onMakeColorTypeAndColorSpace() / onReinterpretColorSpace()
SkImage_GaneshYUVA(sk_sp<GrImageContext> context,const SkImage_GaneshYUVA * image,sk_sp<SkColorSpace> targetCS,ColorSpaceMode csMode)86 SkImage_GaneshYUVA::SkImage_GaneshYUVA(sk_sp<GrImageContext> context,
87 const SkImage_GaneshYUVA* image,
88 sk_sp<SkColorSpace> targetCS,
89 ColorSpaceMode csMode)
90 : INHERITED(std::move(context),
91 image->imageInfo().makeColorSpace(std::move(targetCS)),
92 kNeedNewImageUniqueID)
93 , fYUVAProxies(image->fYUVAProxies)
94 // If we're *reinterpreting* in a new color space, leave fFromColorSpace null.
95 // If we're *converting* to a new color space, it must be non-null, so turn null into sRGB.
96 , fFromColorSpace(csMode == ColorSpaceMode::kReinterpret
97 ? nullptr
98 : (image->colorSpace() ? image->refColorSpace()
99 : SkColorSpace::MakeSRGB())) {}
100
setupMipmapsForPlanes(GrRecordingContext * context) const101 bool SkImage_GaneshYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
102 if (!context || !fContext->priv().matches(context)) {
103 return false;
104 }
105 if (!context->priv().caps()->mipmapSupport()) {
106 // We succeed in this case by doing nothing.
107 return true;
108 }
109 int n = fYUVAProxies.yuvaInfo().numPlanes();
110 sk_sp<GrSurfaceProxy> newProxies[4];
111 for (int i = 0; i < n; ++i) {
112 auto* t = fYUVAProxies.proxy(i)->asTextureProxy();
113 if (t->mipmapped() == skgpu::Mipmapped::kNo && (t->width() > 1 || t->height() > 1)) {
114 auto newView = GrCopyBaseMipMapToView(context, fYUVAProxies.makeView(i));
115 if (!newView) {
116 return false;
117 }
118 SkASSERT(newView.swizzle() == fYUVAProxies.makeView(i).swizzle());
119 newProxies[i] = newView.detachProxy();
120 } else {
121 newProxies[i] = fYUVAProxies.refProxy(i);
122 }
123 }
124 fYUVAProxies =
125 GrYUVATextureProxies(fYUVAProxies.yuvaInfo(), newProxies, fYUVAProxies.textureOrigin());
126 SkASSERT(fYUVAProxies.isValid());
127 return true;
128 }
129
130 //////////////////////////////////////////////////////////////////////////////////////////////////
131
flush(GrDirectContext * dContext,const GrFlushInfo & info) const132 GrSemaphoresSubmitted SkImage_GaneshYUVA::flush(GrDirectContext* dContext,
133 const GrFlushInfo& info) const {
134 if (!fContext->priv().matches(dContext) || dContext->abandoned()) {
135 if (info.fSubmittedProc) {
136 info.fSubmittedProc(info.fSubmittedContext, false);
137 }
138 if (info.fFinishedProc) {
139 info.fFinishedProc(info.fFinishedContext);
140 }
141 return GrSemaphoresSubmitted::kNo;
142 }
143
144 GrSurfaceProxy* proxies[SkYUVAInfo::kMaxPlanes] = {};
145 size_t numProxies = fYUVAProxies.numPlanes();
146 for (size_t i = 0; i < numProxies; ++i) {
147 proxies[i] = fYUVAProxies.proxy(i);
148 }
149 return dContext->priv().flushSurfaces(
150 {proxies, numProxies}, SkSurfaces::BackendSurfaceAccess::kNoAccess, info);
151 }
152
onHasMipmaps() const153 bool SkImage_GaneshYUVA::onHasMipmaps() const {
154 return fYUVAProxies.mipmapped() == skgpu::Mipmapped::kYes;
155 }
156
onIsProtected() const157 bool SkImage_GaneshYUVA::onIsProtected() const {
158 skgpu::Protected isProtected = fYUVAProxies.proxy(0)->isProtected();
159
160 #if defined(SK_DEBUG)
161 for (int i = 1; i < fYUVAProxies.numPlanes(); ++i) {
162 SkASSERT(isProtected == fYUVAProxies.proxy(i)->isProtected());
163 }
164 #endif
165
166 return isProtected == skgpu::Protected::kYes;
167 }
168
169
textureSize() const170 size_t SkImage_GaneshYUVA::textureSize() const {
171 size_t size = 0;
172 for (int i = 0; i < fYUVAProxies.numPlanes(); ++i) {
173 size += fYUVAProxies.proxy(i)->gpuMemorySize();
174 }
175 return size;
176 }
177
onMakeColorTypeAndColorSpace(SkColorType,sk_sp<SkColorSpace> targetCS,GrDirectContext * direct) const178 sk_sp<SkImage> SkImage_GaneshYUVA::onMakeColorTypeAndColorSpace(SkColorType,
179 sk_sp<SkColorSpace> targetCS,
180 GrDirectContext* direct) const {
181 // We explicitly ignore color type changes, for now.
182
183 // we may need a mutex here but for now we expect usage to be in a single thread
184 if (fOnMakeColorSpaceTarget &&
185 SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) {
186 return fOnMakeColorSpaceResult;
187 }
188 sk_sp<SkImage> result = sk_sp<SkImage>(
189 new SkImage_GaneshYUVA(sk_ref_sp(direct), this, targetCS, ColorSpaceMode::kConvert));
190 if (result) {
191 fOnMakeColorSpaceTarget = targetCS;
192 fOnMakeColorSpaceResult = result;
193 }
194 return result;
195 }
196
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const197 sk_sp<SkImage> SkImage_GaneshYUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
198 return sk_sp<SkImage>(
199 new SkImage_GaneshYUVA(fContext, this, std::move(newCS), ColorSpaceMode::kReinterpret));
200 }
201
asView(GrRecordingContext * rContext,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy) const202 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_GaneshYUVA::asView(GrRecordingContext* rContext,
203 skgpu::Mipmapped mipmapped,
204 GrImageTexGenPolicy) const {
205 if (!fContext->priv().matches(rContext)) {
206 return {};
207 }
208 auto sfc = rContext->priv().makeSFC(this->imageInfo(),
209 "Image_GpuYUVA_ReinterpretColorSpace",
210 SkBackingFit::kExact,
211 /*sample count*/ 1,
212 mipmapped,
213 GrProtected::kNo,
214 fYUVAProxies.textureOrigin(),
215 skgpu::Budgeted::kYes);
216 if (!sfc) {
217 return {};
218 }
219
220 const GrCaps& caps = *rContext->priv().caps();
221 auto fp = GrYUVtoRGBEffect::Make(fYUVAProxies, GrSamplerState::Filter::kNearest, caps);
222 if (fFromColorSpace) {
223 fp = GrColorSpaceXformEffect::Make(std::move(fp),
224 fFromColorSpace.get(),
225 this->alphaType(),
226 this->colorSpace(),
227 this->alphaType());
228 }
229 sfc->fillWithFP(std::move(fp));
230
231 return {sfc->readSurfaceView(), sfc->colorInfo().colorType()};
232 }
233
asFragmentProcessor(GrRecordingContext * context,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain) const234 std::unique_ptr<GrFragmentProcessor> SkImage_GaneshYUVA::asFragmentProcessor(
235 GrRecordingContext* context,
236 SkSamplingOptions sampling,
237 const SkTileMode tileModes[2],
238 const SkMatrix& m,
239 const SkRect* subset,
240 const SkRect* domain) const {
241 if (!fContext->priv().matches(context)) {
242 return {};
243 }
244 // At least for now we do not attempt aniso filtering on YUVA images.
245 if (sampling.isAniso()) {
246 sampling =
247 SkSamplingPriv::AnisoFallback(fYUVAProxies.mipmapped() == skgpu::Mipmapped::kYes);
248 }
249
250 auto wmx = SkTileModeToWrapMode(tileModes[0]);
251 auto wmy = SkTileModeToWrapMode(tileModes[1]);
252 GrSamplerState sampler(wmx, wmy, sampling.filter, sampling.mipmap);
253 if (sampler.mipmapped() == skgpu::Mipmapped::kYes && !this->setupMipmapsForPlanes(context)) {
254 sampler = GrSamplerState(sampler.wrapModeX(),
255 sampler.wrapModeY(),
256 sampler.filter(),
257 GrSamplerState::MipmapMode::kNone);
258 }
259
260 const auto& yuvM = sampling.useCubic ? SkMatrix::I() : m;
261 auto fp = GrYUVtoRGBEffect::Make(
262 fYUVAProxies, sampler, *context->priv().caps(), yuvM, subset, domain);
263 if (sampling.useCubic) {
264 fp = GrBicubicEffect::Make(std::move(fp),
265 this->alphaType(),
266 m,
267 sampling.cubic,
268 GrBicubicEffect::Direction::kXY);
269 }
270 if (fFromColorSpace) {
271 fp = GrColorSpaceXformEffect::Make(std::move(fp),
272 fFromColorSpace.get(),
273 this->alphaType(),
274 this->colorSpace(),
275 this->alphaType());
276 }
277 return fp;
278 }
279
280 //////////////////////////////////////////////////////////////////////////////////////////////////
281 namespace SkImages {
TextureFromYUVATextures(GrRecordingContext * context,const GrYUVABackendTextures & yuvaTextures)282 sk_sp<SkImage> TextureFromYUVATextures(GrRecordingContext* context,
283 const GrYUVABackendTextures& yuvaTextures) {
284 return TextureFromYUVATextures(context, yuvaTextures, nullptr, nullptr, nullptr);
285 }
286
TextureFromYUVATextures(GrRecordingContext * context,const GrYUVABackendTextures & yuvaTextures,sk_sp<SkColorSpace> imageColorSpace,TextureReleaseProc textureReleaseProc,ReleaseContext releaseContext)287 sk_sp<SkImage> TextureFromYUVATextures(GrRecordingContext* context,
288 const GrYUVABackendTextures& yuvaTextures,
289 sk_sp<SkColorSpace> imageColorSpace,
290 TextureReleaseProc textureReleaseProc,
291 ReleaseContext releaseContext) {
292 auto releaseHelper = skgpu::RefCntedCallback::Make(textureReleaseProc, releaseContext);
293
294 GrProxyProvider* proxyProvider = context->priv().proxyProvider();
295 int numPlanes = yuvaTextures.yuvaInfo().numPlanes();
296 sk_sp<GrSurfaceProxy> proxies[SkYUVAInfo::kMaxPlanes];
297 for (int plane = 0; plane < numPlanes; ++plane) {
298 proxies[plane] = proxyProvider->wrapBackendTexture(yuvaTextures.texture(plane),
299 kBorrow_GrWrapOwnership,
300 GrWrapCacheable::kNo,
301 kRead_GrIOType,
302 releaseHelper);
303 if (!proxies[plane]) {
304 return {};
305 }
306 }
307 GrYUVATextureProxies yuvaProxies(
308 yuvaTextures.yuvaInfo(), proxies, yuvaTextures.textureOrigin());
309
310 if (!yuvaProxies.isValid()) {
311 return nullptr;
312 }
313
314 return sk_make_sp<SkImage_GaneshYUVA>(
315 sk_ref_sp(context), kNeedNewImageUniqueID, yuvaProxies, imageColorSpace);
316 }
317
TextureFromYUVAPixmaps(GrRecordingContext * context,const SkYUVAPixmaps & pixmaps,skgpu::Mipmapped buildMips,bool limitToMaxTextureSize)318 sk_sp<SkImage> TextureFromYUVAPixmaps(GrRecordingContext* context,
319 const SkYUVAPixmaps& pixmaps,
320 skgpu::Mipmapped buildMips,
321 bool limitToMaxTextureSize) {
322 return TextureFromYUVAPixmaps(context, pixmaps, buildMips, limitToMaxTextureSize, nullptr);
323 }
324
TextureFromYUVAPixmaps(GrRecordingContext * context,const SkYUVAPixmaps & pixmaps,skgpu::Mipmapped buildMips,bool limitToMaxTextureSize,sk_sp<SkColorSpace> imageColorSpace)325 sk_sp<SkImage> TextureFromYUVAPixmaps(GrRecordingContext* context,
326 const SkYUVAPixmaps& pixmaps,
327 skgpu::Mipmapped buildMips,
328 bool limitToMaxTextureSize,
329 sk_sp<SkColorSpace> imageColorSpace) {
330 if (!context) {
331 return nullptr;
332 }
333
334 if (!pixmaps.isValid()) {
335 return nullptr;
336 }
337
338 if (!context->priv().caps()->mipmapSupport()) {
339 buildMips = skgpu::Mipmapped::kNo;
340 }
341
342 // Resize the pixmaps if necessary.
343 int numPlanes = pixmaps.numPlanes();
344 int maxTextureSize = context->priv().caps()->maxTextureSize();
345 int maxDim = std::max(pixmaps.yuvaInfo().width(), pixmaps.yuvaInfo().height());
346
347 SkYUVAPixmaps tempPixmaps;
348 const SkYUVAPixmaps* pixmapsToUpload = &pixmaps;
349 // We assume no plane is larger than the image size (and at least one plane is as big).
350 if (maxDim > maxTextureSize) {
351 if (!limitToMaxTextureSize) {
352 return nullptr;
353 }
354 float scale = static_cast<float>(maxTextureSize) / maxDim;
355 SkISize newDimensions = {
356 std::min(static_cast<int>(pixmaps.yuvaInfo().width() * scale), maxTextureSize),
357 std::min(static_cast<int>(pixmaps.yuvaInfo().height() * scale), maxTextureSize)};
358 SkYUVAInfo newInfo = pixmaps.yuvaInfo().makeDimensions(newDimensions);
359 SkYUVAPixmapInfo newPixmapInfo(newInfo, pixmaps.dataType(), /*row bytes*/ nullptr);
360 tempPixmaps = SkYUVAPixmaps::Allocate(newPixmapInfo);
361 SkSamplingOptions sampling(SkFilterMode::kLinear);
362 if (!tempPixmaps.isValid()) {
363 return nullptr;
364 }
365 for (int i = 0; i < numPlanes; ++i) {
366 if (!pixmaps.plane(i).scalePixels(tempPixmaps.plane(i), sampling)) {
367 return nullptr;
368 }
369 }
370 pixmapsToUpload = &tempPixmaps;
371 }
372
373 // Convert to texture proxies.
374 GrSurfaceProxyView views[SkYUVAInfo::kMaxPlanes];
375 GrColorType pixmapColorTypes[SkYUVAInfo::kMaxPlanes];
376 for (int i = 0; i < numPlanes; ++i) {
377 // Turn the pixmap into a GrTextureProxy
378 SkBitmap bmp;
379 bmp.installPixels(pixmapsToUpload->plane(i));
380 std::tie(views[i], std::ignore) = GrMakeUncachedBitmapProxyView(context, bmp, buildMips);
381 if (!views[i]) {
382 return nullptr;
383 }
384 pixmapColorTypes[i] = SkColorTypeToGrColorType(bmp.colorType());
385 }
386
387 GrYUVATextureProxies yuvaProxies(pixmapsToUpload->yuvaInfo(), views, pixmapColorTypes);
388 SkASSERT(yuvaProxies.isValid());
389 return sk_make_sp<SkImage_GaneshYUVA>(sk_ref_sp(context),
390 kNeedNewImageUniqueID,
391 std::move(yuvaProxies),
392 std::move(imageColorSpace));
393 }
394
PromiseTextureFromYUVA(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,const GrYUVABackendTextureInfo & backendTextureInfo,sk_sp<SkColorSpace> imageColorSpace,PromiseImageTextureFulfillProc textureFulfillProc,PromiseImageTextureReleaseProc textureReleaseProc,PromiseImageTextureContext textureContexts[])395 sk_sp<SkImage> PromiseTextureFromYUVA(sk_sp<GrContextThreadSafeProxy> threadSafeProxy,
396 const GrYUVABackendTextureInfo& backendTextureInfo,
397 sk_sp<SkColorSpace> imageColorSpace,
398 PromiseImageTextureFulfillProc textureFulfillProc,
399 PromiseImageTextureReleaseProc textureReleaseProc,
400 PromiseImageTextureContext textureContexts[]) {
401 if (!backendTextureInfo.isValid()) {
402 return nullptr;
403 }
404
405 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
406 int n = backendTextureInfo.yuvaInfo().planeDimensions(planeDimensions);
407
408 // Our contract is that we will always call the release proc even on failure.
409 // We use the helper to convey the context, so we need to ensure make doesn't fail.
410 textureReleaseProc = textureReleaseProc ? textureReleaseProc : [](void*) {};
411 sk_sp<skgpu::RefCntedCallback> releaseHelpers[4];
412 for (int i = 0; i < n; ++i) {
413 releaseHelpers[i] = skgpu::RefCntedCallback::Make(textureReleaseProc, textureContexts[i]);
414 }
415
416 if (!threadSafeProxy) {
417 return nullptr;
418 }
419
420 SkAlphaType at =
421 backendTextureInfo.yuvaInfo().hasAlpha() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
422 SkImageInfo info = SkImageInfo::Make(
423 backendTextureInfo.yuvaInfo().dimensions(), kAssumedColorType, at, imageColorSpace);
424 if (!SkImageInfoIsValid(info)) {
425 return nullptr;
426 }
427
428 // Make a lazy proxy for each plane
429 sk_sp<GrSurfaceProxy> proxies[4];
430 for (int i = 0; i < n; ++i) {
431 proxies[i] =
432 SkImage_GaneshBase::MakePromiseImageLazyProxy(threadSafeProxy.get(),
433 planeDimensions[i],
434 backendTextureInfo.planeFormat(i),
435 skgpu::Mipmapped::kNo,
436 textureFulfillProc,
437 std::move(releaseHelpers[i]));
438 if (!proxies[i]) {
439 return nullptr;
440 }
441 }
442 GrYUVATextureProxies yuvaTextureProxies(
443 backendTextureInfo.yuvaInfo(), proxies, backendTextureInfo.textureOrigin());
444 SkASSERT(yuvaTextureProxies.isValid());
445 sk_sp<GrImageContext> ctx(GrImageContextPriv::MakeForPromiseImage(std::move(threadSafeProxy)));
446 return sk_make_sp<SkImage_GaneshYUVA>(std::move(ctx),
447 kNeedNewImageUniqueID,
448 std::move(yuvaTextureProxies),
449 std::move(imageColorSpace));
450 }
451 } // namespace SkImages
452