Flutter Engine
The Flutter Engine
Image_Base_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
21
22namespace skgpu::graphite {
23
24Image_Base::Image_Base(const SkImageInfo& info, uint32_t uniqueID)
25 : SkImage_Base(info, uniqueID) {}
26
27Image_Base::~Image_Base() = default;
28
30 SkASSERT(other);
31
32 SkAutoSpinlock lock{other->fDeviceLinkLock};
33 for (const auto& device : other->fLinkedDevices) {
34 this->linkDevice(device);
35 }
36}
37
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
46void 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
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
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
151namespace {
152
153TextureProxy* 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 }
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
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,
192 requiredProps.fMipmapped ? Mipmapped::kYes : Mipmapped::kNo,
194 label);
195}
196
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,
224 requiredProps.fMipmapped ? Mipmapped::kYes : Mipmapped::kNo,
226 label);
227}
228
229// Ganesh APIs are no-ops
230
232 SKGPU_LOG_W("Cannot convert Graphite-backed image to Ganesh");
233 return nullptr;
234}
235
238 GrDirectContext*) const {
239 SKGPU_LOG_W("Cannot convert Graphite-backed image to Ganesh");
240 return nullptr;
241}
242
244 SkIRect srcRect,
245 RescaleGamma rescaleGamma,
246 RescaleMode rescaleMode,
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
254 bool readAlpha,
255 sk_sp<SkColorSpace> dstColorSpace,
256 const SkIRect srcRect,
257 const SkISize dstSize,
258 RescaleGamma rescaleGamma,
259 RescaleMode rescaleMode,
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
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SKGPU_LOG_W(fmt,...)
Definition: Log.h:40
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBackingFit
Definition: SkBackingFit.h:16
SkColorType
Definition: SkColorType.h:19
SkYUVColorSpace
Definition: SkImageInfo.h:68
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
virtual GrImageContext * context() const
Definition: SkImage_Base.h:112
virtual Type type() const =0
const SkImageInfo & imageInfo() const
Definition: SkImage.h:279
void * ReadPixelsContext
Definition: SkImage.h:578
SkAlphaType alphaType() const
Definition: SkImage.cpp:154
RescaleMode
Definition: SkImage.h:587
RescaleGamma
Definition: SkImage.h:585
SkIRect bounds() const
Definition: SkImage.h:303
void(ReadPixelsContext, std::unique_ptr< const AsyncReadResult >) ReadPixelsCallback
Definition: SkImage.h:583
void recordDependency(sk_sp< Task >)
Image_Base(const SkImageInfo &info, uint32_t uniqueID)
void linkDevice(sk_sp< Device >)
void onAsyncRescaleAndReadPixels(const SkImageInfo &, SkIRect srcRect, RescaleGamma, RescaleMode, ReadPixelsCallback, ReadPixelsContext) const override
sk_sp< SkImage > onMakeSubset(Recorder *, const SkIRect &, RequiredProperties) const override
sk_sp< SkImage > makeColorTypeAndColorSpace(Recorder *, SkColorType targetCT, sk_sp< SkColorSpace > targetCS, RequiredProperties) const override
void notifyInUse(Recorder *, DrawContext *drawContext) const
virtual sk_sp< Image > copyImage(Recorder *, const SkIRect &subset, Budgeted, Mipmapped, SkBackingFit, std::string_view label) const
sk_sp< SkImage > onMakeColorTypeAndColorSpace(SkColorType, sk_sp< SkColorSpace >, GrDirectContext *) const override
void linkDevices(const Image_Base *)
void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace, bool readAlpha, sk_sp< SkColorSpace >, SkIRect srcRect, SkISize dstSize, RescaleGamma, RescaleMode, ReadPixelsCallback, ReadPixelsContext) const override
const TextureProxyView & textureProxyView() const
void addPendingRead(const TextureProxy *)
Definition: Recorder.cpp:480
void add(sk_sp< Task >)
Definition: Recorder.cpp:485
const char * label() const
Definition: TextureProxy.h:40
VkDevice device
Definition: main.cc:53
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
sk_sp< Image > CopyAsDraw(Recorder *recorder, const SkImage *image, const SkIRect &subset, const SkColorInfo &dstColorInfo, Budgeted budgeted, Mipmapped mipmapped, SkBackingFit backingFit, std::string_view label)
Budgeted
Definition: GpuTypes.h:35
Mipmapped
Definition: GpuTypes.h:53
Definition: SkRect.h:32
Definition: SkSize.h:16