Flutter Engine
 
Loading...
Searching...
No Matches
image_encoding_impeller.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
9#include "fml/status.h"
14#include "third_party/skia/include/core/SkBitmap.h"
15#include "third_party/skia/include/core/SkImage.h"
16
17namespace flutter {
18namespace {
19
20std::optional<SkColorType> ToSkColorType(impeller::PixelFormat format) {
21 switch (format) {
23 return SkColorType::kRGBA_8888_SkColorType;
25 return SkColorType::kRGBA_F16_SkColorType;
27 return SkColorType::kBGRA_8888_SkColorType;
29 return SkColorType::kBGR_101010x_XR_SkColorType;
31 return SkColorType::kBGRA_10101010_XR_SkColorType;
32 default:
33 return std::nullopt;
34 }
35}
36
37sk_sp<SkImage> ConvertBufferToSkImage(
38 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
39 SkColorType color_type,
40 SkISize dimensions) {
41 SkImageInfo image_info = SkImageInfo::Make(dimensions, color_type,
42 SkAlphaType::kPremul_SkAlphaType);
43 SkBitmap bitmap;
44 auto func = [](void* addr, void* context) {
45 auto buffer =
46 static_cast<std::shared_ptr<impeller::DeviceBuffer>*>(context);
47 buffer->reset();
48 delete buffer;
49 };
50 auto bytes_per_pixel = image_info.bytesPerPixel();
51 bitmap.installPixels(image_info, buffer->OnGetContents(),
52 dimensions.width() * bytes_per_pixel, func,
53 new std::shared_ptr<impeller::DeviceBuffer>(buffer));
54 bitmap.setImmutable();
55
56 sk_sp<SkImage> raster_image = SkImages::RasterFromBitmap(bitmap);
57 return raster_image;
58}
59
60[[nodiscard]] fml::Status DoConvertImageToRasterImpeller(
61 const sk_sp<DlImage>& dl_image,
62 const std::function<void(fml::StatusOr<sk_sp<SkImage>>)>& encode_task,
64 const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
65 const std::shared_ptr<impeller::Context>& impeller_context) {
66 fml::Status result;
67 is_gpu_disabled_sync_switch->Execute(
69 .SetIfTrue([&result] {
70 result =
71 fml::Status(fml::StatusCode::kUnavailable, "GPU unavailable.");
72 })
73 .SetIfFalse(
74 [&dl_image, &encode_task, &snapshot_delegate, &impeller_context] {
76 dl_image, encode_task, snapshot_delegate, impeller_context);
77 }));
78 return result;
79}
80
81/// Same as `DoConvertImageToRasterImpeller` but it will attempt to retry the
82/// operation if `DoConvertImageToRasterImpeller` returns kUnavailable when the
83/// GPU becomes available again.
84void DoConvertImageToRasterImpellerWithRetry(
85 const sk_sp<DlImage>& dl_image,
86 std::function<void(fml::StatusOr<sk_sp<SkImage>>)>&& encode_task,
88 const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
89 const std::shared_ptr<impeller::Context>& impeller_context,
90 const fml::RefPtr<fml::TaskRunner>& retry_runner) {
91 fml::Status status = DoConvertImageToRasterImpeller(
92 dl_image, encode_task, snapshot_delegate, is_gpu_disabled_sync_switch,
93 impeller_context);
94 if (!status.ok()) {
95 // If the conversion failed because of the GPU is unavailable, store the
96 // task on the Context so it can be executed when the GPU becomes available.
97 if (status.code() == fml::StatusCode::kUnavailable) {
98 impeller_context->StoreTaskForGPU(
99 [dl_image, encode_task, snapshot_delegate,
100 is_gpu_disabled_sync_switch, impeller_context,
101 retry_runner]() mutable {
102 auto retry_task = [dl_image, encode_task = std::move(encode_task),
103 snapshot_delegate, is_gpu_disabled_sync_switch,
104 impeller_context] {
105 fml::Status retry_status = DoConvertImageToRasterImpeller(
106 dl_image, encode_task, snapshot_delegate,
107 is_gpu_disabled_sync_switch, impeller_context);
108 if (!retry_status.ok()) {
109 // The retry failed for some reason, maybe the GPU became
110 // unavailable again. Don't retry again, just fail in this case.
111 encode_task(retry_status);
112 }
113 };
114 // If a `retry_runner` is specified, post the retry to it, otherwise
115 // execute it directly.
116 if (retry_runner) {
117 retry_runner->PostTask(retry_task);
118 } else {
119 retry_task();
120 }
121 },
122 [encode_task]() {
123 encode_task(
124 fml::Status(fml::StatusCode::kUnavailable, "GPU unavailable."));
125 });
126 } else {
127 // Pass on errors that are not `kUnavailable`.
128 encode_task(status);
129 }
130 }
131}
132
133} // namespace
134
136 const sk_sp<DlImage>& dl_image,
137 std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
138 const fml::TaskRunnerAffineWeakPtr<SnapshotDelegate>& snapshot_delegate,
139 const std::shared_ptr<impeller::Context>& impeller_context) {
140 auto texture = dl_image->impeller_texture();
141
142 if (impeller_context == nullptr) {
144 "Impeller context was null."));
145 return;
146 }
147
148 if (texture == nullptr) {
149 encode_task(
151 return;
152 }
153
154 auto dimensions = dl_image->GetSize();
155 auto color_type = ToSkColorType(texture->GetTextureDescriptor().format);
156
157 if (dimensions.IsEmpty()) {
159 "Image dimensions were empty."));
160 return;
161 }
162
163 if (!color_type.has_value()) {
165 "Failed to get color type from pixel format."));
166 return;
167 }
168
169 // Ensure that this thread has a context that can execute rendering
170 // commands. The thread may not already have a context if the raster
171 // task runner was assigned to a new thread and no previous rendering
172 // task has run on that thread.
173 if (snapshot_delegate) {
174 if (!snapshot_delegate->MakeRenderContextCurrent()) {
176 "Failed to bind the render context."));
177 return;
178 }
179 }
180
183 buffer_desc.readback = true; // set to false for testing.
184 buffer_desc.size =
185 texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
186 auto buffer =
187 impeller_context->GetResourceAllocator()->CreateBuffer(buffer_desc);
188 if (!buffer) {
190 "Failed to allocate destination buffer."));
191 return;
192 }
193
194 auto command_buffer = impeller_context->CreateCommandBuffer();
195 command_buffer->SetLabel("BlitTextureToBuffer Command Buffer");
196 auto pass = command_buffer->CreateBlitPass();
197 pass->SetLabel("BlitTextureToBuffer Blit Pass");
198 pass->AddCopy(texture, buffer);
199 pass->EncodeCommands();
200 auto completion = [buffer, color_type = color_type.value(), dimensions,
201 encode_task = std::move(encode_task)](
204 encode_task(fml::Status(fml::StatusCode::kUnknown, ""));
205 return;
206 }
207 buffer->Invalidate();
208 auto sk_image =
209 ConvertBufferToSkImage(buffer, color_type, ToSkISize(dimensions));
210 encode_task(sk_image);
211 };
212
213 if (!impeller_context->GetCommandQueue()
214 ->Submit({command_buffer}, completion)
215 .ok()) {
216 FML_LOG(ERROR) << "Failed to submit commands.";
217 }
218
219 impeller_context->DisposeThreadLocalCachedResources();
220}
221
223 const sk_sp<DlImage>& dl_image,
224 std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
225 const fml::RefPtr<fml::TaskRunner>& raster_task_runner,
226 const fml::RefPtr<fml::TaskRunner>& io_task_runner,
227 const fml::TaskRunnerAffineWeakPtr<SnapshotDelegate>& snapshot_delegate,
228 const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
229 const std::shared_ptr<impeller::Context>& impeller_context) {
230 auto original_encode_task = std::move(encode_task);
231 encode_task = [original_encode_task = std::move(original_encode_task),
232 io_task_runner](fml::StatusOr<sk_sp<SkImage>> image) mutable {
234 io_task_runner,
235 [original_encode_task = std::move(original_encode_task),
236 image = std::move(image)]() { original_encode_task(image); });
237 };
238
239 if (dl_image->owning_context() != DlImage::OwningContext::kRaster) {
240 DoConvertImageToRasterImpellerWithRetry(
241 dl_image, std::move(encode_task),
242 /*snapshot_delegate=*/{}, is_gpu_disabled_sync_switch, impeller_context,
243 /*retry_runner=*/nullptr);
244 return;
245 }
246
247 raster_task_runner->PostTask([dl_image, encode_task = std::move(encode_task),
248 io_task_runner, snapshot_delegate,
249 is_gpu_disabled_sync_switch, impeller_context,
250 raster_task_runner]() mutable {
251 DoConvertImageToRasterImpellerWithRetry(
252 dl_image, std::move(encode_task), snapshot_delegate,
253 is_gpu_disabled_sync_switch, impeller_context, raster_task_runner);
254 });
255}
256
258 const std::shared_ptr<impeller::Texture>& texture) {
259 const impeller::TextureDescriptor& desc = texture->GetTextureDescriptor();
260 switch (desc.format) {
261 case impeller::PixelFormat::kB10G10R10XR: // intentional_fallthrough
264 default:
265 return ColorSpace::kSRGB;
266 }
267}
268
269} // namespace flutter
static int GetColorSpace(const std::shared_ptr< impeller::Texture > &texture)
static void ConvertDlImageToSkImage(const sk_sp< DlImage > &dl_image, std::function< void(fml::StatusOr< sk_sp< SkImage > >)> encode_task, const fml::TaskRunnerAffineWeakPtr< SnapshotDelegate > &snapshot_delegate, const std::shared_ptr< impeller::Context > &impeller_context)
static void ConvertImageToRaster(const sk_sp< DlImage > &dl_image, std::function< void(fml::StatusOr< sk_sp< SkImage > >)> encode_task, const fml::RefPtr< fml::TaskRunner > &raster_task_runner, const fml::RefPtr< fml::TaskRunner > &io_task_runner, const fml::TaskRunnerAffineWeakPtr< SnapshotDelegate > &snapshot_delegate, const std::shared_ptr< const fml::SyncSwitch > &is_gpu_disabled_sync_switch, const std::shared_ptr< impeller::Context > &impeller_context)
bool ok() const
Definition status.h:71
fml::StatusCode code() const
Definition status.h:63
static void RunNowOrPostTask(const fml::RefPtr< fml::TaskRunner > &runner, const fml::closure &task)
FlutterVulkanImage * image
#define FML_LOG(severity)
Definition logging.h:101
FlTexture * texture
@ kExtendedSRGB
Definition image.h:18
@ kSRGB
Definition image.h:17
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
Definition switch_defs.h:98
const SkISize & ToSkISize(const DlISize &size)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:99
uint32_t color_type
Represents the 2 code paths available when calling |SyncSwitchExecute|.
Definition sync_switch.h:35
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...