• 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/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