• 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_Base_Graphite.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "include/gpu/graphite/Image.h"
12 #include "include/gpu/graphite/Recorder.h"
13 #include "src/gpu/graphite/Device.h"
14 #include "src/gpu/graphite/DrawContext.h"
15 #include "src/gpu/graphite/Image_Graphite.h"
16 #include "src/gpu/graphite/Image_YUVA_Graphite.h"
17 #include "src/gpu/graphite/Log.h"
18 #include "src/gpu/graphite/RecorderPriv.h"
19 #include "src/gpu/graphite/Surface_Graphite.h"
20 #include "src/gpu/graphite/TextureUtils.h"
21 
22 namespace skgpu::graphite {
23 
Image_Base(const SkImageInfo & info,uint32_t uniqueID)24 Image_Base::Image_Base(const SkImageInfo& info, uint32_t uniqueID)
25     : SkImage_Base(info, uniqueID) {}
26 
27 Image_Base::~Image_Base() = default;
28 
linkDevices(const Image_Base * other)29 void Image_Base::linkDevices(const Image_Base* other) {
30     SkASSERT(other);
31 
32     SkAutoSpinlock lock{other->fDeviceLinkLock};
33     for (const auto& device : other->fLinkedDevices) {
34         this->linkDevice(device);
35     }
36 }
37 
linkDevice(sk_sp<Device> device)38 void Image_Base::linkDevice(sk_sp<Device> device) {
39     // Technically this lock isn't needed since this is only called before the Image is returned to
40     // user code that could expose it to multiple threads. But this quiets threading warnings and
41     // should be uncontested.
42     SkAutoSpinlock lock{fDeviceLinkLock};
43     fLinkedDevices.push_back(std::move(device));
44 }
45 
notifyInUse(Recorder * recorder,DrawContext * drawContext) const46 void Image_Base::notifyInUse(Recorder* recorder, DrawContext* drawContext) const {
47     SkASSERT(recorder);
48 
49     // The ref counts stored on each linked device are thread safe, but the Image's sk_sp's that
50     // track the refs its responsible for are *not* thread safe. Use a spin lock since the majority
51     // of device-linked images will be used only on the Recorder's thread. Since it should be
52     // uncontended, the empty check is also done inside the lock vs. a double-checked locking
53     // pattern that is non-trivial to ensure correctness in C++.
54     SkAutoSpinlock lock{fDeviceLinkLock};
55 
56     if (!fLinkedDevices.empty()) {
57         int emptyCount = 0;
58         for (sk_sp<Device>& device : fLinkedDevices) {
59             if (!device) {
60                 emptyCount++; // Already unlinked but array isn't empty yet
61             } else {
62                 if (device->isScratchDevice()) {
63                     sk_sp<Task> deviceDrawTask = device->lastDrawTask();
64                     if (deviceDrawTask) {
65                         // Increment the pending read count for the device's target
66                         recorder->priv().addPendingRead(device->target());
67                         if (drawContext) {
68                             // Add a reference to the device's drawTask to `drawContext` if that's
69                             // provided.
70                             drawContext->recordDependency(std::move(deviceDrawTask));
71                         } else {
72                             // If there's no `drawContext` this notify represents a copy, so for
73                             // now append the task to the root task list since that is where the
74                             // subsequent copy task will go as well.
75                             recorder->priv().add(std::move(deviceDrawTask));
76                         }
77                     } else {
78                         // If there's no draw task yet, the device is being drawn into a child
79                         // scratch device (backdrop filter or init-from-prev layer), and the child
80                         // will later on be drawn back into the device's `drawContext`. In this case
81                         // `device` should already have performed an internal flush and have no
82                         // pending work, and not yet be marked immutable. The correct action at this
83                         // point in time is to do nothing: the final task order in the device's
84                         // DrawTask will be pre-notified tasks into the device's target, then the
85                         // child's DrawTask when it's drawn back into `device`, and then any post
86                         // tasks that further modify the `device`'s target.
87                         SkASSERT(device->recorder() && device->recorder() == recorder);
88                     }
89 
90                     // Scratch devices are often already marked immutable, but they are also the
91                     // way in which Image finds the last snapped DrawTask so we don't unlink
92                     // scratch devices. The scratch image view will be short-lived as well, or the
93                     // device will transition to a non-scratch device in a future Recording and then
94                     // it will be unlinked then.
95                 } else {
96                     // Automatic flushing of image views only happens when mixing reads and writes
97                     // on the originating Recorder. Draws of the view on another Recorder will
98                     // always see the texture content dependent on how Recordings are inserted.
99                     if (device->recorder() == recorder) {
100                         // Non-scratch devices push their tasks to the root task list to maintain
101                         // an order consistent with the client-triggering actions. Because of this,
102                         // there's no need to add references to the `drawContext` that the device
103                         // is being drawn into.
104                         device->flushPendingWorkToRecorder();
105                     }
106                     if (!device->recorder() || device->unique()) {
107                         // The device will not record any more commands that modify the texture, so
108                         // the image doesn't need to be linked
109                         device.reset();
110                         emptyCount++;
111                     }
112                 }
113             }
114         }
115 
116         if (emptyCount == fLinkedDevices.size()) {
117             fLinkedDevices.clear();
118         }
119     }
120 }
121 
isDynamic() const122 bool Image_Base::isDynamic() const {
123     SkAutoSpinlock lock{fDeviceLinkLock};
124     int emptyCount = 0;
125     if (!fLinkedDevices.empty()) {
126         for (sk_sp<Device>& device : fLinkedDevices) {
127             if (!device || !device->recorder() || device->unique()) {
128                 device.reset();
129                 emptyCount++;
130             }
131         }
132         if (emptyCount == fLinkedDevices.size()) {
133             fLinkedDevices.clear();
134             emptyCount = 0;
135         }
136     }
137 
138     return emptyCount > 0;
139 }
140 
copyImage(Recorder * recorder,const SkIRect & subset,Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit,std::string_view label) const141 sk_sp<Image> Image_Base::copyImage(Recorder* recorder,
142                                    const SkIRect& subset,
143                                    Budgeted budgeted,
144                                    Mipmapped mipmapped,
145                                    SkBackingFit backingFit,
146                                    std::string_view label) const {
147     return CopyAsDraw(recorder, this, subset, this->imageInfo().colorInfo(),
148                       budgeted, mipmapped, backingFit, std::move(label));
149 }
150 
151 namespace {
152 
get_base_proxy_for_label(const Image_Base * baseImage)153 TextureProxy* get_base_proxy_for_label(const Image_Base* baseImage) {
154     if (baseImage->type() == SkImage_Base::Type::kGraphite) {
155         const Image* img = static_cast<const Image*>(baseImage);
156         return img->textureProxyView().proxy();
157     }
158     SkASSERT(baseImage->type() == SkImage_Base::Type::kGraphiteYUVA);
159     // We will end up flattening to RGBA for a YUVA image when we get a subset. We just grab
160     // the label off of the first channel's proxy and use that to be the stand in label.
161     const Image_YUVA* img = static_cast<const Image_YUVA*>(baseImage);
162     return img->proxyView(0).proxy();
163 }
164 
165 } // anonymous namespace
166 
onMakeSubset(Recorder * recorder,const SkIRect & subset,RequiredProperties requiredProps) const167 sk_sp<SkImage> Image_Base::onMakeSubset(Recorder* recorder,
168                                         const SkIRect& subset,
169                                         RequiredProperties requiredProps) const {
170     // optimization : return self if the subset == our bounds and requirements met and the image's
171     // texture is immutable
172     if (this->bounds() == subset &&
173         (!requiredProps.fMipmapped || this->hasMipmaps()) &&
174         !this->isDynamic()) {
175         return sk_ref_sp(this);
176     }
177 
178     TextureProxy* proxy = get_base_proxy_for_label(this);
179     SkASSERT(proxy);
180     std::string label = proxy->label();
181     if (label.empty()) {
182         label = "ImageSubsetTexture";
183     } else {
184         label += "_Subset";
185     }
186 
187     // The copied image is not considered budgeted because this is a client-invoked API and they
188     // will own the image.
189     return this->copyImage(recorder,
190                            subset,
191                            Budgeted::kNo,
192                            requiredProps.fMipmapped ? Mipmapped::kYes : Mipmapped::kNo,
193                            SkBackingFit::kExact,
194                            label);
195 }
196 
makeColorTypeAndColorSpace(Recorder * recorder,SkColorType targetCT,sk_sp<SkColorSpace> targetCS,RequiredProperties requiredProps) const197 sk_sp<SkImage> Image_Base::makeColorTypeAndColorSpace(Recorder* recorder,
198                                                       SkColorType targetCT,
199                                                       sk_sp<SkColorSpace> targetCS,
200                                                       RequiredProperties requiredProps) const {
201     SkColorInfo dstColorInfo{targetCT, this->alphaType(), std::move(targetCS)};
202     // optimization : return self if there's no color type/space change and the image's texture
203     // is immutable
204     if (this->imageInfo().colorInfo() == dstColorInfo && !this->isDynamic()) {
205         return sk_ref_sp(this);
206     }
207 
208     TextureProxy* proxy = get_base_proxy_for_label(this);
209     SkASSERT(proxy);
210     std::string label = proxy->label();
211     if (label.empty()) {
212         label = "ImageMakeCTandCSTexture";
213     } else {
214         label += "_CTandCSConversion";
215     }
216 
217     // Use CopyAsDraw directly to perform the color space changes. The copied image is not
218     // considered budgeted because this is a client-invoked API and they will own the image.
219     return CopyAsDraw(recorder,
220                       this,
221                       this->bounds(),
222                       dstColorInfo,
223                       Budgeted::kNo,
224                       requiredProps.fMipmapped ? Mipmapped::kYes : Mipmapped::kNo,
225                       SkBackingFit::kExact,
226                       label);
227 }
228 
229 // Ganesh APIs are no-ops
230 
onMakeSubset(GrDirectContext *,const SkIRect &) const231 sk_sp<SkImage> Image_Base::onMakeSubset(GrDirectContext*, const SkIRect&) const {
232     SKGPU_LOG_W("Cannot convert Graphite-backed image to Ganesh");
233     return nullptr;
234 }
235 
onMakeColorTypeAndColorSpace(SkColorType,sk_sp<SkColorSpace>,GrDirectContext *) const236 sk_sp<SkImage> Image_Base::onMakeColorTypeAndColorSpace(SkColorType,
237                                                         sk_sp<SkColorSpace>,
238                                                         GrDirectContext*) const {
239     SKGPU_LOG_W("Cannot convert Graphite-backed image to Ganesh");
240     return nullptr;
241 }
242 
onAsyncRescaleAndReadPixels(const SkImageInfo & info,SkIRect srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const243 void Image_Base::onAsyncRescaleAndReadPixels(const SkImageInfo& info,
244                                              SkIRect srcRect,
245                                              RescaleGamma rescaleGamma,
246                                              RescaleMode rescaleMode,
247                                              ReadPixelsCallback callback,
248                                              ReadPixelsContext context) const {
249     SKGPU_LOG_W("Cannot use Ganesh async API with Graphite-backed image, use API on Context");
250     callback(context, nullptr);
251 }
252 
onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,bool readAlpha,sk_sp<SkColorSpace> dstColorSpace,const SkIRect srcRect,const SkISize dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const253 void Image_Base::onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
254                                                    bool readAlpha,
255                                                    sk_sp<SkColorSpace> dstColorSpace,
256                                                    const SkIRect srcRect,
257                                                    const SkISize dstSize,
258                                                    RescaleGamma rescaleGamma,
259                                                    RescaleMode rescaleMode,
260                                                    ReadPixelsCallback callback,
261                                                    ReadPixelsContext context) const {
262     SKGPU_LOG_W("Cannot use Ganesh async API with Graphite-backed image, use API on Context");
263     callback(context, nullptr);
264 }
265 
266 } // namespace skgpu::graphite
267