|
12 | 12 | #include "flutter/fml/logging.h" |
13 | 13 | #include "flutter/fml/paths.h" |
14 | 14 | #include "flutter/fml/platform/win/wstring_conversion.h" |
| 15 | +#include "flutter/fml/synchronization/waitable_event.h" |
15 | 16 | #include "flutter/shell/platform/common/client_wrapper/binary_messenger_impl.h" |
16 | 17 | #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h" |
17 | 18 | #include "flutter/shell/platform/common/path_utils.h" |
@@ -149,6 +150,7 @@ FlutterWindowsEngine::FlutterWindowsEngine( |
149 | 150 | : project_(std::make_unique<FlutterProjectBundle>(project)), |
150 | 151 | windows_proc_table_(std::move(windows_proc_table)), |
151 | 152 | aot_data_(nullptr, nullptr), |
| 153 | + views_mutex_(fml::SharedMutex::Create()), |
152 | 154 | lifecycle_manager_(std::make_unique<WindowsLifecycleManager>(this)) { |
153 | 155 | if (windows_proc_table_ == nullptr) { |
154 | 156 | windows_proc_table_ = std::make_shared<WindowsProcTable>(); |
@@ -492,34 +494,118 @@ bool FlutterWindowsEngine::Stop() { |
492 | 494 |
|
493 | 495 | std::unique_ptr<FlutterWindowsView> FlutterWindowsEngine::CreateView( |
494 | 496 | std::unique_ptr<WindowBindingHandler> window) { |
495 | | - // TODO(loicsharma): Remove implicit view assumption. |
496 | | - // https://github.com/flutter/flutter/issues/142845 |
| 497 | + auto view_id = next_view_id_; |
497 | 498 | auto view = std::make_unique<FlutterWindowsView>( |
498 | | - kImplicitViewId, this, std::move(window), windows_proc_table_); |
| 499 | + view_id, this, std::move(window), windows_proc_table_); |
499 | 500 |
|
500 | 501 | view->CreateRenderSurface(); |
501 | 502 |
|
502 | | - views_[kImplicitViewId] = view.get(); |
| 503 | + next_view_id_++; |
| 504 | + |
| 505 | + { |
| 506 | + // Add the view to the embedder. This must happen before the engine |
| 507 | + // is notified the view exists and starts presenting to it. |
| 508 | + fml::UniqueLock write_lock{*views_mutex_}; |
| 509 | + FML_DCHECK(views_.find(view_id) == views_.end()); |
| 510 | + views_[view_id] = view.get(); |
| 511 | + } |
| 512 | + |
| 513 | + if (!view->IsImplicitView()) { |
| 514 | + FML_DCHECK(running()); |
| 515 | + |
| 516 | + struct Captures { |
| 517 | + fml::AutoResetWaitableEvent latch; |
| 518 | + bool added; |
| 519 | + }; |
| 520 | + Captures captures; |
| 521 | + |
| 522 | + FlutterWindowMetricsEvent metrics = view->CreateWindowMetricsEvent(); |
| 523 | + |
| 524 | + FlutterAddViewInfo info = {}; |
| 525 | + info.struct_size = sizeof(FlutterAddViewInfo); |
| 526 | + info.view_id = view_id; |
| 527 | + info.view_metrics = &metrics; |
| 528 | + info.user_data = &captures; |
| 529 | + info.add_view_callback = [](const FlutterAddViewResult* result) { |
| 530 | + Captures* captures = reinterpret_cast<Captures*>(result->user_data); |
| 531 | + captures->added = result->added; |
| 532 | + captures->latch.Signal(); |
| 533 | + }; |
| 534 | + |
| 535 | + embedder_api_.AddView(engine_, &info); |
| 536 | + |
| 537 | + // Block the platform thread until the engine has added the view. |
| 538 | + // TODO(loicsharma): This blocks the platform thread eagerly and can |
| 539 | + // cause unnecessary delay in input processing. Instead, this should block |
| 540 | + // lazily only when the app does an operation which needs the view. |
| 541 | + // https://github.com/flutter/flutter/issues/146248 |
| 542 | + captures.latch.Wait(); |
| 543 | + |
| 544 | + if (!captures.added) { |
| 545 | + // Adding the view failed. Update the embedder's state to match the |
| 546 | + // engine's state. This is unexpected and indicates a bug in the Windows |
| 547 | + // embedder. |
| 548 | + FML_LOG(ERROR) << "FlutterEngineAddView failed to add view"; |
| 549 | + fml::UniqueLock write_lock{*views_mutex_}; |
| 550 | + views_.erase(view_id); |
| 551 | + return nullptr; |
| 552 | + } |
| 553 | + } |
503 | 554 |
|
504 | 555 | return std::move(view); |
505 | 556 | } |
506 | 557 |
|
507 | 558 | void FlutterWindowsEngine::RemoveView(FlutterViewId view_id) { |
508 | 559 | FML_DCHECK(running()); |
509 | | - FML_DCHECK(views_.find(view_id) != views_.end()); |
510 | 560 |
|
511 | | - if (view_id == kImplicitViewId) { |
512 | | - // The engine and framework assume the implicit view always exists. |
513 | | - // Attempts to render to the implicit view will be ignored. |
514 | | - views_.erase(view_id); |
515 | | - return; |
| 561 | + // Notify the engine to stop rendering to the view if it isn't the implicit |
| 562 | + // view. The engine and framework assume the implicit view always exists and |
| 563 | + // can continue presenting. |
| 564 | + if (view_id != kImplicitViewId) { |
| 565 | + struct Captures { |
| 566 | + fml::AutoResetWaitableEvent latch; |
| 567 | + bool removed; |
| 568 | + }; |
| 569 | + Captures captures; |
| 570 | + |
| 571 | + FlutterRemoveViewInfo info = {}; |
| 572 | + info.struct_size = sizeof(FlutterRemoveViewInfo); |
| 573 | + info.view_id = view_id; |
| 574 | + info.user_data = &captures; |
| 575 | + info.remove_view_callback = [](const FlutterRemoveViewResult* result) { |
| 576 | + // This is invoked on the raster thread, the same thread that the present |
| 577 | + // callback is invoked. If |FlutterRemoveViewResult.removed| is `true`, |
| 578 | + // the engine guarantees the view won't be presented. |
| 579 | + Captures* captures = reinterpret_cast<Captures*>(result->user_data); |
| 580 | + captures->removed = result->removed; |
| 581 | + captures->latch.Signal(); |
| 582 | + }; |
| 583 | + |
| 584 | + embedder_api_.RemoveView(engine_, &info); |
| 585 | + |
| 586 | + // Block the platform thread until the engine has removed the view. |
| 587 | + // TODO(loicsharma): This blocks the platform thread eagerly and can |
| 588 | + // cause unnecessary delay in input processing. Instead, this should block |
| 589 | + // lazily only when an operation needs the view. |
| 590 | + // https://github.com/flutter/flutter/issues/146248 |
| 591 | + captures.latch.Wait(); |
| 592 | + |
| 593 | + if (!captures.removed) { |
| 594 | + // Removing the view failed. This is unexpected and indicates a bug in the |
| 595 | + // Windows embedder. |
| 596 | + FML_LOG(ERROR) << "FlutterEngineRemoveView failed to remove view"; |
| 597 | + return; |
| 598 | + } |
516 | 599 | } |
517 | 600 |
|
518 | | - // TODO(loicsharma): Remove the view from the engine using the |
519 | | - // `FlutterEngineRemoveView` embedder API. Windows does not |
520 | | - // support views other than the implicit view yet. |
521 | | - // https://github.com/flutter/flutter/issues/144810 |
522 | | - FML_UNREACHABLE(); |
| 601 | + { |
| 602 | + // The engine no longer presents to the view. Remove the view from the |
| 603 | + // embedder. |
| 604 | + fml::UniqueLock write_lock{*views_mutex_}; |
| 605 | + |
| 606 | + FML_DCHECK(views_.find(view_id) != views_.end()); |
| 607 | + views_.erase(view_id); |
| 608 | + } |
523 | 609 | } |
524 | 610 |
|
525 | 611 | void FlutterWindowsEngine::OnVsync(intptr_t baton) { |
@@ -551,6 +637,8 @@ std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() { |
551 | 637 | } |
552 | 638 |
|
553 | 639 | FlutterWindowsView* FlutterWindowsEngine::view(FlutterViewId view_id) const { |
| 640 | + fml::SharedLock read_lock{*views_mutex_}; |
| 641 | + |
554 | 642 | auto iterator = views_.find(view_id); |
555 | 643 | if (iterator == views_.end()) { |
556 | 644 | return nullptr; |
@@ -779,6 +867,8 @@ bool FlutterWindowsEngine::DispatchSemanticsAction( |
779 | 867 |
|
780 | 868 | void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) { |
781 | 869 | if (engine_ && semantics_enabled_ != enabled) { |
| 870 | + fml::SharedLock read_lock{*views_mutex_}; |
| 871 | + |
782 | 872 | semantics_enabled_ = enabled; |
783 | 873 | embedder_api_.UpdateSemanticsEnabled(engine_, enabled); |
784 | 874 | for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) { |
@@ -844,6 +934,8 @@ void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd, |
844 | 934 | } |
845 | 935 |
|
846 | 936 | void FlutterWindowsEngine::OnDwmCompositionChanged() { |
| 937 | + fml::SharedLock read_lock{*views_mutex_}; |
| 938 | + |
847 | 939 | for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) { |
848 | 940 | iterator->second->OnDwmCompositionChanged(); |
849 | 941 | } |
@@ -875,6 +967,11 @@ void FlutterWindowsEngine::OnChannelUpdate(std::string name, bool listening) { |
875 | 967 | } |
876 | 968 |
|
877 | 969 | bool FlutterWindowsEngine::Present(const FlutterPresentViewInfo* info) { |
| 970 | + // This runs on the raster thread. Lock the views map for the entirety of the |
| 971 | + // present operation to block the platform thread from destroying the |
| 972 | + // view during the present. |
| 973 | + fml::SharedLock read_lock{*views_mutex_}; |
| 974 | + |
878 | 975 | auto iterator = views_.find(info->view_id); |
879 | 976 | if (iterator == views_.end()) { |
880 | 977 | return false; |
|
0 commit comments