-
Notifications
You must be signed in to change notification settings - Fork 6k
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
Smooth window resizing on macOS #21525
Smooth window resizing on macOS #21525
Conversation
It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat. Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
d28d64b
to
d97ebaa
Compare
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.
Left some initial feedback, still need to look at FlutterResizeSynchronizer
and the cond_var notification mechanism.
|
||
@end | ||
|
||
// Encapsulates the logic for blocking platform thread during window resize |
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.
It will be document how the member functions of this interface need to be run. Some of this is mentioned in beginResize
, but some questions that would likely need to be answered by the docs are:
-
What if
beginResize
gets called and only one ofshouldEnsureSurfaceForSize
orrequestCommit
is called? Should one of them be called before the other? -
When the platform thread is blocked, is it blocked with a timeout? What happens when we exceed the said timeout?
-
beginResize
seems to indicate that there is anendResize
but instead we haverequestCommit
, should we call itendResize
instead?
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'll document it in class comment
-
There is no timeout. No sure if there needs to be one. It seems that if you rely on timeout here, something must have gone wrong already. In which case it's probably better to fail early be locking up, than "sort-of work".
-
requestCommit
is called out of resize as well. There is another thingFlutterResizeSynchronizer
does - it ensures that delegate commit method is called on platform thread, while raster thread waits inrequestCommit
. It could be that the class needs more generic name, since it's not just resizing that it synchronizes.
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.
Documented here.
shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.mm
Show resolved
Hide resolved
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[0]); | ||
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _backingTexture[0]); | ||
glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 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.
Create a private method createTextureAndBindToFBO
and re-use for both front and back buffers.
layer = layer_; | ||
openGLContext = opengLContext_; | ||
|
||
NSOpenGLContext* prev = [NSOpenGLContext currentContext]; |
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 are doing this pattern of
- Save old context.
- Make context current.
- Reset old context or clear it.
This is being done at a couple of places in this class. This is very reminiscent of GLContextSwitch
. Can we use that wrapper here or create an RAII wrapper?
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.
Added MacOSSwitchableOpenGLContext.
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[0]); | ||
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _backingTexture[0]); | ||
glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
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.
It might also be worth doing:
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
To avoid having the default which is GL_REPEAT
.
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 not sure this does anything give that we don't actually use these as textures anywhere, but I suppose it's not going to harm.
shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm
Outdated
Show resolved
Hide resolved
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); | ||
|
||
glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferId[i]); | ||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, |
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.
Check the status after this is done glCheckFramebufferStatus
. Maybe even have an FML_CHECK
to ensure it succeeds.
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.
Good catch. Added.
[resizeSynchronizer requestCommit]; | ||
} | ||
|
||
- (void)start { |
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 still not sure if start
is something we want outside of the init
method. Are there situations where we will want to create the FlutterView
and not start
it right away?
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 problem here is that we block the platform thread every time the view is resized, which is detected by overriding [NSView setFrameSize]
. It is normal to get some calls to setFrameSize
before the shell is running and producing frames. If we block the thread too soon, it will deadlock. So [FlutterView start]
basically tells the view that the shell is ready and running and that if it block the platform thread, a frame will come.
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.
So is this call more like: readyForSynchronousResizing
?
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.
Well, perhaps, but it also blocks the platform thread until it gets proper sized frame. I don't think that's necessary though. readyForSynchronousResizing
sounds good.
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 changed it here. [FlutterView start]
is gone, instead there is synchronousResizing
property, which when true means the view will block the platform thread during resizing. If false, resizing still works in a non blocking way.
if (active) { | ||
CGSize scaledSize = [self convertSizeToBacking:self.bounds.size]; | ||
[resizeSynchronizer beginResize:scaledSize | ||
notify:^{ |
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.
Rather than notify
what do you think of onResizeCommit
?
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.
Seems confusing. The notify block is basically invoked as soon as we get the mutex (and unblock raster thread). That's at the beginning of resizing. requestCommit
(which triggers delegate's resizeSynchronizerCommit
) is when raster thread wants to present frame.
So presenting frame is called commit here, not sure why starting the resizing process (which notify does) would also be called commit.
[CATransaction begin]; | ||
[CATransaction setDisableActions:YES]; | ||
self.layer.frame = self.bounds; | ||
self.layer.sublayerTransform = CATransform3DTranslate(CATransform3DMakeScale(1, -1, 1), 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.
Maybe add a comment explaining why this transform is needed?
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 thought about this, and the transform is basically needed because we're rendering to an OpenGL texture, which are flipped. The fact, that we're rendering to an OpenGL texture / IOSurface is an implementation detail of FlutterSurfaceManager, and thus the surface manager should take care of flipping the layer, just like it takes care of settings layer contents. I updated the behavior here.
|
||
waiting = true; | ||
|
||
condBlock.wait(lock); |
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.
Better to wait with a condition here. Maybe the condition here is []() { return pendingCommit; }
.
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.
Lets do it for all wait
calls in this class.
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 version before that was running the thread loop didn't have this problem. Definitely need to check for spurious wake-up.
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.
Should be fixed here: d046e86
|
||
condBlock.wait(lock); | ||
|
||
if (pendingCommit) { |
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 shouldn't ever be false.
@interface FlutterResizeSynchronizer () { | ||
uint32_t cookie; // counter to detect stale callbacks | ||
std::mutex mutex; | ||
std::condition_variable condRun; |
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.
What about renaming:
condBlock
->pendingCommits
condRun
->noPendingCommits
28895d2
to
d046e86
Compare
I added another commit that separates flush and commit. There's no reason to flush the OpenGL context on platform tread, so this commit moves it to raster thread. |
if (waiting) { // BeginResize is in progress, interrupt it and schedule commit call | ||
pendingCommit = true; | ||
condPendingCommit.notify_all(); | ||
condNoPendingCommit.wait(lock, [&]() { return !pendingCommit; }); |
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.
move this outside of the if {} else {}
block given that we always have to wait for pending commits to be done in requestCommit
.
07dbce0
to
c32f0cd
Compare
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.
LGTM modulo the some minor comments.
- (void)swapBuffers { | ||
contentLayer.frame = layer.bounds; | ||
|
||
contentLayer.transform = CATransform3DMakeScale(1, -1, 1); |
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.
Add a comment here for why we need this transform
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.
- (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { | ||
[CATransaction begin]; | ||
[CATransaction setDisableActions:YES]; | ||
self.layer.frame = self.bounds; |
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 you add a comment explaining why this is needed? Specifically why setFrameSize:
doesn't do 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.
Actually, the root layer should be auto-resized with the view, I think it is safe to remove the self.layer.frame = self.bounds;
call. I'll look into it.
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 removed the assignment.
|
||
- (void)resizeSynchronizerCommit:(FlutterResizeSynchronizer*)synchronizer { | ||
[CATransaction begin]; | ||
[CATransaction setDisableActions:YES]; |
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.
Why do we need to disable the actions here?
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.
Without this every change will have implicit animation added to it. So if you set bounds for example the bounds change will be animated, which is not something you want during resizing.
Can you please amend the licenses file:
|
@knopp there are some failures still. I think some of them will be fixed by a rebase. Can you rebase on-top of master? Thanks! |
Flush can be performed on raster thread
* 53d5d68 Add dart-lang/sdk's new package:clock dependency (flutter/engine#22142) * c32e3d8 Roll Skia from 7737a5bd2510 to 5567a6091ceb (8 revisions) (flutter/engine#22146) * 376045c Roll Fuchsia Linux SDK from XYN02FThN... to UKgKCjxSA... (flutter/engine#22147) * 03395de Roll Fuchsia Mac SDK from GKPwGj1Ux... to xHjtLQPQ5... (flutter/engine#22151) * 0c7e952 Manual Dart SDK roll 6e015bd9cddb to 9c6e76468ca4 (6 revisions (flutter/engine#22153) * e5f168a Update constraint to latest integration test (flutter/engine#22169) * e61e8c2 Smooth window resizing on macOS (flutter/engine#21525) * acece00 Allow parse_and_send to use access tokens (flutter/engine#22019) * 0270632 Includes roles for links, checkboxes, and radio buttons in semantics (flutter/engine#22061) * 632354d Roll Fuchsia Linux SDK from UKgKCjxSA... to PY5hNI-wY... (flutter/engine#22175) * 62d50af Add plumbing for command line arguments on Windows (flutter/engine#22094) * 06b0910 Fix possible use of std::moved value in Rasterizer (flutter/engine#22125) * 005dec4 [web] Clean up unused previousStyle logic (flutter/engine#22150) * ca05bdc Roll Skia from 5567a6091ceb to f548a028ce70 (7 revisions) (flutter/engine#22155) * 5b07ac4 Roll Fuchsia Mac SDK from xHjtLQPQ5... to ICK_JlnKJ... (flutter/engine#22188) * d615a97 Roll Fuchsia Linux SDK from PY5hNI-wY... to Usec4YBzR... (flutter/engine#22197) * 11ed711 Invalidate the cached SkParagraph font collection when a new font manager is installed (flutter/engine#22157) * 07c780b [web] Assign default natural width/height for svgs that report 0,0 on firefox and ie11 (flutter/engine#22184) * b54bb88 Migrate runZoned to runZonedGuarded (flutter/engine#22198) * 247139a [web] Fix transform not invalidating path bounds causing debugValidate failure (flutter/engine#22172) * e4dffc1 [web] Fix scroll wheel line delta on Firefox. (flutter/engine#21928) * d6627c6 Reland [ios] Refactor IOSSurface factory and unify surface creation (flutter/engine#22016) * ce1dd11 Standardize macro names for dartdoc macros (flutter/engine#22180) * f81bc37 [profiling] Handle thread_info to account for killed threads (flutter/engine#22170) * fd94c86 Fix for firefox custom clipping (flutter/engine#22182) * 9ccf9f1 bring back build_test to ensure we validate licenses (flutter/engine#22201) * 38f6665 Set strut font to Roboto if the given font hasn't been registered (flutter/engine#22129) * caf32d5 Add a proc table version of embedder API (flutter/engine#21813) * ed0f477 Use preTranslate when applying offset to matrix (flutter/engine#21648) * b457e2d Refactor make_mock_engine into fl_test (flutter/engine#21585) * 28497c8 Fix typos in FlValue docs (flutter/engine#21875) * bb32446 Fix FlTextInputPlugin tear down (flutter/engine#22007) * 7a7804b Add "input shield" to capture pointer input for reinjection (flutter/engine#22067) * fe85f94 Update painting.dart (flutter/engine#22195) * 99cc50d [ios] Surface factory holds the canonical reference to the external view embedder (flutter/engine#22206)
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.
Thanks for this work; I only just saw that this had landed.
I've done a quick post-submit review pass since I was looking through it out of curiousity, and found a lot of style issues. Could you do a follow-up to address them?
There is also one substantive issue, which is the new use of code that isn't in embedder.h. I wish that had been raised with me before adding new dependencies that are not consistent with how any of the desktop embeddings have been written. How extensive is that code? Do we need to share it?
return 0; | ||
static uint32_t OnFBO(FlutterEngine* engine, const FlutterFrameInfo* info) { | ||
CGSize size = CGSizeMake(info->size.width, info->size.height); | ||
return [engine.viewController.flutterView getFrameBufferIdForSize:size]; |
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.
Could you update this to follow the format used in all of the other functions, where it forwards directly, with no logic, to an engine method? That structure was intentional.
|
||
@class FlutterResizeSynchronizer; | ||
|
||
@protocol FlutterResizeSynchronizerDelegate |
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.
All protocols should have declaration comments explaining their role, per style guide.
|
||
@protocol FlutterResizeSynchronizerDelegate | ||
|
||
// Invoked on raster thread; Delegate should flush the OpenGL context |
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.
All of these comments need a .
at the end, per style guide.
@protocol FlutterResizeSynchronizerDelegate | ||
|
||
// Invoked on raster thread; Delegate should flush the OpenGL context | ||
- (void)resizeSynchronizerFlush:(FlutterResizeSynchronizer*)synchronizer; |
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.
All of the macOS embedding's headers should have nullability annotations.
@@ -0,0 +1,104 @@ | |||
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterResizeSynchronizer.h" | |||
|
|||
#import <mutex> |
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.
#include
@@ -0,0 +1,21 @@ | |||
#include "flutter/flow/gl_context_switch.h" |
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 file is missing its license.
|
||
#import <Cocoa/Cocoa.h> | ||
|
||
class MacOSSwitchableGLContext final : public flutter::SwitchableGLContext { |
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.
All classes need declaration comments.
public: | ||
explicit MacOSSwitchableGLContext(NSOpenGLContext* context); | ||
|
||
bool SetCurrent() override; |
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.
Please put the class name this is coming from in a comment, as is done throughout the engine codebase.
|
||
FML_DECLARE_THREAD_CHECKER(checker); | ||
|
||
FML_DISALLOW_COPY_AND_ASSIGN(MacOSSwitchableGLContext); |
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.
New code should not use this macro; it violates style. Delete the relevant methods directly.
@@ -0,0 +1,19 @@ | |||
#import "flutter/shell/platform/darwin/macos/framework/Source/MacOSSwitchableGLContext.h" |
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.
Missing license again.
No problem, I'll submit a PR for this
I think this refers to |
Right, that was referring to my comment on the /cc @chinmaygarde for thoughts on the latter; I think it's probably worth considering having a place for that kind of code. |
(I just ran with this fix for the first time, by the way, and it feels great 😃 ) |
@stuartmorgan, I have created #22270 for the code style issues. It should address all your points (unless I missed something) except for the enum not being enum class (the values are meant to be used as indices). |
Previously, FlutterSurfaceManager was a protocol with two concrete implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager. Because much of the surface manager code was shared, they had a shared superclass FlutterIOSurfaceManager that implemented the bulk of the logic and called into the subclasses when OpenGL or Metal-specific code was required. It did this via a delegate pattern where each of the surface manager subclasses implemented the FlutterIOSurfaceManagerDelegate protocol that exposed operations that required backend-specific code. Now that only the Metal implementation remains, the delegate code can be inlined into the calling functions, and the class hierarchy can be squashed into a single concrete implementation class, FlutterSurfaceManager, similar to how it was originally implemented in flutter#21525 before we had two backends. Issue: flutter/flutter#108304 Issue: flutter/flutter#114445
Previously, FlutterSurfaceManager was a protocol with two concrete implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager. Most of the implementation was in a shared superclass, FlutterIOSurfaceManager, which called into the OpenGL or Metal-specific subclass when backend-specific operations (such as allocating textures) was required. It did so via a delegate pattern, wherein the subclasses both implemented the FlutterIOSurfaceManagerDelegate protocol that exposed the backend-specific functionality. Now that only the Metal implementation remains, the delegate code can be inlined into the calling functions, and the class hierarchy can be squashed into a single concrete implementation class, FlutterSurfaceManager, similar to how it was originally implemented in flutter#21525 before we had two backends. Issue: flutter/flutter#108304 Issue: flutter/flutter#114445
Previously, FlutterSurfaceManager was a protocol with two concrete implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager. Most of the implementation was in a shared superclass, FlutterIOSurfaceManager, which called into the OpenGL or Metal-specific subclass when backend-specific operations (such as allocating textures) was required. It did so via a delegate pattern, wherein the subclasses both implemented the FlutterIOSurfaceManagerDelegate protocol that exposed the backend-specific functionality. Now that only the Metal implementation remains, the delegate code can be inlined into the calling functions, and the class hierarchy can be squashed into a single concrete implementation class, FlutterSurfaceManager, similar to how it was originally implemented in flutter#21525 before we had two backends. Issue: flutter/flutter#108304 Issue: flutter/flutter#114445
Previously, FlutterSurfaceManager was a protocol with two concrete implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager. Most of the implementation was in a shared superclass, FlutterIOSurfaceManager, which called into the OpenGL or Metal-specific subclass when backend-specific operations (such as allocating textures) was required. It did so via a delegate pattern, wherein the subclasses both implemented the FlutterIOSurfaceManagerDelegate protocol that exposed the backend-specific functionality. Now that only the Metal implementation remains, the delegate code can be inlined into the calling functions, and the class hierarchy can be squashed into a single concrete implementation class, FlutterSurfaceManager, similar to how it was originally implemented in flutter#21525 before we had two backends. Issue: flutter/flutter#108304 Issue: flutter/flutter#114445
Previously, FlutterSurfaceManager was a protocol with two concrete implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager. Most of the implementation was in a shared superclass, FlutterIOSurfaceManager, which called into the OpenGL or Metal-specific subclass when backend-specific operations (such as allocating textures) was required. It did so via a delegate pattern, wherein the subclasses both implemented the FlutterIOSurfaceManagerDelegate protocol that exposed the backend-specific functionality. Now that only the Metal implementation remains, the delegate code can be inlined into the calling functions, and the class hierarchy can be squashed into a single concrete implementation class, FlutterSurfaceManager, similar to how it was originally implemented in flutter#21525 before we had two backends. Issue: flutter/flutter#108304 Issue: flutter/flutter#114445
Previously, FlutterSurfaceManager was a protocol with two concrete implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager. Most of the implementation was in a shared superclass, FlutterIOSurfaceManager, which called into the OpenGL or Metal-specific subclass when backend-specific operations (such as allocating textures) was required. It did so via a delegate pattern, wherein the subclasses both implemented the FlutterIOSurfaceManagerDelegate protocol that exposed the backend-specific functionality. Now that only the Metal implementation remains, the delegate code can be inlined into the calling functions, and the class hierarchy can be squashed into a single concrete implementation class, FlutterSurfaceManager, similar to how it was originally implemented in flutter#21525 before we had two backends. Issue: flutter/flutter#108304 Issue: flutter/flutter#114445
Previously, FlutterSurfaceManager was a protocol with two concrete implementations: FlutterGLSurfaceManager and FlutterMetalSurfaceManager. Most of the implementation was in a shared superclass, FlutterIOSurfaceManager, which called into the OpenGL or Metal-specific subclass when backend-specific operations (such as allocating textures) was required. It did so via a delegate pattern, wherein the subclasses both implemented the FlutterIOSurfaceManagerDelegate protocol that exposed the backend-specific functionality. Now that only the Metal implementation remains, the delegate code can be inlined into the calling functions, and the class hierarchy can be squashed into a single concrete implementation class, FlutterSurfaceManager, similar to how it was originally implemented in #21525 before we had two backends. Issue: flutter/flutter#108304 Issue: flutter/flutter#114445
Description
Replaces #21252
Make window resizing on macOS smooth, responsive and synchronous with windows size.
Related design doc: https://flutter.dev/go/desktop-resize-macos
Notable change: It seems that double buffered IOSurface backed NSOpenGLContext is not all that double buffered after all and I was able to create a test case which resulted in partial drawn frame visible. This version uses single buffered NSOpenGLContext with two IOSurfaces (front and back) which eliminates the problem.
Depends on: #21108
Related Issues
flutter/flutter#44136
Tests
I added the following tests:
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]
). This will ensure a smooth and quick review process.Breaking Change
Did any tests fail when you ran them? Please read handling breaking changes.