From 82097a42680e1804d09562d76e7f16ed44b4addc Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 14 Jan 2019 09:39:03 -0800 Subject: [PATCH] Move Picture.toImage rasterization to the GPU thread (#7442) Reuses the implementation that was previously done for Scene.toImage (see https://github.com/flutter/engine/commit/20c805c97312fb69e67984847d031339242a9924) This introduces a breaking API change: Picture.toImage is now asynchronous and returns a Future Fixes https://github.com/flutter/flutter/issues/23621 --- lib/ui/compositing/scene.cc | 74 +---------------------------- lib/ui/painting.dart | 10 +++- lib/ui/painting/picture.cc | 92 ++++++++++++++++++++++++++++++++++--- lib/ui/painting/picture.h | 9 +++- 4 files changed, 105 insertions(+), 80 deletions(-) diff --git a/lib/ui/compositing/scene.cc b/lib/ui/compositing/scene.cc index 8d3537750f5f..3ad02d3867cb 100644 --- a/lib/ui/compositing/scene.cc +++ b/lib/ui/compositing/scene.cc @@ -4,17 +4,15 @@ #include "flutter/lib/ui/compositing/scene.h" -#include "flutter/fml/make_copyable.h" #include "flutter/fml/trace_event.h" #include "flutter/lib/ui/painting/image.h" +#include "flutter/lib/ui/painting/picture.h" #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkSurface.h" #include "third_party/tonic/converter/dart_converter.h" #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" #include "third_party/tonic/dart_library_natives.h" -#include "third_party/tonic/dart_persistent_value.h" -#include "third_party/tonic/logging/dart_invoke.h" namespace blink { @@ -57,85 +55,17 @@ Dart_Handle Scene::toImage(uint32_t width, uint32_t height, Dart_Handle raw_image_callback) { TRACE_EVENT0("flutter", "Scene::toImage"); - if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { - return tonic::ToDart("Image callback was invalid"); - } if (!m_layerTree) { return tonic::ToDart("Scene did not contain a layer tree."); } - if (width == 0 || height == 0) { - return tonic::ToDart("Image dimensions for scene were invalid."); - } - - auto* dart_state = UIDartState::Current(); - auto image_callback = std::make_unique( - dart_state, raw_image_callback); - auto unref_queue = dart_state->GetSkiaUnrefQueue(); - auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner(); - auto gpu_task_runner = dart_state->GetTaskRunners().GetGPUTaskRunner(); - auto snapshot_delegate = dart_state->GetSnapshotDelegate(); - - // We can't create an image on this task runner because we don't have a - // graphics context. Even if we did, it would be slow anyway. Also, this - // thread owns the sole reference to the layer tree. So we flatten the layer - // tree into a picture and use that as the thread transport mechanism. - - auto picture_bounds = SkISize::Make(width, height); auto picture = m_layerTree->Flatten(SkRect::MakeWH(width, height)); - if (!picture) { - // Already in Dart scope. return tonic::ToDart("Could not flatten scene into a layer tree."); } - auto ui_task = fml::MakeCopyable([ui_task_runner, - image_callback = std::move(image_callback), - unref_queue]( - sk_sp raster_image) mutable { - // Send the raster image back to the UI thread for submission to the - // framework. - ui_task_runner->PostTask(fml::MakeCopyable([raster_image, - image_callback = - std::move(image_callback), - unref_queue]() mutable { - auto dart_state = image_callback->dart_state().lock(); - if (!dart_state) { - // The root isolate could have died in the meantime. - return; - } - tonic::DartState::Scope scope(dart_state); - - if (!raster_image) { - tonic::DartInvoke(image_callback->Get(), {Dart_Null()}); - return; - } - - auto dart_image = CanvasImage::Create(); - dart_image->set_image({std::move(raster_image), std::move(unref_queue)}); - auto* raw_dart_image = tonic::ToDart(std::move(dart_image)); - - // All done! - tonic::DartInvoke(image_callback->Get(), {raw_dart_image}); - })); - }); - - auto gpu_task = fml::MakeCopyable([gpu_task_runner, picture, picture_bounds, - snapshot_delegate, ui_task]() { - gpu_task_runner->PostTask([snapshot_delegate, picture, picture_bounds, - ui_task]() { - // Snapshot the picture on the GPU thread. This thread has access to the - // GPU contexts that may contain the sole references to texture backed - // images in the picture. - ui_task(snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds)); - }); - }); - - // Kick things off on the GPU. - gpu_task(); - - return Dart_Null(); + return Picture::RasterizeToImage(picture, width, height, raw_image_callback); } std::unique_ptr Scene::takeLayerTree() { diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index ee71d37bdd12..86a814e5bf09 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3593,7 +3593,15 @@ class Picture extends NativeFieldWrapperClass2 { /// /// Although the image is returned synchronously, the picture is actually /// rasterized the first time the image is drawn and then cached. - Image toImage(int width, int height) native 'Picture_toImage'; + Future toImage(int width, int height) { + if (width <= 0 || height <= 0) + throw new Exception('Invalid image dimensions.'); + return _futurize( + (_Callback callback) => _toImage(width, height, callback) + ); + } + + String _toImage(int width, int height, _Callback callback) native 'Picture_toImage'; /// Release the resources used by this object. The object is no longer usable /// after this method is called. diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index e65d1368d8a4..b6b314c7f54b 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -4,6 +4,7 @@ #include "flutter/lib/ui/painting/picture.h" +#include "flutter/fml/make_copyable.h" #include "flutter/lib/ui/painting/canvas.h" #include "flutter/lib/ui/ui_dart_state.h" #include "third_party/skia/include/core/SkImage.h" @@ -11,6 +12,8 @@ #include "third_party/tonic/dart_args.h" #include "third_party/tonic/dart_binding_macros.h" #include "third_party/tonic/dart_library_natives.h" +#include "third_party/tonic/dart_persistent_value.h" +#include "third_party/tonic/logging/dart_invoke.h" namespace blink { @@ -32,12 +35,14 @@ Picture::Picture(flow::SkiaGPUObject picture) Picture::~Picture() = default; -fml::RefPtr Picture::toImage(int width, int height) { - fml::RefPtr image = CanvasImage::Create(); - image->set_image(UIDartState::CreateGPUObject(SkImage::MakeFromPicture( - picture_.get(), SkISize::Make(width, height), nullptr, nullptr, - SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB()))); - return image; +Dart_Handle Picture::toImage(uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback) { + if (!picture_.get()) { + return tonic::ToDart("Picture is null"); + } + + return RasterizeToImage(picture_.get(), width, height, raw_image_callback); } void Picture::dispose() { @@ -52,4 +57,79 @@ size_t Picture::GetAllocationSize() { } } +Dart_Handle Picture::RasterizeToImage(sk_sp picture, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback) { + if (Dart_IsNull(raw_image_callback) || !Dart_IsClosure(raw_image_callback)) { + return tonic::ToDart("Image callback was invalid"); + } + + if (width == 0 || height == 0) { + return tonic::ToDart("Image dimensions for scene were invalid."); + } + + auto* dart_state = UIDartState::Current(); + auto image_callback = std::make_unique( + dart_state, raw_image_callback); + auto unref_queue = dart_state->GetSkiaUnrefQueue(); + auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner(); + auto gpu_task_runner = dart_state->GetTaskRunners().GetGPUTaskRunner(); + auto snapshot_delegate = dart_state->GetSnapshotDelegate(); + + // We can't create an image on this task runner because we don't have a + // graphics context. Even if we did, it would be slow anyway. Also, this + // thread owns the sole reference to the layer tree. So we flatten the layer + // tree into a picture and use that as the thread transport mechanism. + + auto picture_bounds = SkISize::Make(width, height); + + auto ui_task = fml::MakeCopyable([ui_task_runner, + image_callback = std::move(image_callback), + unref_queue]( + sk_sp raster_image) mutable { + // Send the raster image back to the UI thread for submission to the + // framework. + ui_task_runner->PostTask(fml::MakeCopyable([raster_image, + image_callback = + std::move(image_callback), + unref_queue]() mutable { + auto dart_state = image_callback->dart_state().lock(); + if (!dart_state) { + // The root isolate could have died in the meantime. + return; + } + tonic::DartState::Scope scope(dart_state); + + if (!raster_image) { + tonic::DartInvoke(image_callback->Get(), {Dart_Null()}); + return; + } + + auto dart_image = CanvasImage::Create(); + dart_image->set_image({std::move(raster_image), std::move(unref_queue)}); + auto* raw_dart_image = tonic::ToDart(std::move(dart_image)); + + // All done! + tonic::DartInvoke(image_callback->Get(), {raw_dart_image}); + })); + }); + + auto gpu_task = fml::MakeCopyable([gpu_task_runner, picture, picture_bounds, + snapshot_delegate, ui_task]() { + gpu_task_runner->PostTask([snapshot_delegate, picture, picture_bounds, + ui_task]() { + // Snapshot the picture on the GPU thread. This thread has access to the + // GPU contexts that may contain the sole references to texture backed + // images in the picture. + ui_task(snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds)); + }); + }); + + // Kick things off on the GPU. + gpu_task(); + + return Dart_Null(); +} + } // namespace blink diff --git a/lib/ui/painting/picture.h b/lib/ui/painting/picture.h index c95afd30f1f7..c1ff32a9bd7e 100644 --- a/lib/ui/painting/picture.h +++ b/lib/ui/painting/picture.h @@ -27,7 +27,9 @@ class Picture : public RefCountedDartWrappable { sk_sp picture() const { return picture_.get(); } - fml::RefPtr toImage(int width, int height); + Dart_Handle toImage(uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); void dispose(); @@ -35,6 +37,11 @@ class Picture : public RefCountedDartWrappable { static void RegisterNatives(tonic::DartLibraryNatives* natives); + static Dart_Handle RasterizeToImage(sk_sp picture, + uint32_t width, + uint32_t height, + Dart_Handle raw_image_callback); + private: explicit Picture(flow::SkiaGPUObject picture);