Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit bd5ec97

Browse files
committed
[Impeller] Return image decoder error messages to the Dart API
Fixes flutter/flutter#127061 See flutter/flutter#126768
1 parent 3a93bd7 commit bd5ec97

File tree

11 files changed

+161
-107
lines changed

11 files changed

+161
-107
lines changed

lib/ui/fixtures/ui_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,12 @@ void createPath() {
248248
external void _validatePath(Path path);
249249

250250
@pragma('vm:entry-point')
251-
void frameCallback(Object? image, int durationMilliseconds) {
252-
validateFrameCallback(image, durationMilliseconds);
251+
void frameCallback(Object? image, int durationMilliseconds, String decodeError) {
252+
validateFrameCallback(image, durationMilliseconds, decodeError);
253253
}
254254

255255
@pragma('vm:external-name', 'ValidateFrameCallback')
256-
external void validateFrameCallback(Object? image, int durationMilliseconds);
256+
external void validateFrameCallback(Object? image, int durationMilliseconds, String decodeError);
257257

258258
@pragma('vm:entry-point')
259259
void platformMessagePortResponseTest() async {

lib/ui/painting.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2125,9 +2125,12 @@ base class _NativeCodec extends NativeFieldWrapperClass1 implements Codec {
21252125
@override
21262126
Future<FrameInfo> getNextFrame() async {
21272127
final Completer<FrameInfo> completer = Completer<FrameInfo>.sync();
2128-
final String? error = _getNextFrame((_Image? image, int durationMilliseconds) {
2128+
final String? error = _getNextFrame((_Image? image, int durationMilliseconds, String decodeError) {
21292129
if (image == null) {
2130-
completer.completeError(Exception('Codec failed to produce an image, possibly due to invalid image data.'));
2130+
if (decodeError.isEmpty) {
2131+
decodeError = 'Codec failed to produce an image, possibly due to invalid image data.';
2132+
}
2133+
completer.completeError(Exception(decodeError));
21312134
} else {
21322135
completer.complete(FrameInfo._(
21332136
image: Image._(image, image.width, image.height),
@@ -2143,7 +2146,7 @@ base class _NativeCodec extends NativeFieldWrapperClass1 implements Codec {
21432146

21442147
/// Returns an error message on failure, null on success.
21452148
@Native<Handle Function(Pointer<Void>, Handle)>(symbol: 'Codec::getNextFrame')
2146-
external String? _getNextFrame(void Function(_Image?, int) callback);
2149+
external String? _getNextFrame(void Function(_Image?, int, String) callback);
21472150

21482151
@override
21492152
@Native<Void Function(Pointer<Void>)>(symbol: 'Codec::dispose')

lib/ui/painting/image_decoder.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class ImageDecoder {
3131

3232
virtual ~ImageDecoder();
3333

34-
using ImageResult = std::function<void(sk_sp<DlImage>)>;
34+
using ImageResult = std::function<void(sk_sp<DlImage>, std::string)>;
3535

3636
// Takes an image descriptor and returns a handle to a texture resident on the
3737
// GPU. All image decompression and resizes are done on a worker thread

lib/ui/painting/image_decoder_impeller.cc

Lines changed: 87 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,17 @@ static SkAlphaType ChooseCompatibleAlphaType(SkAlphaType type) {
107107
return type;
108108
}
109109

110-
std::optional<DecompressResult> ImageDecoderImpeller::DecompressTexture(
110+
DecompressResult ImageDecoderImpeller::DecompressTexture(
111111
ImageDescriptor* descriptor,
112112
SkISize target_size,
113113
impeller::ISize max_texture_size,
114114
bool supports_wide_gamut,
115115
const std::shared_ptr<impeller::Allocator>& allocator) {
116116
TRACE_EVENT0("impeller", __FUNCTION__);
117117
if (!descriptor) {
118-
FML_DLOG(ERROR) << "Invalid descriptor.";
119-
return std::nullopt;
118+
std::string decode_error("Invalid descriptor");
119+
FML_DLOG(ERROR) << decode_error;
120+
return DecompressResult{.decode_error = decode_error};
120121
}
121122

122123
target_size.set(std::min(static_cast<int32_t>(max_texture_size.width),
@@ -162,8 +163,9 @@ std::optional<DecompressResult> ImageDecoderImpeller::DecompressTexture(
162163
const auto pixel_format =
163164
impeller::skia_conversions::ToPixelFormat(image_info.colorType());
164165
if (!pixel_format.has_value()) {
165-
FML_DLOG(ERROR) << "Codec pixel format not supported by Impeller.";
166-
return std::nullopt;
166+
std::string decode_error("Codec pixel format not supported by Impeller.");
167+
FML_DLOG(ERROR) << decode_error;
168+
return DecompressResult{.decode_error = decode_error};
167169
}
168170

169171
auto bitmap = std::make_shared<SkBitmap>();
@@ -172,14 +174,16 @@ std::optional<DecompressResult> ImageDecoderImpeller::DecompressTexture(
172174

173175
if (descriptor->is_compressed()) {
174176
if (!bitmap->tryAllocPixels(bitmap_allocator.get())) {
175-
FML_DLOG(ERROR)
176-
<< "Could not allocate intermediate for image decompression.";
177-
return std::nullopt;
177+
std::string decode_error(
178+
"Could not allocate intermediate for image decompression.");
179+
FML_DLOG(ERROR) << decode_error;
180+
return DecompressResult{.decode_error = decode_error};
178181
}
179182
// Decode the image into the image generator's closest supported size.
180183
if (!descriptor->get_pixels(bitmap->pixmap())) {
181-
FML_DLOG(ERROR) << "Could not decompress image.";
182-
return std::nullopt;
184+
std::string decode_error("Could not decompress image.");
185+
FML_DLOG(ERROR) << decode_error;
186+
return DecompressResult{.decode_error = decode_error};
183187
}
184188
} else {
185189
auto temp_bitmap = std::make_shared<SkBitmap>();
@@ -189,9 +193,10 @@ std::optional<DecompressResult> ImageDecoderImpeller::DecompressTexture(
189193
temp_bitmap->setPixelRef(pixel_ref, 0, 0);
190194

191195
if (!bitmap->tryAllocPixels(bitmap_allocator.get())) {
192-
FML_DLOG(ERROR)
193-
<< "Could not allocate intermediate for pixel conversion.";
194-
return std::nullopt;
196+
std::string decode_error(
197+
"Could not allocate intermediate for pixel conversion.");
198+
FML_DLOG(ERROR) << decode_error;
199+
return DecompressResult{.decode_error = decode_error};
195200
}
196201
temp_bitmap->readPixels(bitmap->pixmap());
197202
bitmap->setImmutable();
@@ -200,7 +205,7 @@ std::optional<DecompressResult> ImageDecoderImpeller::DecompressTexture(
200205
if (bitmap->dimensions() == target_size) {
201206
auto buffer = bitmap_allocator->GetDeviceBuffer();
202207
if (!buffer.has_value()) {
203-
return std::nullopt;
208+
return DecompressResult{.decode_error = "Unable to get device buffer"};
204209
}
205210
return DecompressResult{.device_buffer = buffer.value(),
206211
.sk_bitmap = bitmap,
@@ -218,9 +223,10 @@ std::optional<DecompressResult> ImageDecoderImpeller::DecompressTexture(
218223
auto scaled_allocator = std::make_shared<ImpellerAllocator>(allocator);
219224
scaled_bitmap->setInfo(scaled_image_info);
220225
if (!scaled_bitmap->tryAllocPixels(scaled_allocator.get())) {
221-
FML_LOG(ERROR)
222-
<< "Could not allocate scaled bitmap for image decompression.";
223-
return std::nullopt;
226+
std::string decode_error(
227+
"Could not allocate scaled bitmap for image decompression.");
228+
FML_DLOG(ERROR) << decode_error;
229+
return DecompressResult{.decode_error = decode_error};
224230
}
225231
if (!bitmap->pixmap().scalePixels(
226232
scaled_bitmap->pixmap(),
@@ -231,26 +237,28 @@ std::optional<DecompressResult> ImageDecoderImpeller::DecompressTexture(
231237

232238
auto buffer = scaled_allocator->GetDeviceBuffer();
233239
if (!buffer.has_value()) {
234-
return std::nullopt;
240+
return DecompressResult{.decode_error = "Unable to get device buffer"};
235241
}
236242
return DecompressResult{.device_buffer = buffer.value(),
237243
.sk_bitmap = scaled_bitmap,
238244
.image_info = scaled_bitmap->info()};
239245
}
240246

241-
sk_sp<DlImage> ImageDecoderImpeller::UploadTextureToPrivate(
247+
std::pair<sk_sp<DlImage>, std::string>
248+
ImageDecoderImpeller::UploadTextureToPrivate(
242249
const std::shared_ptr<impeller::Context>& context,
243250
const std::shared_ptr<impeller::DeviceBuffer>& buffer,
244251
const SkImageInfo& image_info) {
245252
TRACE_EVENT0("impeller", __FUNCTION__);
246253
if (!context || !buffer) {
247-
return nullptr;
254+
return std::make_pair(nullptr, "No Impeller context or buffer available");
248255
}
249256
const auto pixel_format =
250257
impeller::skia_conversions::ToPixelFormat(image_info.colorType());
251258
if (!pixel_format) {
252-
FML_DLOG(ERROR) << "Pixel format unsupported by Impeller.";
253-
return nullptr;
259+
std::string decode_error("Pixel format unsupported by Impeller.");
260+
FML_DLOG(ERROR) << decode_error;
261+
return std::make_pair(nullptr, decode_error);
254262
}
255263

256264
impeller::TextureDescriptor texture_descriptor;
@@ -263,24 +271,29 @@ sk_sp<DlImage> ImageDecoderImpeller::UploadTextureToPrivate(
263271
auto dest_texture =
264272
context->GetResourceAllocator()->CreateTexture(texture_descriptor);
265273
if (!dest_texture) {
266-
FML_DLOG(ERROR) << "Could not create Impeller texture.";
267-
return nullptr;
274+
std::string decode_error("Could not create Impeller texture.");
275+
FML_DLOG(ERROR) << decode_error;
276+
return std::make_pair(nullptr, decode_error);
268277
}
269278

270279
dest_texture->SetLabel(
271280
impeller::SPrintF("ui.Image(%p)", dest_texture.get()).c_str());
272281

273282
auto command_buffer = context->CreateCommandBuffer();
274283
if (!command_buffer) {
275-
FML_DLOG(ERROR) << "Could not create command buffer for mipmap generation.";
276-
return nullptr;
284+
std::string decode_error(
285+
"Could not create command buffer for mipmap generation.");
286+
FML_DLOG(ERROR) << decode_error;
287+
return std::make_pair(nullptr, decode_error);
277288
}
278289
command_buffer->SetLabel("Mipmap Command Buffer");
279290

280291
auto blit_pass = command_buffer->CreateBlitPass();
281292
if (!blit_pass) {
282-
FML_DLOG(ERROR) << "Could not create blit pass for mipmap generation.";
283-
return nullptr;
293+
std::string decode_error(
294+
"Could not create blit pass for mipmap generation.");
295+
FML_DLOG(ERROR) << decode_error;
296+
return std::make_pair(nullptr, decode_error);
284297
}
285298
blit_pass->SetLabel("Mipmap Blit Pass");
286299
blit_pass->AddCopy(buffer->AsBufferView(), dest_texture);
@@ -290,27 +303,31 @@ sk_sp<DlImage> ImageDecoderImpeller::UploadTextureToPrivate(
290303

291304
blit_pass->EncodeCommands(context->GetResourceAllocator());
292305
if (!command_buffer->SubmitCommands()) {
293-
FML_DLOG(ERROR) << "Failed to submit blit pass command buffer.";
294-
return nullptr;
306+
std::string decode_error("Failed to submit blit pass command buffer.");
307+
FML_DLOG(ERROR) << decode_error;
308+
return std::make_pair(nullptr, decode_error);
295309
}
296310

297-
return impeller::DlImageImpeller::Make(std::move(dest_texture));
311+
return std::make_pair(
312+
impeller::DlImageImpeller::Make(std::move(dest_texture)), std::string());
298313
}
299314

300-
sk_sp<DlImage> ImageDecoderImpeller::UploadTextureToShared(
315+
std::pair<sk_sp<DlImage>, std::string>
316+
ImageDecoderImpeller::UploadTextureToShared(
301317
const std::shared_ptr<impeller::Context>& context,
302318
std::shared_ptr<SkBitmap> bitmap,
303319
bool create_mips) {
304320
TRACE_EVENT0("impeller", __FUNCTION__);
305321
if (!context || !bitmap) {
306-
return nullptr;
322+
return std::make_pair(nullptr, "No Impeller context or buffer available");
307323
}
308324
const auto image_info = bitmap->info();
309325
const auto pixel_format =
310326
impeller::skia_conversions::ToPixelFormat(image_info.colorType());
311327
if (!pixel_format) {
312-
FML_DLOG(ERROR) << "Pixel format unsupported by Impeller.";
313-
return nullptr;
328+
std::string decode_error("Pixel format unsupported by Impeller.");
329+
FML_DLOG(ERROR) << decode_error;
330+
return std::make_pair(nullptr, decode_error);
314331
}
315332

316333
impeller::TextureDescriptor texture_descriptor;
@@ -323,8 +340,9 @@ sk_sp<DlImage> ImageDecoderImpeller::UploadTextureToShared(
323340
auto texture =
324341
context->GetResourceAllocator()->CreateTexture(texture_descriptor);
325342
if (!texture) {
326-
FML_DLOG(ERROR) << "Could not create Impeller texture.";
327-
return nullptr;
343+
std::string decode_error("Could not create Impeller texture.");
344+
FML_DLOG(ERROR) << decode_error;
345+
return std::make_pair(nullptr, decode_error);
328346
}
329347

330348
auto mapping = std::make_shared<fml::NonOwnedMapping>(
@@ -334,38 +352,44 @@ sk_sp<DlImage> ImageDecoderImpeller::UploadTextureToShared(
334352
);
335353

336354
if (!texture->SetContents(mapping)) {
337-
FML_DLOG(ERROR) << "Could not copy contents into Impeller texture.";
338-
return nullptr;
355+
std::string decode_error("Could not copy contents into Impeller texture.");
356+
FML_DLOG(ERROR) << decode_error;
357+
return std::make_pair(nullptr, decode_error);
339358
}
340359

341360
texture->SetLabel(impeller::SPrintF("ui.Image(%p)", texture.get()).c_str());
342361

343362
if (texture_descriptor.mip_count > 1u && create_mips) {
344363
auto command_buffer = context->CreateCommandBuffer();
345364
if (!command_buffer) {
346-
FML_DLOG(ERROR)
347-
<< "Could not create command buffer for mipmap generation.";
348-
return nullptr;
365+
std::string decode_error(
366+
"Could not create command buffer for mipmap generation.");
367+
FML_DLOG(ERROR) << decode_error;
368+
return std::make_pair(nullptr, decode_error);
349369
}
350370
command_buffer->SetLabel("Mipmap Command Buffer");
351371

352372
auto blit_pass = command_buffer->CreateBlitPass();
353373
if (!blit_pass) {
354-
FML_DLOG(ERROR) << "Could not create blit pass for mipmap generation.";
355-
return nullptr;
374+
std::string decode_error(
375+
"Could not create blit pass for mipmap generation.");
376+
FML_DLOG(ERROR) << decode_error;
377+
return std::make_pair(nullptr, decode_error);
356378
}
357379
blit_pass->SetLabel("Mipmap Blit Pass");
358380
blit_pass->GenerateMipmap(texture);
359381

360382
blit_pass->EncodeCommands(context->GetResourceAllocator());
361383
if (!command_buffer->SubmitCommands()) {
362-
FML_DLOG(ERROR) << "Failed to submit blit pass command buffer.";
363-
return nullptr;
384+
std::string decode_error("Failed to submit blit pass command buffer.");
385+
FML_DLOG(ERROR) << decode_error;
386+
return std::make_pair(nullptr, decode_error);
364387
}
365388
command_buffer->WaitUntilScheduled();
366389
}
367390

368-
return impeller::DlImageImpeller::Make(std::move(texture));
391+
return std::make_pair(impeller::DlImageImpeller::Make(std::move(texture)),
392+
std::string());
369393
}
370394

371395
// |ImageDecoder|
@@ -382,10 +406,10 @@ void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
382406
ImageResult result = [p_result, //
383407
raw_descriptor, //
384408
ui_runner = runners_.GetUITaskRunner() //
385-
](auto image) {
386-
ui_runner->PostTask([raw_descriptor, p_result, image]() {
409+
](auto image, std::string decode_error) {
410+
ui_runner->PostTask([raw_descriptor, p_result, image, decode_error]() {
387411
raw_descriptor->Release();
388-
p_result(std::move(image));
412+
p_result(std::move(image), std::move(decode_error));
389413
});
390414
};
391415

@@ -398,7 +422,7 @@ void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
398422
supports_wide_gamut = supports_wide_gamut_ //
399423
]() {
400424
if (!context) {
401-
result(nullptr);
425+
result(nullptr, "No Impeller context is available");
402426
return;
403427
}
404428
auto max_size_supported =
@@ -408,21 +432,24 @@ void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
408432
auto bitmap_result = DecompressTexture(
409433
raw_descriptor, target_size, max_size_supported,
410434
supports_wide_gamut, context->GetResourceAllocator());
411-
if (!bitmap_result.has_value()) {
412-
result(nullptr);
435+
if (!bitmap_result.device_buffer) {
436+
result(nullptr, bitmap_result.decode_error);
413437
return;
414438
}
415439
auto upload_texture_and_invoke_result = [result, context,
416-
bitmap_result =
417-
bitmap_result.value()]() {
418-
// TODO(jonahwilliams): remove ifdef once blit from buffer to texture is
419-
// implemented on other platforms.
440+
bitmap_result]() {
441+
// TODO(jonahwilliams): remove ifdef once blit from buffer to
442+
// texture is implemented on other platforms.
443+
sk_sp<DlImage> image;
444+
std::string decode_error;
420445
#if (FML_OS_IOS && !TARGET_IPHONE_SIMULATOR)
421-
result(UploadTextureToPrivate(context, bitmap_result.device_buffer,
422-
bitmap_result.image_info));
446+
std::tie(image, decode_error) = UploadTextureToPrivate(
447+
context, bitmap_result.device_buffer, bitmap_result.image_info);
423448
#else
424-
result(UploadTextureToShared(context, bitmap_result.sk_bitmap));
449+
std::tie(image, decode_error) =
450+
UploadTextureToShared(context, bitmap_result.sk_bitmap);
425451
#endif
452+
result(image, decode_error);
426453
};
427454
// TODO(jonahwilliams): https://github.com/flutter/flutter/issues/123058
428455
// Technically we don't need to post tasks to the io runner, but without

0 commit comments

Comments
 (0)