• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/gpu/graphite/Image_YUVA_Graphite.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkSurface.h"
15 #include "include/gpu/GpuTypes.h"
16 #include "include/gpu/graphite/Image.h"
17 #include "include/gpu/graphite/Recorder.h"
18 #include "include/gpu/graphite/Surface.h"
19 #include "src/core/SkYUVAInfoLocation.h"
20 #include "src/gpu/graphite/Caps.h"
21 #include "src/gpu/graphite/Image_Graphite.h"
22 #include "src/gpu/graphite/Log.h"
23 #include "src/gpu/graphite/RecorderPriv.h"
24 #include "src/gpu/graphite/ResourceProvider.h"
25 #include "src/gpu/graphite/Texture.h"
26 #include "src/gpu/graphite/TextureInfoPriv.h"
27 #include "src/gpu/graphite/TextureProxy.h"
28 #include "src/gpu/graphite/TextureProxyView.h"
29 #include "src/gpu/graphite/TextureUtils.h"
30 #include "src/shaders/SkImageShader.h"
31 
32 
33 namespace skgpu::graphite {
34 
35 namespace {
36 
37 constexpr auto kAssumedColorType = kRGBA_8888_SkColorType;
38 
39 static constexpr int kY = static_cast<int>(SkYUVAInfo::kY);
40 static constexpr int kU = static_cast<int>(SkYUVAInfo::kU);
41 static constexpr int kV = static_cast<int>(SkYUVAInfo::kV);
42 static constexpr int kA = static_cast<int>(SkYUVAInfo::kA);
43 
yuva_alpha_type(const SkYUVAInfo & yuvaInfo)44 static SkAlphaType yuva_alpha_type(const SkYUVAInfo& yuvaInfo) {
45     // If an alpha channel is present we always use kPremul. This is because, although the planar
46     // data is always un-premul and the final interleaved RGBA sample produced in the shader is
47     // unpremul (and similar if flattened), the client is expecting premul.
48     return yuvaInfo.hasAlpha() ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
49 }
50 
51 } // anonymous
52 
Image_YUVA(const YUVAProxies & proxies,const SkYUVAInfo & yuvaInfo,sk_sp<SkColorSpace> imageColorSpace)53 Image_YUVA::Image_YUVA(const YUVAProxies& proxies,
54                        const SkYUVAInfo& yuvaInfo,
55                        sk_sp<SkColorSpace> imageColorSpace)
56         : Image_Base(SkImageInfo::Make(yuvaInfo.dimensions(),
57                                        kAssumedColorType,
58                                        yuva_alpha_type(yuvaInfo),
59                                        std::move(imageColorSpace)),
60                      kNeedNewImageUniqueID)
61         , fProxies(std::move(proxies))
62         , fYUVAInfo(yuvaInfo)
63         , fUVSubsampleFactors(SkYUVAInfo::SubsamplingFactors(yuvaInfo.subsampling())) {
64     // The caller should have checked this, just verifying.
65     SkASSERT(fYUVAInfo.isValid());
66     for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
67         if (!fProxies[i]) {
68             SkASSERT(i == kA);
69             continue;
70         }
71         if (fProxies[i].proxy()->mipmapped() == Mipmapped::kNo) {
72             fMipmapped = Mipmapped::kNo;
73         }
74         if (fProxies[i].proxy()->isProtected() == Protected::kYes) {
75             fProtected = Protected::kYes;
76         }
77     }
78 }
79 
80 Image_YUVA::~Image_YUVA() = default;
81 
Make(const Caps * caps,const SkYUVAInfo & yuvaInfo,SkSpan<TextureProxyView> planes,sk_sp<SkColorSpace> imageColorSpace)82 sk_sp<Image_YUVA> Image_YUVA::Make(const Caps* caps,
83                                    const SkYUVAInfo& yuvaInfo,
84                                    SkSpan<TextureProxyView> planes,
85                                    sk_sp<SkColorSpace> imageColorSpace) {
86     if (!yuvaInfo.isValid()) {
87         return nullptr;
88     }
89     SkImageInfo info = SkImageInfo::Make(
90             yuvaInfo.dimensions(), kAssumedColorType, yuva_alpha_type(yuvaInfo), imageColorSpace);
91     if (!SkImageInfoIsValid(info)) {
92         return nullptr;
93     }
94 
95     // Invoke the PlaneProxyFactoryFn for each plane and validate it against the plane config
96     const int numPlanes = yuvaInfo.numPlanes();
97     SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
98     if (numPlanes != yuvaInfo.planeDimensions(planeDimensions)) {
99         return nullptr;
100     }
101     uint32_t pixmapChannelmasks[SkYUVAInfo::kMaxPlanes];
102     for (int i = 0; i < numPlanes; ++i) {
103         if (!planes[i] || !caps->isTexturable(planes[i].proxy()->textureInfo())) {
104             return nullptr;
105         }
106         if (planes[i].dimensions() != planeDimensions[i]) {
107             return nullptr;
108         }
109         pixmapChannelmasks[i] = TextureInfoPriv::ChannelMask(planes[i].proxy()->textureInfo());
110     }
111 
112     // Re-arrange the proxies from planes to channels
113     SkYUVAInfo::YUVALocations locations = yuvaInfo.toYUVALocations(pixmapChannelmasks);
114     int expectedPlanes;
115     if (!SkYUVAInfo::YUVALocation::AreValidLocations(locations, &expectedPlanes) ||
116         expectedPlanes != numPlanes) {
117         return nullptr;
118     }
119     // Y channel should match the YUVAInfo dimensions
120     if (planes[locations[kY].fPlane].dimensions() != yuvaInfo.dimensions()) {
121         return nullptr;
122     }
123     // UV channels should have planes with the same dimensions and subsampling factor.
124     if (planes[locations[kU].fPlane].dimensions() != planes[locations[kV].fPlane].dimensions()) {
125         return nullptr;
126     }
127     // If A channel is present, it should match the Y channel
128     if (locations[kA].fPlane >= 0 &&
129         planes[locations[kA].fPlane].dimensions() != yuvaInfo.dimensions()) {
130         return nullptr;
131     }
132 
133     if (yuvaInfo.planeSubsamplingFactors(locations[kU].fPlane) !=
134         yuvaInfo.planeSubsamplingFactors(locations[kV].fPlane)) {
135         return nullptr;
136     }
137 
138     // Re-arrange into YUVA channel order and apply the location to the swizzle
139     YUVAProxies channelProxies;
140     for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
141         auto [plane, channel] = locations[i];
142         if (plane >= 0) {
143             // Compose the YUVA location with the data swizzle. replaceSwizzle() is used since
144             // selectChannelInR() effectively does the composition (vs. Swizzle::Concat).
145             Swizzle channelSwizzle = planes[plane].swizzle().selectChannelInR((int) channel);
146             channelProxies[i] = planes[plane].replaceSwizzle(channelSwizzle);
147         } else if (i == kA) {
148             // The alpha channel is allowed to be not provided, set it to an empty view
149             channelProxies[i] = {};
150         } else {
151             SKGPU_LOG_W("YUVA channel %d does not have a valid location", i);
152             return nullptr;
153         }
154     }
155 
156     return sk_sp<Image_YUVA>(new Image_YUVA(std::move(channelProxies),
157                                             yuvaInfo,
158                                             std::move(imageColorSpace)));
159 }
160 
WrapImages(const Caps * caps,const SkYUVAInfo & yuvaInfo,SkSpan<const sk_sp<SkImage>> images,sk_sp<SkColorSpace> imageColorSpace)161 sk_sp<Image_YUVA> Image_YUVA::WrapImages(const Caps* caps,
162                                          const SkYUVAInfo& yuvaInfo,
163                                          SkSpan<const sk_sp<SkImage>> images,
164                                          sk_sp<SkColorSpace> imageColorSpace) {
165     if (SkTo<int>(images.size()) < yuvaInfo.numPlanes()) {
166         return nullptr;
167     }
168 
169     TextureProxyView planes[SkYUVAInfo::kMaxPlanes];
170     for (int i = 0; i < yuvaInfo.numPlanes(); ++i) {
171         planes[i] = AsView(images[i]);
172         if (!planes[i]) {
173             // A null image, or not graphite-backed, or not backed by a single texture.
174             return nullptr;
175         }
176         // The YUVA shader expects to sample from the red channel for single-channel textures, so
177         // reset the swizzle for alpha-only textures to compensate for that
178         if (images[i]->isAlphaOnly()) {
179             planes[i] = planes[i].makeSwizzle(Swizzle("aaaa"));
180         }
181     }
182 
183     sk_sp<Image_YUVA> image = Make(caps, yuvaInfo, SkSpan(planes), std::move(imageColorSpace));
184     if (image) {
185         // Unlike the other factories, this YUVA image shares the texture proxies with each plane
186         // Image, so if those are linked to Devices, it must inherit those same links.
187         for (int plane = 0; plane < yuvaInfo.numPlanes(); ++plane) {
188             SkASSERT(as_IB(images[plane])->isGraphiteBacked());
189             image->linkDevices(static_cast<Image_Base*>(images[plane].get()));
190         }
191     }
192     return image;
193 }
194 
textureSize() const195 size_t Image_YUVA::textureSize() const {
196     // We could look at the plane config and plane count to determine how many different textures
197     // to expect, but it's theoretically possible for an Image_YUVA to be constructed where the
198     // same TextureProxy is aliased to both the U and the V planes (and similarly for the Y and A)
199     // even when the plane config specifies that those channels are not packed into the same texture
200     //
201     // Given that it's simpler to just sum the total gpu memory of non-duplicate textures.
202     size_t size = 0;
203     for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
204         if (!fProxies[i]) {
205             continue; // Null channels (A) have no size.
206         }
207         bool repeat = false;
208         for (int j = i - 1; j >= 0; --j) {
209             if (fProxies[i].proxy() == fProxies[j].proxy()) {
210                 repeat = true;
211                 break;
212             }
213         }
214         if (!repeat) {
215             if (fProxies[i].proxy()->isInstantiated()) {
216                 size += fProxies[i].proxy()->texture()->gpuMemorySize();
217             } else {
218                 size += fProxies[i].proxy()->uninstantiatedGpuMemorySize();
219             }
220         }
221     }
222 
223     return size;
224 }
225 
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const226 sk_sp<SkImage> Image_YUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
227     sk_sp<Image_YUVA> view{new Image_YUVA(fProxies,
228                                           fYUVAInfo,
229                                           std::move(newCS))};
230     // The new Image object shares the same texture planes, so it should also share linked Devices
231     view->linkDevices(this);
232     return view;
233 }
234 
235 }  // namespace skgpu::graphite
236