Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement toGpuImage, a synchronous, GPU-resident version of #33736

Merged
merged 28 commits into from Jun 24, 2022

Conversation

dnfield
Copy link
Contributor

@dnfield dnfield commented Jun 1, 2022

Picture.toImage.

Fixes flutter/flutter#13498
Fixes flutter/flutter#77289
fixes flutter/flutter#106381

Some additional context at flutter/flutter#40990

This method kicks off asynchronous work on the raster task runner.
If it fails to rasterize, it will synchronously throw later when
the user attempts to draw to a canvas.

This supports several use cases:

  • Quickly snapping off an expensive-to-rasterize image for reuse
    across multiple frames.
  • Applying multi-pass filters to a render target.

This patch amends flutter_tester so that it can produce an image
object, but that image will always be a four square checkerboard of white and light grey.

Adds support for CanvasKit on Web, which basically already used
this method for its Picture.toImage implementation.

Throws an UnsupportedError for HTML on Web, since any implementation
there would almost certainly be slower than drawPicture.

Starting as a draft to run tests.

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or Hixie said the PR is test-exempt. See testing the engine for instructions on
    writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@flutter-dashboard flutter-dashboard bot added the platform-web Code specifically for the web engine label Jun 1, 2022
Picture.toImage.

This method kicks off asynchronous work on the raster task runner.
If it fails to rasterize, it will synchronously throw later when
the user attempts to draw to a canvas.

This supports several use cases:

- Quickly snapping off an expensive-to-rasterize image for reuse
  across multiple frames.
- Applying multi-pass filters to a render target.

This patch amends flutter_tester so that it can produce an image
object, but that image will always be transparent black pixels.

Adds support for CanvasKit on Web, which basically already used
this method for its Picture.toImage implementation.

Throws an UnsupportedError for HTML on Web, since any implementation
there would almost certainly be slower than drawPicture.
@dnfield
Copy link
Contributor Author

dnfield commented Jun 1, 2022

This won't work because of the mock gr context. Need to fix that. Going to close this in the mean time.

@dnfield dnfield closed this Jun 1, 2022
@dnfield dnfield reopened this Jun 21, 2022
@dnfield
Copy link
Contributor Author

dnfield commented Jun 21, 2022

Ok. On flutter_tester toGpuImage now just emits a checkerboard image.

The following things remain around this:

  • Implement for Scene. I skipped this because Scene currently uses SkPicture directly and I'd rather avoid implementing API compatible with this, but would also rather avoid fixing Remove Engine flag for disabling the DisplayList flutter#86634 in this patch.
  • Implement for ImageShader. Should be doable but this patch is already relatively large.
  • Move image creation to the IO thread. Should be doable but requires some more synchronization and is probably fine as a follow up patch.

///
/// In the flutter_tester, this will always created a light gray and white
/// checkerboard bitmap with the requested dimensions.
Image toGpuImage(int width, int height) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be toTexture?

Is that term overloaded?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its all quite overloaded.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it worth clarifying here (and elsewhere) that these are physical pixels and not the dp used in the framework?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't document this elsewhere - I think generally all the dart:ui API works in physical pixels, the framework introduces logical pixels.

@@ -5871,3 +5905,22 @@ Future<T> _futurize<T>(_Callbacker<T> callbacker) {
throw Exception(error);
return completer.future;
}

class PictureRasterizationException implements Exception {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be its own exception type, or can use use Exception for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want this to be something you can try to specifically catch, with the idea being that this API could potentially be grown to support more of an idea of what kind of exception was thrown (particularly if you tried to make a texture larger than the user's GPU supports)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where would you put the catch though?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Around calls to canvas.drawImage, or whatever else calls it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would give this a documentation comment at least describing the intent

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@skia-gold
Copy link

Gold has detected about 1 new digest(s) on patchset 7.
View them at https://flutter-engine-gold.skia.org/cl/github/33736

@dnfield dnfield marked this pull request as ready for review June 22, 2022 00:09
@skia-gold
Copy link

Gold has detected about 1 new digest(s) on patchset 9.
View them at https://flutter-engine-gold.skia.org/cl/github/33736

@flutter-dashboard
Copy link

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

Changes reported for pull request #33736 at sha f727e50

@@ -5871,3 +5907,30 @@ Future<T> _futurize<T>(_Callbacker<T> callbacker) {
throw Exception(error);
return completer.future;
}

/// An exception thrown by [Canvas.drawImage] and realted methods when drawing
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// An exception thrown by [Canvas.drawImage] and realted methods when drawing
/// An exception thrown by [Canvas.drawImage] and related methods when drawing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


if (image->image()->owning_context() != DlImage::OwningContext::kIO) {
matrix4.Release();
// TODO(dnfield): it should be possible to support this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm extremely excited about this

// On a slower CI machine, the raster thread may get behind the UI thread
// here. However, once the image is in an error state it will immediately
// throw on subsequent attempts.
while (true) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me a bit uncomfortable, I'd be worried about the scenario where we break the error reporting somehow and this shard times out instead of fails.

If this had a max iteration count + perhaps a delay between drawImage attempts that might prevent that scenario, assuming there is no other way to stabilize it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to introduce a delay - the await null is just to turn the event loop. I'm fine with having a max iteration count though. I thought we had timeouts on these tests though - somewhere in litetest or something it should just eventually say the test failed due to timeout.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah as long as it times out before the CI shard times out, that is fine. The await null will run the next iteration in another microtask which not be enough, you need a non-zero duration await to get a full event loop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL. I'll add a 1ms delay.

Copy link
Member

@jonahwilliams jonahwilliams left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall LGTM

@skia-gold
Copy link

Gold has detected about 2 new digest(s) on patchset 24.
View them at https://flutter-engine-gold.skia.org/cl/github/33736

@flutter-dashboard
Copy link

Golden file changes are available for triage from new commit, Click here to view.

Changes reported for pull request #33736 at sha db97f79

}

// |DlImage|
size_t DlDeferredImageGPU::GetApproximateByteSize() const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be invoked on the UI thread, and it's reading image_ (which is written on the raster thread).

Can this compute an estimate based on size_? That would avoid potential races.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

TRACE_EVENT0("flutter", "Rasterizer::MakeGpuImage");
FML_DCHECK(display_list);

switch (gpu_image_behavior_) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified to:

if (gpu_image_behavior_ == MakeGpuImageBehavior::kBitmap) {
  return MakeBitmapImage(picture_size);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to avoid == for enums

@dnfield dnfield added the autosubmit Merge PR when tree becomes green via auto submit App label Jun 24, 2022
@skia-gold
Copy link

Gold has detected about 1 new digest(s) on patchset 26.
View them at https://flutter-engine-gold.skia.org/cl/github/33736

@skia-gold
Copy link

Gold has detected about 1 new digest(s) on patchset 28.
View them at https://flutter-engine-gold.skia.org/cl/github/33736

@dnfield dnfield merged commit e752095 into flutter:main Jun 24, 2022
@dnfield dnfield deleted the gpu_promise_img branch June 24, 2022 23:14
@fzyzcjy
Copy link
Contributor

fzyzcjy commented Jun 24, 2022

🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App platform-web Code specifically for the web engine will affect goldens
Projects
None yet
5 participants