-
Notifications
You must be signed in to change notification settings - Fork 6k
Platform View implemenation for Metal #11070
Conversation
This is ready for review. It still needs a test, which I'll work on as we go through review. |
@@ -27,22 +27,27 @@ Animator::Animator(Delegate& delegate, | |||
waiter_(std::move(waiter)), | |||
last_begin_frame_time_(), | |||
dart_frame_deadline_(0), | |||
#if FLUTTER_SHELL_ENABLE_METAL | |||
layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't an issue on Metal, I believe because we're controlling when we actually present the frame so that it always happens before the transaction is committed.
Can we do that on OpenGL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the issue was identified when using OpenGL. IIRC this was a speculative fix for the keyboard lag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - but the keyboard lag doesn't happen on metal. Without the wait added in this PR for metal, similar issues are seen but much worse.
shell/gpu/gpu_surface_metal.mm
Outdated
[command_buffer.get() commit]; | ||
if (!wait) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC, presentDrawable
is just waiting (asynchronously) for the command buffer to be scheduled and the and then presenting the same. Since you want the wait to happen now, the following code should be equivalent and more readable.
if (wait) {
[command_buffer waitUntilScheduled];
}
[command_buffer presentDrawable:drawable];
Nit: Just a suggestion that I found more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little concerned that presentDrawable
could end up unexpectedly yielding - the documentation on presentsWithTransaction
says to use present
instead of presentDrawable
for this reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just had to update this - the current formulation doesn't work, we have to call the presentDrawable
before we commit otherwise it throws an exception sometimes.
@@ -33,6 +33,10 @@ class IOSSurface { | |||
|
|||
virtual std::unique_ptr<Surface> CreateGPUSurface() = 0; | |||
|
|||
// Creates a secondary surface using the supplied GrContext, if supported. | |||
// Otherwise, the same as CreateGPUSurface. | |||
virtual std::unique_ptr<Surface> CreateGPUSurface(GrContext* gr_context) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just remove the no argument CreateGPUSurface
and make the argument nullable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
pending_frame_semaphore_(1), | ||
frame_number_(1), | ||
paused_(false), | ||
regenerate_layer_tree_(false), | ||
frame_scheduled_(false), | ||
notify_idle_task_id_(0), | ||
dimension_change_pending_(false), | ||
weak_factory_(this) {} | ||
weak_factory_(this) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This may require a clang-format
pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how clang-format is putting it - maybe it's confused by the preprocessor macros?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nits. All minor. Otherwise looks good. Thanks!
shell/gpu/gpu_surface_metal.mm
Outdated
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction | ||
// TODO(dnfield): only do this if transactions are actually being used. | ||
// https://github.com/flutter/flutter/issues/24133 | ||
bool wait = delegate_->GetExternalViewEmbedder() != nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: may be clearer if we call this hasExternalViewEmbedder
and move the comment above the if
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
[command_buffer.get() commit]; | ||
} else { | ||
[command_buffer.get() commit]; | ||
[command_buffer.get() waitUntilScheduled]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to not block here but instead finish rasterizing, and then do all the waits, present, and commit the transaction?
[Not asking to do it in this PR, just wondering whether it is possible and whether we think it can be more performant]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should do as much work as we possibly can before getting the next drawable - guidelines from Apple say to hold it for as little time as possible. But if we use transactions, we have to block before presenting so that we are sure to present in the transaction. Alternatively, we have to make sure to not commit until we've presented in a callback.
} else if ([self.layer isKindOfClass:[CAMetalLayer class]]) { | ||
fml::scoped_nsobject<CAMetalLayer> metalLayer( | ||
reinterpret_cast<CAMetalLayer*>([self.layer retain])); | ||
if (@available(iOS 8.0, *)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
woohoo
} else if ([self.layer isKindOfClass:[CAMetalLayer class]]) { | ||
fml::scoped_nsobject<CAMetalLayer> metalLayer( | ||
reinterpret_cast<CAMetalLayer*>([self.layer retain])); | ||
if (@available(iOS 8.0, *)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need a guard for iOS 8 features?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not, but it doesn't hurt either right? I can remove if you want.
overlays_[overlay_id] = std::make_unique<FlutterPlatformViewLayer>( | ||
fml::scoped_nsobject<UIView>(overlay_view), std::move(ios_surface), std::move(surface)); | ||
std::move(overlay_view), std::move(ios_surface), std::move(surface)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading this code again I realize we have a bug here (we had it before this change): If we have multiple overlays and the gr_context is changed we only update it for the first overlay (as we immediately follow by updating overlays_gr_context_). We should probably instead update overlays_gr_context after iterating on composition_order_
.
FML_DCHECK(flutter_view_); | ||
|
||
auto overlay_it = overlays_.find(overlay_id); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The flow for this function is getting a little confusing, how about we do:
EnsureGrContextIsCurrent(overlay_id, gr_context);
if (overlays_.count(overlay_id) != 0) {
return;
}
// single copy of the overlay view creation code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We create the overlay view differently if there is or is not a GrContext, and in the software backend there is none.
@@ -63,6 +63,7 @@ struct FlutterPlatformViewLayer { | |||
fml::scoped_nsobject<UIView> overlay_view; | |||
std::unique_ptr<IOSSurface> ios_surface; | |||
std::unique_ptr<Surface> surface; | |||
void UpdateSurface(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to implement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed - I thought I'd need this and didn't.
@@ -31,7 +31,10 @@ class IOSSurface { | |||
|
|||
virtual void UpdateStorageSizeIfNecessary() = 0; | |||
|
|||
virtual std::unique_ptr<Surface> CreateGPUSurface() = 0; | |||
// Creates a secondary surface using the supplied GrContext, if supported. | |||
// The gr_context argument may be nullptr. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: mention that it creates a new GrContext if gr_context
is null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated this comment to make more sense.
git@github.com:flutter/engine.git/compare/dfd281bcaefe...00b72a1 git log dfd281b..00b72a1 --no-merges --oneline 2019-08-23 50503328+flar@users.noreply.github.com Add tracing of the number of frames in flight in the pipeline. (flutter/engine#11423) 2019-08-23 dnfield@google.com Platform View implemenation for Metal (flutter/engine#11070) 2019-08-23 bkonyi@google.com Roll src/third_party/dart 2e4c89aa73..ef07c76302 (5 commits) The AutoRoll server is located here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md If the roll is causing failures, please contact the current sheriff (liyuqian@google.com), and stop the roller if necessary.
Having 2 or more platform views simultaneously in the layer tree was crashing immediately on iOS with GL backend. This regressed in #11070 which passed gl_context to a function in a loop using std::move (which meant on the second iteration the caller is no longer the owner of the field). I added a scenarios_app test, though this test doesn't run on a physical device on CI so it would have only caught the problem when running locally (flutter/flutter#43852).
…13449) Having 2 or more platform views simultaneously in the layer tree was crashing immediately on iOS with GL backend. This regressed in flutter#11070 which passed gl_context to a function in a loop using std::move (which meant on the second iteration the caller is no longer the owner of the field). I added a scenarios_app test, though this test doesn't run on a physical device on CI so it would have only caught the problem when running locally (flutter/flutter#43852).
I've refactored bits and pieces to try to make more sense of this - in particular, some of the different methods for GL vs not GL have been merged and may do nothing on Software.
Fixes flutter/flutter#33487