1 /*
2 * Copyright 2021 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_Graphite.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkSurface.h"
13 #include "include/gpu/graphite/BackendTexture.h"
14 #include "include/gpu/graphite/Recorder.h"
15 #include "src/gpu/RefCntedCallback.h"
16 #include "src/gpu/graphite/Caps.h"
17 #include "src/gpu/graphite/Log.h"
18 #include "src/gpu/graphite/RecorderPriv.h"
19 #include "src/gpu/graphite/ResourceProvider.h"
20 #include "src/gpu/graphite/Texture.h"
21
22 namespace skgpu::graphite {
23
Image(uint32_t uniqueID,TextureProxyView view,const SkColorInfo & info)24 Image::Image(uint32_t uniqueID,
25 TextureProxyView view,
26 const SkColorInfo& info)
27 : Image_Base(SkImageInfo::Make(view.proxy()->dimensions(), info), uniqueID)
28 , fTextureProxyView(std::move(view)) {
29 }
30
Image(TextureProxyView view,const SkColorInfo & info)31 Image::Image(TextureProxyView view,
32 const SkColorInfo& info)
33 : Image_Base(SkImageInfo::Make(view.proxy()->dimensions(), info), kNeedNewImageUniqueID)
34 , fTextureProxyView(std::move(view)) {
35 }
36
~Image()37 Image::~Image() {}
38
onMakeSubset(const SkIRect & subset,Recorder * recorder,RequiredImageProperties requiredProps) const39 sk_sp<SkImage> Image::onMakeSubset(const SkIRect& subset,
40 Recorder* recorder,
41 RequiredImageProperties requiredProps) const {
42 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
43
44 // optimization : return self if the subset == our bounds and requirements met
45 if (bounds == subset && (requiredProps.fMipmapped == Mipmapped::kNo || this->hasMipmaps())) {
46 const SkImage* image = this;
47 return sk_ref_sp(const_cast<SkImage*>(image));
48 }
49
50 return this->copyImage(subset, recorder, requiredProps);
51 }
52
onMakeTextureImage(Recorder * recorder,RequiredImageProperties requiredProps) const53 sk_sp<SkImage> Image::onMakeTextureImage(Recorder* recorder,
54 RequiredImageProperties requiredProps) const {
55 if (requiredProps.fMipmapped == Mipmapped::kNo || this->hasMipmaps()) {
56 const SkImage* image = this;
57 return sk_ref_sp(const_cast<SkImage*>(image));
58 }
59
60 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
61 return this->copyImage(bounds, recorder, requiredProps);
62 }
63
copyImage(const SkIRect & subset,Recorder * recorder,RequiredImageProperties requiredProps) const64 sk_sp<SkImage> Image::copyImage(const SkIRect& subset,
65 Recorder* recorder,
66 RequiredImageProperties requiredProps) const {
67 TextureProxyView srcView = this->textureProxyView();
68 if (!srcView) {
69 return nullptr;
70 }
71
72 TextureProxyView copiedView = TextureProxyView::Copy(recorder,
73 this->imageInfo().colorInfo(),
74 srcView,
75 subset,
76 requiredProps.fMipmapped);
77 if (!copiedView) {
78 return nullptr;
79 }
80
81 return sk_sp<Image>(new Image(kNeedNewImageUniqueID,
82 std::move(copiedView),
83 this->imageInfo().colorInfo()));
84 }
85
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const86 sk_sp<SkImage> Image::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
87 return sk_make_sp<Image>(kNeedNewImageUniqueID,
88 fTextureProxyView,
89 this->imageInfo().colorInfo().makeColorSpace(std::move(newCS)));
90 }
91
onMakeColorTypeAndColorSpace(SkColorType targetCT,sk_sp<SkColorSpace> targetCS,Recorder * recorder,RequiredImageProperties requiredProps) const92 sk_sp<SkImage> Image::onMakeColorTypeAndColorSpace(SkColorType targetCT,
93 sk_sp<SkColorSpace> targetCS,
94 Recorder* recorder,
95 RequiredImageProperties requiredProps) const {
96 SkAlphaType at = (this->alphaType() == kOpaque_SkAlphaType) ? kPremul_SkAlphaType
97 : this->alphaType();
98
99 SkImageInfo ii = SkImageInfo::Make(this->dimensions(), targetCT, at, std::move(targetCS));
100
101 sk_sp<SkSurface> s = SkSurface::MakeGraphite(recorder, ii, requiredProps.fMipmapped);
102 if (!s) {
103 return nullptr;
104 }
105
106 s->getCanvas()->drawImage(this, 0, 0);
107
108 return s->asImage();
109 }
110
111 } // namespace skgpu::graphite
112
113 using namespace skgpu::graphite;
114
115 namespace {
116
validate_backend_texture(const Caps * caps,const BackendTexture & texture,const SkColorInfo & info)117 bool validate_backend_texture(const Caps* caps,
118 const BackendTexture& texture,
119 const SkColorInfo& info) {
120 if (!texture.isValid() ||
121 texture.dimensions().width() <= 0 ||
122 texture.dimensions().height() <= 0) {
123 return false;
124 }
125
126 if (!SkColorInfoIsValid(info)) {
127 return false;
128 }
129
130 if (!caps->isTexturable(texture.info())) {
131 return false;
132 }
133
134 return caps->areColorTypeAndTextureInfoCompatible(info.colorType(), texture.info());
135 }
136
137 } // anonymous namespace
138
MakePromiseImageLazyProxy(SkISize dimensions,TextureInfo textureInfo,Volatile isVolatile,GraphitePromiseImageFulfillProc fulfillProc,sk_sp<skgpu::RefCntedCallback> releaseHelper,GraphitePromiseTextureReleaseProc textureReleaseProc)139 sk_sp<TextureProxy> Image::MakePromiseImageLazyProxy(
140 SkISize dimensions,
141 TextureInfo textureInfo,
142 Volatile isVolatile,
143 GraphitePromiseImageFulfillProc fulfillProc,
144 sk_sp<skgpu::RefCntedCallback> releaseHelper,
145 GraphitePromiseTextureReleaseProc textureReleaseProc) {
146 SkASSERT(!dimensions.isEmpty());
147 SkASSERT(releaseHelper);
148
149 if (!fulfillProc) {
150 return nullptr;
151 }
152
153 /**
154 * This class is the lazy instantiation callback for promise images. It manages calling the
155 * client's Fulfill, ImageRelease, and TextureRelease procs.
156 */
157 class PromiseLazyInstantiateCallback {
158 public:
159 PromiseLazyInstantiateCallback(GraphitePromiseImageFulfillProc fulfillProc,
160 sk_sp<skgpu::RefCntedCallback> releaseHelper,
161 GraphitePromiseTextureReleaseProc textureReleaseProc)
162 : fFulfillProc(fulfillProc)
163 , fReleaseHelper(std::move(releaseHelper))
164 , fTextureReleaseProc(textureReleaseProc) {
165 }
166 PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
167 PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
168 // Because we get wrapped in std::function we must be copyable. But we should never
169 // be copied.
170 SkASSERT(false);
171 }
172 PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
173 PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
174 SkASSERT(false);
175 return *this;
176 }
177
178 sk_sp<Texture> operator()(ResourceProvider* resourceProvider) {
179
180 auto [ backendTexture, textureReleaseCtx ] = fFulfillProc(fReleaseHelper->context());
181 if (!backendTexture.isValid()) {
182 SKGPU_LOG_W("FulFill Proc failed");
183 return nullptr;
184 }
185
186 sk_sp<RefCntedCallback> textureReleaseCB = RefCntedCallback::Make(fTextureReleaseProc,
187 textureReleaseCtx);
188
189 sk_sp<Texture> texture = resourceProvider->createWrappedTexture(backendTexture);
190 if (!texture) {
191 SKGPU_LOG_W("Texture creation failed");
192 return nullptr;
193 }
194
195 texture->setReleaseCallback(std::move(textureReleaseCB));
196 return texture;
197 }
198
199 private:
200 GraphitePromiseImageFulfillProc fFulfillProc;
201 sk_sp<skgpu::RefCntedCallback> fReleaseHelper;
202 GraphitePromiseTextureReleaseProc fTextureReleaseProc;
203
204 } callback(fulfillProc, std::move(releaseHelper), textureReleaseProc);
205
206 return TextureProxy::MakeLazy(dimensions,
207 textureInfo,
208 skgpu::Budgeted::kNo, // This is destined for a user's SkImage
209 isVolatile,
210 std::move(callback));
211 }
212
MakeGraphitePromiseTexture(Recorder * recorder,SkISize dimensions,const TextureInfo & textureInfo,const SkColorInfo & colorInfo,Volatile isVolatile,GraphitePromiseImageFulfillProc fulfillProc,GraphitePromiseImageReleaseProc imageReleaseProc,GraphitePromiseTextureReleaseProc textureReleaseProc,GraphitePromiseImageContext imageContext)213 sk_sp<SkImage> SkImage::MakeGraphitePromiseTexture(
214 Recorder* recorder,
215 SkISize dimensions,
216 const TextureInfo& textureInfo,
217 const SkColorInfo& colorInfo,
218 Volatile isVolatile,
219 GraphitePromiseImageFulfillProc fulfillProc,
220 GraphitePromiseImageReleaseProc imageReleaseProc,
221 GraphitePromiseTextureReleaseProc textureReleaseProc,
222 GraphitePromiseImageContext imageContext) {
223
224 // Our contract is that we will always call the _image_ release proc even on failure.
225 // We use the helper to convey the imageContext, so we need to ensure Make doesn't fail.
226 imageReleaseProc = imageReleaseProc ? imageReleaseProc : [](void*) {};
227 auto releaseHelper = skgpu::RefCntedCallback::Make(imageReleaseProc, imageContext);
228
229 if (!recorder) {
230 SKGPU_LOG_W("Null Recorder");
231 return nullptr;
232 }
233
234 const Caps* caps = recorder->priv().caps();
235
236 SkImageInfo info = SkImageInfo::Make(dimensions, colorInfo);
237 if (!SkImageInfoIsValid(info)) {
238 SKGPU_LOG_W("Invalid SkImageInfo");
239 return nullptr;
240 }
241
242 if (!caps->areColorTypeAndTextureInfoCompatible(colorInfo.colorType(), textureInfo)) {
243 SKGPU_LOG_W("Incompatible SkColorType and TextureInfo");
244 return nullptr;
245 }
246
247 sk_sp<TextureProxy> proxy = Image::MakePromiseImageLazyProxy(dimensions,
248 textureInfo,
249 isVolatile,
250 fulfillProc,
251 std::move(releaseHelper),
252 textureReleaseProc);
253 if (!proxy) {
254 return nullptr;
255 }
256
257 skgpu::Swizzle swizzle = caps->getReadSwizzle(colorInfo.colorType(), textureInfo);
258 TextureProxyView view(std::move(proxy), swizzle);
259 return sk_make_sp<Image>(view, colorInfo);
260 }
261
MakeGraphiteFromBackendTexture(Recorder * recorder,const BackendTexture & backendTex,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)262 sk_sp<SkImage> SkImage::MakeGraphiteFromBackendTexture(Recorder* recorder,
263 const BackendTexture& backendTex,
264 SkColorType ct,
265 SkAlphaType at,
266 sk_sp<SkColorSpace> cs) {
267 if (!recorder) {
268 return nullptr;
269 }
270
271 const Caps* caps = recorder->priv().caps();
272
273 SkColorInfo info(ct, at, std::move(cs));
274
275 if (!validate_backend_texture(caps, backendTex, info)) {
276 return nullptr;
277 }
278
279 sk_sp<Texture> texture = recorder->priv().resourceProvider()->createWrappedTexture(backendTex);
280 if (!texture) {
281 return nullptr;
282 }
283
284 sk_sp<TextureProxy> proxy(new TextureProxy(std::move(texture)));
285
286 skgpu::Swizzle swizzle = caps->getReadSwizzle(ct, backendTex.info());
287 TextureProxyView view(std::move(proxy), swizzle);
288 return sk_make_sp<Image>(view, info);
289 }
290