Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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
10#include "fml/status.h"
15#include "third_party/skia/include/core/SkBitmap.h"
16#include "third_party/skia/include/core/SkImage.h"
17
18namespace flutter {
19namespace {
20
21std::optional<SkColorType> ToSkColorType(impeller::PixelFormat format) {
22 switch (format) {
24 return SkColorType::kRGBA_8888_SkColorType;
26 return SkColorType::kRGBA_F16_SkColorType;
28 return SkColorType::kBGRA_8888_SkColorType;
30 return SkColorType::kBGR_101010x_XR_SkColorType;
32 return SkColorType::kBGRA_10101010_XR_SkColorType;
34 return SkColorType::kRGBA_F32_SkColorType;
35 default:
36 return std::nullopt;
37 }
38}
39
40sk_sp<SkImage> ConvertBufferToSkImage(
41 const std::shared_ptr<impeller::DeviceBuffer>& buffer,
42 SkColorType color_type,
43 SkISize dimensions) {
44 SkImageInfo image_info = SkImageInfo::Make(dimensions, color_type,
45 SkAlphaType::kPremul_SkAlphaType);
46 SkBitmap bitmap;
47 auto func = [](void* addr, void* context) {
48 auto buffer =
49 static_cast<std::shared_ptr<impeller::DeviceBuffer>*>(context);
50 buffer->reset();
51 delete buffer;
52 };
53 auto bytes_per_pixel = image_info.bytesPerPixel();
54 bitmap.installPixels(image_info, buffer->OnGetContents(),
55 dimensions.width() * bytes_per_pixel, func,
56 new std::shared_ptr<impeller::DeviceBuffer>(buffer));
57 bitmap.setImmutable();
58
59 sk_sp<SkImage> raster_image = SkImages::RasterFromBitmap(bitmap);
60 return raster_image;
61}
62
63[[nodiscard]] fml::Status DoConvertImageToRasterImpeller(
64 const sk_sp<DlImage>& dl_image,
65 const std::function<void(fml::StatusOr<sk_sp<SkImage>>)>& encode_task,
67 const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
68 const std::shared_ptr<impeller::Context>& impeller_context) {
69 fml::Status result;
70 is_gpu_disabled_sync_switch->Execute(
72 .SetIfTrue([&result] {
73 result =
74 fml::Status(fml::StatusCode::kUnavailable, "GPU unavailable.");
75 })
76 .SetIfFalse(
77 [&dl_image, &encode_task, &snapshot_delegate, &impeller_context] {
79 dl_image, encode_task, snapshot_delegate, impeller_context);
80 }));
81 return result;
82}
83
84/// Same as `DoConvertImageToRasterImpeller` but it will attempt to retry the
85/// operation if `DoConvertImageToRasterImpeller` returns kUnavailable when the
86/// GPU becomes available again.
87void DoConvertImageToRasterImpellerWithRetry(
88 const sk_sp<DlImage>& dl_image,
89 std::function<void(fml::StatusOr<sk_sp<SkImage>>)>&& encode_task,
91 const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
92 const std::shared_ptr<impeller::Context>& impeller_context,
93 const fml::RefPtr<fml::TaskRunner>& retry_runner) {
94 fml::Status status = DoConvertImageToRasterImpeller(
95 dl_image, encode_task, snapshot_delegate, is_gpu_disabled_sync_switch,
96 impeller_context);
97 if (!status.ok()) {
98 // If the conversion failed because of the GPU is unavailable, store the
99 // task on the Context so it can be executed when the GPU becomes available.
100 if (status.code() == fml::StatusCode::kUnavailable) {
101 impeller_context->StoreTaskForGPU(
102 [dl_image, encode_task, snapshot_delegate,
103 is_gpu_disabled_sync_switch, impeller_context,
104 retry_runner]() mutable {
105 auto retry_task = [dl_image, encode_task = std::move(encode_task),
106 snapshot_delegate, is_gpu_disabled_sync_switch,
107 impeller_context] {
108 fml::Status retry_status = DoConvertImageToRasterImpeller(
109 dl_image, encode_task, snapshot_delegate,
110 is_gpu_disabled_sync_switch, impeller_context);
111 if (!retry_status.ok()) {
112 // The retry failed for some reason, maybe the GPU became
113 // unavailable again. Don't retry again, just fail in this case.
114 encode_task(retry_status);
115 }
116 };
117 // If a `retry_runner` is specified, post the retry to it, otherwise
118 // execute it directly.
119 if (retry_runner) {
120 retry_runner->PostTask(retry_task);
121 } else {
122 retry_task();
123 }
124 },
125 [encode_task]() {
126 encode_task(
127 fml::Status(fml::StatusCode::kUnavailable, "GPU unavailable."));
128 });
129 } else {
130 // Pass on errors that are not `kUnavailable`.
131 encode_task(status);
132 }
133 }
134}
135
136} // namespace
137
139 const sk_sp<DlImage>& dl_image,
140 std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
141 const fml::TaskRunnerAffineWeakPtr<SnapshotDelegate>& snapshot_delegate,
142 const std::shared_ptr<impeller::Context>& impeller_context) {
143 auto impeller_image = dl_image->asImpellerImage();
144 if (!impeller_image) {
146 "Image is not an Impeller image."));
147 return;
148 }
149 auto texture = impeller_image->GetImpellerTexture(impeller_context);
150
151 if (impeller_context == nullptr) {
153 "Impeller context was null."));
154 return;
155 }
156
157 if (texture == nullptr) {
158 encode_task(
160 return;
161 }
162
163 // Use the size of the texture allocated by the Impeller back end (which may
164 // differ from the original image size).
165 auto dimensions = DlISize(texture->GetSize());
166 auto color_type = ToSkColorType(texture->GetTextureDescriptor().format);
167
168 if (dimensions.IsEmpty()) {
170 "Image dimensions were empty."));
171 return;
172 }
173
174 if (!color_type.has_value()) {
176 "Failed to get color type from pixel format."));
177 return;
178 }
179
180 // Ensure that this thread has a context that can execute rendering
181 // commands. The thread may not already have a context if the raster
182 // task runner was assigned to a new thread and no previous rendering
183 // task has run on that thread.
184 if (snapshot_delegate) {
185 if (!snapshot_delegate->MakeRenderContextCurrent()) {
187 "Failed to bind the render context."));
188 return;
189 }
190 }
191
194 buffer_desc.readback = true; // set to false for testing.
195 buffer_desc.size =
196 texture->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
197 auto buffer =
198 impeller_context->GetResourceAllocator()->CreateBuffer(buffer_desc);
199 if (!buffer) {
201 "Failed to allocate destination buffer."));
202 return;
203 }
204
205 auto command_buffer = impeller_context->CreateCommandBuffer();
206 command_buffer->SetLabel("BlitTextureToBuffer Command Buffer");
207 auto pass = command_buffer->CreateBlitPass();
208 pass->SetLabel("BlitTextureToBuffer Blit Pass");
209 pass->AddCopy(texture, buffer);
210 pass->EncodeCommands();
211 auto completion = [buffer, color_type = color_type.value(), dimensions,
212 encode_task = std::move(encode_task)](
215 encode_task(fml::Status(fml::StatusCode::kUnknown, ""));
216 return;
217 }
218 buffer->Invalidate();
219 auto sk_image =
220 ConvertBufferToSkImage(buffer, color_type, ToSkISize(dimensions));
221 encode_task(sk_image);
222 };
223
224 if (!impeller_context->GetCommandQueue()
225 ->Submit({command_buffer}, completion)
226 .ok()) {
227 FML_LOG(ERROR) << "Failed to submit commands.";
228 }
229
230 impeller_context->DisposeThreadLocalCachedResources();
231}
232
234 const sk_sp<DlImage>& dl_image,
235 std::function<void(fml::StatusOr<sk_sp<SkImage>>)> encode_task,
236 const fml::RefPtr<fml::TaskRunner>& raster_task_runner,
237 const fml::RefPtr<fml::TaskRunner>& io_task_runner,
238 const fml::TaskRunnerAffineWeakPtr<SnapshotDelegate>& snapshot_delegate,
239 const std::shared_ptr<const fml::SyncSwitch>& is_gpu_disabled_sync_switch,
240 const std::shared_ptr<impeller::Context>& impeller_context) {
241 auto original_encode_task = std::move(encode_task);
242 encode_task = [original_encode_task = std::move(original_encode_task),
243 io_task_runner](fml::StatusOr<sk_sp<SkImage>> image) mutable {
245 io_task_runner,
246 [original_encode_task = std::move(original_encode_task),
247 image = std::move(image)]() { original_encode_task(image); });
248 };
249
250 if (dl_image->owning_context() != DlImage::OwningContext::kRaster) {
251 DoConvertImageToRasterImpellerWithRetry(
252 dl_image, std::move(encode_task),
253 /*snapshot_delegate=*/{}, is_gpu_disabled_sync_switch, impeller_context,
254 /*retry_runner=*/nullptr);
255 return;
256 }
257
258 raster_task_runner->PostTask([dl_image, encode_task = std::move(encode_task),
259 io_task_runner, snapshot_delegate,
260 is_gpu_disabled_sync_switch, impeller_context,
261 raster_task_runner]() mutable {
262 DoConvertImageToRasterImpellerWithRetry(
263 dl_image, std::move(encode_task), snapshot_delegate,
264 is_gpu_disabled_sync_switch, impeller_context, raster_task_runner);
265 });
266}
267
269 const std::shared_ptr<impeller::Texture>& texture) {
270 const impeller::TextureDescriptor& desc = texture->GetTextureDescriptor();
271 switch (desc.format) {
272 case impeller::PixelFormat::kB10G10R10XR: // intentional_fallthrough
275 default:
276 return ColorSpace::kSRGB;
277 }
278}
279
280} // 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
std::shared_ptr< SkBitmap > bitmap
FlTexture * texture
@ kExtendedSRGB
Definition image.h:26
@ kSRGB
Definition image.h:25
impeller::ISize32 DlISize
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
std::shared_ptr< ContextGLES > context
std::shared_ptr< CommandBuffer > command_buffer
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...