Flutter Engine
The Flutter Engine
Image_YUVA_Graphite.cpp
Go to the documentation of this file.
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
9
30
31
32namespace skgpu::graphite {
33
34namespace {
35
37
38static constexpr int kY = static_cast<int>(SkYUVAInfo::kY);
39static constexpr int kU = static_cast<int>(SkYUVAInfo::kU);
40static constexpr int kV = static_cast<int>(SkYUVAInfo::kV);
41static constexpr int kA = static_cast<int>(SkYUVAInfo::kA);
42
43static SkAlphaType yuva_alpha_type(const SkYUVAInfo& yuvaInfo) {
44 // If an alpha channel is present we always use kPremul. This is because, although the planar
45 // data is always un-premul, the final interleaved RGBA sample produced in the shader is premul
46 // (and similar if flattened).
48}
49
50} // anonymous
51
52Image_YUVA::Image_YUVA(const YUVAProxies& proxies,
53 const SkYUVAInfo& yuvaInfo,
54 sk_sp<SkColorSpace> imageColorSpace)
55 : Image_Base(SkImageInfo::Make(yuvaInfo.dimensions(),
57 yuva_alpha_type(yuvaInfo),
58 std::move(imageColorSpace)),
60 , fProxies(std::move(proxies))
61 , fYUVAInfo(yuvaInfo)
62 , fUVSubsampleFactors(SkYUVAInfo::SubsamplingFactors(yuvaInfo.subsampling())) {
63 // The caller should have checked this, just verifying.
64 SkASSERT(fYUVAInfo.isValid());
65 for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
66 if (!fProxies[i]) {
67 SkASSERT(i == kA);
68 continue;
69 }
70 if (fProxies[i].proxy()->mipmapped() == Mipmapped::kNo) {
71 fMipmapped = Mipmapped::kNo;
72 }
73 if (fProxies[i].proxy()->isProtected()) {
74 fProtected = Protected::kYes;
75 }
76 }
77}
78
79Image_YUVA::~Image_YUVA() = default;
80
82 const SkYUVAInfo& yuvaInfo,
84 sk_sp<SkColorSpace> imageColorSpace) {
85 if (!yuvaInfo.isValid()) {
86 return nullptr;
87 }
89 yuvaInfo.dimensions(), kAssumedColorType, yuva_alpha_type(yuvaInfo), imageColorSpace);
91 return nullptr;
92 }
93
94 // Invoke the PlaneProxyFactoryFn for each plane and validate it against the plane config
95 const int numPlanes = yuvaInfo.numPlanes();
96 SkISize planeDimensions[SkYUVAInfo::kMaxPlanes];
97 if (numPlanes != yuvaInfo.planeDimensions(planeDimensions)) {
98 return nullptr;
99 }
100 uint32_t pixmapChannelmasks[SkYUVAInfo::kMaxPlanes];
101 for (int i = 0; i < numPlanes; ++i) {
102 if (!planes[i] || !caps->isTexturable(planes[i].proxy()->textureInfo())) {
103 return nullptr;
104 }
105 if (planes[i].dimensions() != planeDimensions[i]) {
106 return nullptr;
107 }
108 pixmapChannelmasks[i] = caps->channelMask(planes[i].proxy()->textureInfo());
109 }
110
111 // Re-arrange the proxies from planes to channels
112 SkYUVAInfo::YUVALocations locations = yuvaInfo.toYUVALocations(pixmapChannelmasks);
113 int expectedPlanes;
114 if (!SkYUVAInfo::YUVALocation::AreValidLocations(locations, &expectedPlanes) ||
115 expectedPlanes != numPlanes) {
116 return nullptr;
117 }
118 // Y channel should match the YUVAInfo dimensions
119 if (planes[locations[kY].fPlane].dimensions() != yuvaInfo.dimensions()) {
120 return nullptr;
121 }
122 // UV channels should have planes with the same dimensions and subsampling factor.
123 if (planes[locations[kU].fPlane].dimensions() != planes[locations[kV].fPlane].dimensions()) {
124 return nullptr;
125 }
126 // If A channel is present, it should match the Y channel
127 if (locations[kA].fPlane >= 0 &&
128 planes[locations[kA].fPlane].dimensions() != yuvaInfo.dimensions()) {
129 return nullptr;
130 }
131
132 if (yuvaInfo.planeSubsamplingFactors(locations[kU].fPlane) !=
133 yuvaInfo.planeSubsamplingFactors(locations[kV].fPlane)) {
134 return nullptr;
135 }
136
137 // Re-arrange into YUVA channel order and apply the location to the swizzle
138 YUVAProxies channelProxies;
139 for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
140 auto [plane, channel] = locations[i];
141 if (plane >= 0) {
142 // Compose the YUVA location with the data swizzle. replaceSwizzle() is used since
143 // selectChannelInR() effectively does the composition (vs. Swizzle::Concat).
144 Swizzle channelSwizzle = planes[plane].swizzle().selectChannelInR((int) channel);
145 channelProxies[i] = planes[plane].replaceSwizzle(channelSwizzle);
146 } else if (i == kA) {
147 // The alpha channel is allowed to be not provided, set it to an empty view
148 channelProxies[i] = {};
149 } else {
150 SKGPU_LOG_W("YUVA channel %d does not have a valid location", i);
151 return nullptr;
152 }
153 }
154
155 return sk_sp<Image_YUVA>(new Image_YUVA(std::move(channelProxies),
156 yuvaInfo,
157 std::move(imageColorSpace)));
158}
159
160sk_sp<Image_YUVA> Image_YUVA::WrapImages(const Caps* caps,
161 const SkYUVAInfo& yuvaInfo,
163 sk_sp<SkColorSpace> imageColorSpace) {
164 if (SkTo<int>(images.size()) < yuvaInfo.numPlanes()) {
165 return nullptr;
166 }
167
169 for (int i = 0; i < yuvaInfo.numPlanes(); ++i) {
170 planes[i] = AsView(images[i]);
171 if (!planes[i]) {
172 // A null image, or not graphite-backed, or not backed by a single texture.
173 return nullptr;
174 }
175 // The YUVA shader expects to sample from the red channel for single-channel textures, so
176 // reset the swizzle for alpha-only textures to compensate for that
177 if (images[i]->isAlphaOnly()) {
178 planes[i] = planes[i].makeSwizzle(Swizzle("aaaa"));
179 }
180 }
181
182 sk_sp<Image_YUVA> image = Make(caps, yuvaInfo, SkSpan(planes), std::move(imageColorSpace));
183 if (image) {
184 // Unlike the other factories, this YUVA image shares the texture proxies with each plane
185 // Image, so if those are linked to Devices, it must inherit those same links.
186 for (int plane = 0; plane < yuvaInfo.numPlanes(); ++plane) {
187 SkASSERT(as_IB(images[plane])->isGraphiteBacked());
188 image->linkDevices(static_cast<Image_Base*>(images[plane].get()));
189 }
190 }
191 return image;
192}
193
194size_t Image_YUVA::textureSize() const {
195 // We could look at the plane config and plane count to determine how many different textures
196 // to expect, but it's theoretically possible for an Image_YUVA to be constructed where the
197 // same TextureProxy is aliased to both the U and the V planes (and similarly for the Y and A)
198 // even when the plane config specifies that those channels are not packed into the same texture
199 //
200 // Given that it's simpler to just sum the total gpu memory of non-duplicate textures.
201 size_t size = 0;
202 for (int i = 0; i < SkYUVAInfo::kYUVAChannelCount; ++i) {
203 if (!fProxies[i]) {
204 continue; // Null channels (A) have no size.
205 }
206 bool repeat = false;
207 for (int j = 0; j < i - 1; ++j) {
208 if (fProxies[i].proxy() == fProxies[j].proxy()) {
209 repeat = true;
210 break;
211 }
212 }
213 if (!repeat) {
214 if (fProxies[i].proxy()->isInstantiated()) {
215 size += fProxies[i].proxy()->texture()->gpuMemorySize();
216 } else {
217 size += fProxies[i].proxy()->uninstantiatedGpuMemorySize();
218 }
219 }
220 }
221
222 return size;
223}
224
225sk_sp<SkImage> Image_YUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
226 sk_sp<Image_YUVA> view{new Image_YUVA(fProxies,
227 fYUVAInfo,
228 std::move(newCS))};
229 // The new Image object shares the same texture planes, so it should also share linked Devices
230 view->linkDevices(this);
231 return view;
232}
233
234} // namespace skgpu::graphite
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SKGPU_LOG_W(fmt,...)
Definition: Log.h:40
SkAlphaType
Definition: SkAlphaType.h:26
@ kOpaque_SkAlphaType
pixel is opaque
Definition: SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SkASSERT(cond)
Definition: SkAssert.h:116
static unsigned repeat(SkFixed fx, int max)
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
static bool SkImageInfoIsValid(const SkImageInfo &info)
static SkImage_Base * as_IB(SkImage *image)
Definition: SkImage_Base.h:201
@ kNeedNewImageUniqueID
Definition: SkImage_Base.h:33
static constexpr auto kAssumedColorType
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
int numPlanes() const
Definition: SkYUVAInfo.h:204
static constexpr int kMaxPlanes
Definition: SkYUVAInfo.h:98
bool hasAlpha() const
Definition: SkYUVAInfo.h:185
YUVALocations toYUVALocations(const uint32_t *channelFlags) const
Definition: SkYUVAInfo.cpp:358
std::tuple< int, int > planeSubsamplingFactors(int planeIdx) const
Definition: SkYUVAInfo.h:163
int planeDimensions(SkISize planeDimensions[kMaxPlanes]) const
Definition: SkYUVAInfo.h:192
std::array< YUVALocation, kYUVAChannelCount > YUVALocations
Definition: SkYUVAInfo.h:32
static constexpr int kYUVAChannelCount
Definition: SkYUVAInfo.h:29
bool isValid() const
Definition: SkYUVAInfo.h:233
SkISize dimensions() const
Definition: SkYUVAInfo.h:171
virtual uint32_t channelMask(const TextureInfo &) const =0
bool isTexturable(const TextureInfo &) const
Definition: Caps.cpp:66
void linkDevices(const Image_Base *)
TextureProxyView makeSwizzle(Swizzle swizzle) const &
std::array< MockImage, 3 > images
Definition: mock_vulkan.cc:41
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
sk_sp< const SkImage > image
Definition: SkRecords.h:269
const auto kA
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
const myers::Point & get(const myers::Segment &)
std::tuple< GrSurfaceProxyView, GrColorType > AsView(GrRecordingContext *rContext, const SkImage *img, skgpu::Mipmapped mipmapped, GrImageTexGenPolicy policy)
Definition: ref_ptr.h:256
Definition: SkSize.h:16
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static bool AreValidLocations(const SkYUVAInfo::YUVALocations &locations, int *numPlanes=nullptr)