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/Surface_Graphite.h"
9
10 #include "include/core/SkCapabilities.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/gpu/graphite/BackendTexture.h"
13 #include "include/gpu/graphite/Recorder.h"
14 #include "include/gpu/graphite/Surface.h"
15 #include "src/core/SkSurfacePriv.h"
16 #include "src/gpu/RefCntedCallback.h"
17 #include "src/gpu/SkBackingFit.h"
18 #include "src/gpu/graphite/Caps.h"
19 #include "src/gpu/graphite/Device.h"
20 #include "src/gpu/graphite/Image_Graphite.h"
21 #include "src/gpu/graphite/Log.h"
22 #include "src/gpu/graphite/RecorderPriv.h"
23 #include "src/gpu/graphite/ResourceProvider.h"
24 #include "src/gpu/graphite/Texture.h"
25
26 namespace skgpu::graphite {
27
Surface(sk_sp<Device> device)28 Surface::Surface(sk_sp<Device> device)
29 : SkSurface_Base(device->width(), device->height(), &device->surfaceProps())
30 , fDevice(std::move(device))
31 , fImageView(Image::WrapDevice(fDevice)) {}
32
~Surface()33 Surface::~Surface() {
34 // Mark the device immutable when the Surface is destroyed to flush any pending work to the
35 // recorder and to flag the device so that any linked image views can detach from the Device
36 // when they are next drawn.
37 fDevice->setImmutable();
38 }
39
imageInfo() const40 SkImageInfo Surface::imageInfo() const {
41 return fDevice->imageInfo();
42 }
43
onGetRecorder() const44 Recorder* Surface::onGetRecorder() const { return fDevice->recorder(); }
45
readSurfaceView() const46 TextureProxyView Surface::readSurfaceView() const {
47 return fDevice->readSurfaceView();
48 }
49
onNewCanvas()50 SkCanvas* Surface::onNewCanvas() { return new SkCanvas(fDevice); }
51
onNewSurface(const SkImageInfo & ii)52 sk_sp<SkSurface> Surface::onNewSurface(const SkImageInfo& ii) {
53 return fDevice->makeSurface(ii, this->props());
54 }
55
onNewImageSnapshot(const SkIRect * subset)56 sk_sp<SkImage> Surface::onNewImageSnapshot(const SkIRect* subset) {
57 return this->makeImageCopy(subset, fDevice->target()->mipmapped());
58 }
59
asImage() const60 sk_sp<Image> Surface::asImage() const {
61 if (this->hasCachedImage()) {
62 SKGPU_LOG_W("Intermingling makeImageSnapshot and asImage calls may produce "
63 "unexpected results. Please use either the old _or_ new API.");
64 }
65 return fImageView;
66 }
67
makeImageCopy(const SkIRect * subset,Mipmapped mipmapped) const68 sk_sp<Image> Surface::makeImageCopy(const SkIRect* subset, Mipmapped mipmapped) const {
69 if (this->hasCachedImage()) {
70 SKGPU_LOG_W("Intermingling makeImageSnapshot and asImage calls may produce "
71 "unexpected results. Please use either the old _or_ new API.");
72 }
73
74 SkIRect srcRect = subset ? *subset : SkIRect::MakeSize(this->imageInfo().dimensions());
75 // NOTE: Must copy through fDevice and not fImageView if the surface's texture is not sampleable
76 return fDevice->makeImageCopy(srcRect, Budgeted::kNo, mipmapped, SkBackingFit::kExact);
77 }
78
onWritePixels(const SkPixmap & pixmap,int x,int y)79 void Surface::onWritePixels(const SkPixmap& pixmap, int x, int y) {
80 fDevice->writePixels(pixmap, x, y);
81 }
82
onCopyOnWrite(ContentChangeMode)83 bool Surface::onCopyOnWrite(ContentChangeMode) { return true; }
84
onAsyncRescaleAndReadPixels(const SkImageInfo & info,SkIRect srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context)85 void Surface::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
86 SkIRect srcRect,
87 RescaleGamma rescaleGamma,
88 RescaleMode rescaleMode,
89 ReadPixelsCallback callback,
90 ReadPixelsContext context) {
91 // Not supported for Graphite. Use Context::asyncRescaleAndReadPixels instead.
92 callback(context, nullptr);
93 }
94
onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,bool readAlpha,sk_sp<SkColorSpace> dstColorSpace,SkIRect srcRect,SkISize dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context)95 void Surface::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
96 bool readAlpha,
97 sk_sp<SkColorSpace> dstColorSpace,
98 SkIRect srcRect,
99 SkISize dstSize,
100 RescaleGamma rescaleGamma,
101 RescaleMode rescaleMode,
102 ReadPixelsCallback callback,
103 ReadPixelsContext context) {
104 // Not supported for Graphite. Use Context::asyncRescaleAndReadPixelsYUV420 instead.
105 callback(context, nullptr);
106 }
107
onCapabilities()108 sk_sp<const SkCapabilities> Surface::onCapabilities() {
109 return fDevice->recorder()->priv().caps()->capabilities();
110 }
111
backingTextureProxy() const112 TextureProxy* Surface::backingTextureProxy() const { return fDevice->target(); }
113
Make(Recorder * recorder,const SkImageInfo & info,std::string_view label,Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit,const SkSurfaceProps * props,LoadOp initialLoadOp,bool registerWithRecorder)114 sk_sp<Surface> Surface::Make(Recorder* recorder,
115 const SkImageInfo& info,
116 std::string_view label,
117 Budgeted budgeted,
118 Mipmapped mipmapped,
119 SkBackingFit backingFit,
120 const SkSurfaceProps* props,
121 LoadOp initialLoadOp,
122 bool registerWithRecorder) {
123 sk_sp<Device> device = Device::Make(recorder,
124 info,
125 budgeted,
126 mipmapped,
127 backingFit,
128 SkSurfacePropsCopyOrDefault(props),
129 initialLoadOp,
130 std::move(label),
131 registerWithRecorder);
132 if (!device) {
133 return nullptr;
134 }
135 // A non-budgeted surface should be fully instantiated before we return it
136 // to the client.
137 SkASSERT(budgeted == Budgeted::kYes || device->target()->isInstantiated());
138 return sk_make_sp<Surface>(std::move(device));
139 }
140
Flush(sk_sp<SkSurface> surface)141 void Flush(sk_sp<SkSurface> surface) {
142 return Flush(surface.get());
143 }
144
Flush(SkSurface * surface)145 void Flush(SkSurface* surface) {
146 if (!surface) {
147 return;
148 }
149 auto sb = asSB(surface);
150 if (!sb->isGraphiteBacked()) {
151 return;
152 }
153 auto gs = static_cast<Surface*>(surface);
154 gs->fDevice->flushPendingWorkToRecorder();
155 }
156
157 } // namespace skgpu::graphite
158
159 using namespace skgpu::graphite;
160
161 namespace {
162
validate_backend_texture(const Caps * caps,const BackendTexture & texture,const SkColorInfo & info)163 bool validate_backend_texture(const Caps* caps,
164 const BackendTexture& texture,
165 const SkColorInfo& info) {
166 if (!texture.isValid() ||
167 texture.dimensions().width() <= 0 ||
168 texture.dimensions().height() <= 0) {
169 return false;
170 }
171
172 if (!SkColorInfoIsValid(info)) {
173 return false;
174 }
175
176 if (!caps->isRenderable(texture.info())) {
177 return false;
178 }
179
180 return caps->areColorTypeAndTextureInfoCompatible(info.colorType(), texture.info());
181 }
182
183 } // anonymous namespace
184
185 namespace SkSurfaces {
AsImage(sk_sp<const SkSurface> surface)186 sk_sp<SkImage> AsImage(sk_sp<const SkSurface> surface) {
187 if (!surface) {
188 return nullptr;
189 }
190 auto sb = asConstSB(surface.get());
191 if (!sb->isGraphiteBacked()) {
192 return nullptr;
193 }
194 auto gs = static_cast<const Surface*>(surface.get());
195 return gs->asImage();
196 }
197
AsImageCopy(sk_sp<const SkSurface> surface,const SkIRect * subset,skgpu::Mipmapped mipmapped)198 sk_sp<SkImage> AsImageCopy(sk_sp<const SkSurface> surface,
199 const SkIRect* subset,
200 skgpu::Mipmapped mipmapped) {
201 if (!surface) {
202 return nullptr;
203 }
204 auto sb = asConstSB(surface.get());
205 if (!sb->isGraphiteBacked()) {
206 return nullptr;
207 }
208 auto gs = static_cast<const Surface*>(surface.get());
209 return gs->makeImageCopy(subset, mipmapped);
210 }
211
RenderTarget(Recorder * recorder,const SkImageInfo & info,skgpu::Mipmapped mipmapped,const SkSurfaceProps * props,std::string_view label)212 sk_sp<SkSurface> RenderTarget(Recorder* recorder,
213 const SkImageInfo& info,
214 skgpu::Mipmapped mipmapped,
215 const SkSurfaceProps* props,
216 std::string_view label) {
217 if (label.empty()) {
218 label = "SkSurfaceRenderTarget";
219 }
220 // The client is getting the ref on this surface so it must be unbudgeted.
221 return skgpu::graphite::Surface::Make(recorder, info, std::move(label), skgpu::Budgeted::kNo,
222 mipmapped, SkBackingFit::kExact, props);
223 }
224
WrapBackendTexture(Recorder * recorder,const BackendTexture & backendTex,SkColorType ct,sk_sp<SkColorSpace> cs,const SkSurfaceProps * props,TextureReleaseProc releaseP,ReleaseContext releaseC,std::string_view label)225 sk_sp<SkSurface> WrapBackendTexture(Recorder* recorder,
226 const BackendTexture& backendTex,
227 SkColorType ct,
228 sk_sp<SkColorSpace> cs,
229 const SkSurfaceProps* props,
230 TextureReleaseProc releaseP,
231 ReleaseContext releaseC,
232 std::string_view label) {
233 auto releaseHelper = skgpu::RefCntedCallback::Make(releaseP, releaseC);
234
235 if (!recorder) {
236 return nullptr;
237 }
238
239 const Caps* caps = recorder->priv().caps();
240
241 SkColorInfo info(ct, kPremul_SkAlphaType, std::move(cs));
242
243 if (!validate_backend_texture(caps, backendTex, info)) {
244 SKGPU_LOG_E("validate_backend_texture failed: backendTex.info = %s; colorType = %d",
245 backendTex.info().toString().c_str(),
246 info.colorType());
247 return nullptr;
248 }
249
250 if (label.empty()) {
251 label = "SkSurfaceWrappedTexture";
252 }
253
254 sk_sp<Texture> texture =
255 recorder->priv().resourceProvider()->createWrappedTexture(backendTex, std::move(label));
256 if (!texture) {
257 return nullptr;
258 }
259 texture->setReleaseCallback(std::move(releaseHelper));
260
261 sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
262 SkISize deviceSize = proxy->dimensions();
263 // Use kLoad for this device to preserve the existing contents of the wrapped backend texture.
264 sk_sp<Device> device = Device::Make(recorder,
265 std::move(proxy),
266 deviceSize,
267 info,
268 SkSurfacePropsCopyOrDefault(props),
269 LoadOp::kLoad);
270 return device ? sk_make_sp<Surface>(std::move(device)) : nullptr;
271 }
272
273 } // namespace SkSurfaces
274