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/SkImageInfo.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkSurface.h"
14 #include "include/core/SkYUVAInfo.h"
15 #include "include/gpu/GpuTypes.h"
16 #include "include/gpu/ganesh/GrBackendSurface.h" // IWYU pragma: keep
17 #include "include/gpu/ganesh/GrDirectContext.h"
18 #include "include/gpu/ganesh/GrRecordingContext.h"
19 #include "include/gpu/ganesh/GrTypes.h"
20 #include "include/private/base/SkAssert.h"
21 #include "include/private/base/SkDebug.h"
22 #include "include/private/gpu/ganesh/GrImageContext.h"
23 #include "src/core/SkSamplingPriv.h"
24 #include "src/gpu/SkBackingFit.h"
25 #include "src/gpu/Swizzle.h"
26 #include "src/gpu/ganesh/GrCaps.h"
27 #include "src/gpu/ganesh/GrColorInfo.h"
28 #include "src/gpu/ganesh/GrColorSpaceXform.h"
29 #include "src/gpu/ganesh/GrDirectContextPriv.h"
30 #include "src/gpu/ganesh/GrFragmentProcessor.h"
31 #include "src/gpu/ganesh/GrImageContextPriv.h"
32 #include "src/gpu/ganesh/GrImageInfo.h"
33 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
34 #include "src/gpu/ganesh/GrSamplerState.h"
35 #include "src/gpu/ganesh/GrSurfaceProxy.h"
36 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
37 #include "src/gpu/ganesh/GrTextureProxy.h"
38 #include "src/gpu/ganesh/SkGr.h"
39 #include "src/gpu/ganesh/SurfaceFillContext.h"
40 #include "src/gpu/ganesh/effects/GrBicubicEffect.h"
41 #include "src/gpu/ganesh/effects/GrYUVtoRGBEffect.h"
42 #include "src/image/SkImage_Base.h"
43
44 #include <utility>
45
46 enum class SkTileMode;
47 struct SkRect;
48
SkImage_GaneshYUVA(sk_sp<GrImageContext> context,uint32_t uniqueID,GrYUVATextureProxies proxies,sk_sp<SkColorSpace> imageColorSpace)49 SkImage_GaneshYUVA::SkImage_GaneshYUVA(sk_sp<GrImageContext> context,
50 uint32_t uniqueID,
51 GrYUVATextureProxies proxies,
52 sk_sp<SkColorSpace> imageColorSpace)
53 : INHERITED(std::move(context),
54 SkImageInfo::Make(proxies.yuvaInfo().dimensions(),
55 kAssumedColorType,
56 // If an alpha channel is present we always use kPremul. This
57 // is because, although the planar data is always un-premul,
58 // the final interleaved RGBA sample produced in the shader
59 // is premul (and similar if flattened via asView).
60 proxies.yuvaInfo().hasAlpha() ? kPremul_SkAlphaType
61 : kOpaque_SkAlphaType,
62 std::move(imageColorSpace)),
63 uniqueID)
64 , fYUVAProxies(std::move(proxies)) {
65 // The caller should have checked this, just verifying.
66 SkASSERT(fYUVAProxies.isValid());
67 }
68
69 // For onMakeColorTypeAndColorSpace() / onReinterpretColorSpace()
SkImage_GaneshYUVA(sk_sp<GrImageContext> context,const SkImage_GaneshYUVA * image,sk_sp<SkColorSpace> targetCS,ColorSpaceMode csMode)70 SkImage_GaneshYUVA::SkImage_GaneshYUVA(sk_sp<GrImageContext> context,
71 const SkImage_GaneshYUVA* image,
72 sk_sp<SkColorSpace> targetCS,
73 ColorSpaceMode csMode)
74 : INHERITED(std::move(context),
75 image->imageInfo().makeColorSpace(std::move(targetCS)),
76 kNeedNewImageUniqueID)
77 , fYUVAProxies(image->fYUVAProxies)
78 // If we're *reinterpreting* in a new color space, leave fFromColorSpace null.
79 // If we're *converting* to a new color space, it must be non-null, so turn null into sRGB.
80 , fFromColorSpace(csMode == ColorSpaceMode::kReinterpret
81 ? nullptr
82 : (image->colorSpace() ? image->refColorSpace()
83 : SkColorSpace::MakeSRGB())) {}
84
setupMipmapsForPlanes(GrRecordingContext * context) const85 bool SkImage_GaneshYUVA::setupMipmapsForPlanes(GrRecordingContext* context) const {
86 if (!context || !fContext->priv().matches(context)) {
87 return false;
88 }
89 if (!context->priv().caps()->mipmapSupport()) {
90 // We succeed in this case by doing nothing.
91 return true;
92 }
93 int n = fYUVAProxies.yuvaInfo().numPlanes();
94 sk_sp<GrSurfaceProxy> newProxies[4];
95 for (int i = 0; i < n; ++i) {
96 auto* t = fYUVAProxies.proxy(i)->asTextureProxy();
97 if (t->mipmapped() == skgpu::Mipmapped::kNo && (t->width() > 1 || t->height() > 1)) {
98 auto newView = GrCopyBaseMipMapToView(context, fYUVAProxies.makeView(i));
99 if (!newView) {
100 return false;
101 }
102 SkASSERT(newView.swizzle() == fYUVAProxies.makeView(i).swizzle());
103 newProxies[i] = newView.detachProxy();
104 } else {
105 newProxies[i] = fYUVAProxies.refProxy(i);
106 }
107 }
108 fYUVAProxies =
109 GrYUVATextureProxies(fYUVAProxies.yuvaInfo(), newProxies, fYUVAProxies.textureOrigin());
110 SkASSERT(fYUVAProxies.isValid());
111 return true;
112 }
113
114 //////////////////////////////////////////////////////////////////////////////////////////////////
115
flush(GrDirectContext * dContext,const GrFlushInfo & info) const116 GrSemaphoresSubmitted SkImage_GaneshYUVA::flush(GrDirectContext* dContext,
117 const GrFlushInfo& info) const {
118 if (!fContext->priv().matches(dContext) || dContext->abandoned()) {
119 if (info.fSubmittedProc) {
120 info.fSubmittedProc(info.fSubmittedContext, false);
121 }
122 if (info.fFinishedProc) {
123 info.fFinishedProc(info.fFinishedContext);
124 }
125 return GrSemaphoresSubmitted::kNo;
126 }
127
128 GrSurfaceProxy* proxies[SkYUVAInfo::kMaxPlanes] = {};
129 size_t numProxies = fYUVAProxies.numPlanes();
130 for (size_t i = 0; i < numProxies; ++i) {
131 proxies[i] = fYUVAProxies.proxy(i);
132 }
133 return dContext->priv().flushSurfaces(
134 {proxies, numProxies}, SkSurfaces::BackendSurfaceAccess::kNoAccess, info);
135 }
136
onHasMipmaps() const137 bool SkImage_GaneshYUVA::onHasMipmaps() const {
138 return fYUVAProxies.mipmapped() == skgpu::Mipmapped::kYes;
139 }
140
onIsProtected() const141 bool SkImage_GaneshYUVA::onIsProtected() const {
142 skgpu::Protected isProtected = fYUVAProxies.proxy(0)->isProtected();
143
144 #if defined(SK_DEBUG)
145 for (int i = 1; i < fYUVAProxies.numPlanes(); ++i) {
146 SkASSERT(isProtected == fYUVAProxies.proxy(i)->isProtected());
147 }
148 #endif
149
150 return isProtected == skgpu::Protected::kYes;
151 }
152
153
textureSize() const154 size_t SkImage_GaneshYUVA::textureSize() const {
155 size_t size = 0;
156 for (int i = 0; i < fYUVAProxies.numPlanes(); ++i) {
157 size += fYUVAProxies.proxy(i)->gpuMemorySize();
158 }
159 return size;
160 }
161
onMakeColorTypeAndColorSpace(SkColorType,sk_sp<SkColorSpace> targetCS,GrDirectContext * direct) const162 sk_sp<SkImage> SkImage_GaneshYUVA::onMakeColorTypeAndColorSpace(SkColorType,
163 sk_sp<SkColorSpace> targetCS,
164 GrDirectContext* direct) const {
165 // We explicitly ignore color type changes, for now.
166
167 // we may need a mutex here but for now we expect usage to be in a single thread
168 if (fOnMakeColorSpaceTarget &&
169 SkColorSpace::Equals(targetCS.get(), fOnMakeColorSpaceTarget.get())) {
170 return fOnMakeColorSpaceResult;
171 }
172 sk_sp<SkImage> result = sk_sp<SkImage>(
173 new SkImage_GaneshYUVA(sk_ref_sp(direct), this, targetCS, ColorSpaceMode::kConvert));
174 if (result) {
175 fOnMakeColorSpaceTarget = targetCS;
176 fOnMakeColorSpaceResult = result;
177 }
178 return result;
179 }
180
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const181 sk_sp<SkImage> SkImage_GaneshYUVA::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
182 return sk_sp<SkImage>(
183 new SkImage_GaneshYUVA(fContext, this, std::move(newCS), ColorSpaceMode::kReinterpret));
184 }
185
asView(GrRecordingContext * rContext,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy) const186 std::tuple<GrSurfaceProxyView, GrColorType> SkImage_GaneshYUVA::asView(GrRecordingContext* rContext,
187 skgpu::Mipmapped mipmapped,
188 GrImageTexGenPolicy) const {
189 if (!fContext->priv().matches(rContext)) {
190 return {};
191 }
192 auto sfc = rContext->priv().makeSFC(this->imageInfo(),
193 "Image_GpuYUVA_ReinterpretColorSpace",
194 SkBackingFit::kExact,
195 /*sample count*/ 1,
196 mipmapped,
197 GrProtected::kNo,
198 fYUVAProxies.textureOrigin(),
199 skgpu::Budgeted::kYes);
200 if (!sfc) {
201 return {};
202 }
203
204 const GrCaps& caps = *rContext->priv().caps();
205 auto fp = GrYUVtoRGBEffect::Make(fYUVAProxies, GrSamplerState::Filter::kNearest, caps);
206 if (fFromColorSpace) {
207 fp = GrColorSpaceXformEffect::Make(std::move(fp),
208 fFromColorSpace.get(),
209 this->alphaType(),
210 this->colorSpace(),
211 this->alphaType());
212 }
213 sfc->fillWithFP(std::move(fp));
214
215 return {sfc->readSurfaceView(), sfc->colorInfo().colorType()};
216 }
217
asFragmentProcessor(GrRecordingContext * context,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain) const218 std::unique_ptr<GrFragmentProcessor> SkImage_GaneshYUVA::asFragmentProcessor(
219 GrRecordingContext* context,
220 SkSamplingOptions sampling,
221 const SkTileMode tileModes[2],
222 const SkMatrix& m,
223 const SkRect* subset,
224 const SkRect* domain) const {
225 if (!fContext->priv().matches(context)) {
226 return {};
227 }
228 // At least for now we do not attempt aniso filtering on YUVA images.
229 if (sampling.isAniso()) {
230 sampling =
231 SkSamplingPriv::AnisoFallback(fYUVAProxies.mipmapped() == skgpu::Mipmapped::kYes);
232 }
233
234 auto wmx = SkTileModeToWrapMode(tileModes[0]);
235 auto wmy = SkTileModeToWrapMode(tileModes[1]);
236 GrSamplerState sampler(wmx, wmy, sampling.filter, sampling.mipmap);
237 if (sampler.mipmapped() == skgpu::Mipmapped::kYes && !this->setupMipmapsForPlanes(context)) {
238 sampler = GrSamplerState(sampler.wrapModeX(),
239 sampler.wrapModeY(),
240 sampler.filter(),
241 GrSamplerState::MipmapMode::kNone);
242 }
243
244 const auto& yuvM = sampling.useCubic ? SkMatrix::I() : m;
245 auto fp = GrYUVtoRGBEffect::Make(
246 fYUVAProxies, sampler, *context->priv().caps(), yuvM, subset, domain);
247 if (sampling.useCubic) {
248 fp = GrBicubicEffect::Make(std::move(fp),
249 this->alphaType(),
250 m,
251 sampling.cubic,
252 GrBicubicEffect::Direction::kXY);
253 }
254 if (fFromColorSpace) {
255 fp = GrColorSpaceXformEffect::Make(std::move(fp),
256 fFromColorSpace.get(),
257 this->alphaType(),
258 this->colorSpace(),
259 this->alphaType());
260 }
261 return fp;
262 }
263