-
Notifications
You must be signed in to change notification settings - Fork 0
Perf/pointer event handling #9
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -664,8 +664,15 @@ void CompositionEventHandler::onCharacterReceived( | |||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPathToRoot( | ||||||||
| std::vector<winrt::Microsoft::ReactNative::ComponentView> CompositionEventHandler::GetTouchableViewsInPathToRoot( | ||||||||
| const winrt::Microsoft::ReactNative::ComponentView &componentView) { | ||||||||
| if (componentView) { | ||||||||
| auto tag = componentView.Tag(); | ||||||||
| if (tag == m_cachedEventPathTag) { | ||||||||
| return m_cachedEventPath; | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| std::vector<winrt::Microsoft::ReactNative::ComponentView> results; | ||||||||
| auto view = componentView; | ||||||||
| while (view) { | ||||||||
|
|
@@ -674,6 +681,12 @@ std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPat | |||||||
| } | ||||||||
| view = view.Parent(); | ||||||||
| } | ||||||||
|
|
||||||||
| if (componentView) { | ||||||||
| m_cachedEventPathTag = componentView.Tag(); | ||||||||
| m_cachedEventPath = results; | ||||||||
| } | ||||||||
|
|
||||||||
| return results; | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -1030,7 +1043,7 @@ void CompositionEventHandler::getTargetPointerArgs( | |||||||
| // to apply the current origin | ||||||||
| ptScaled += RootComponentView().layoutMetrics().frame.origin; | ||||||||
|
|
||||||||
| if (std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId) != m_capturedPointers.end()) { | ||||||||
| if (m_capturedPointers.count(pointerId)) { | ||||||||
| assert(m_pointerCapturingComponentTag != -1); | ||||||||
| tag = m_pointerCapturingComponentTag; | ||||||||
|
|
||||||||
|
|
@@ -1054,7 +1067,7 @@ void CompositionEventHandler::onPointerCaptureLost( | |||||||
|
|
||||||||
| if (m_pointerCapturingComponentTag) { | ||||||||
|
Comment on lines
1067
to
1068
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The comment says "copy array to avoid iterator being invalidated during deletion", which was the original intent. Now that
Suggested change
|
||||||||
| // copy array to avoid iterator being invalidated during deletion | ||||||||
| std::vector<PointerId> capturedPointers = m_capturedPointers; | ||||||||
| std::unordered_set<PointerId> capturedPointers = m_capturedPointers; | ||||||||
|
|
||||||||
| for (auto pointerId : capturedPointers) { | ||||||||
| releasePointerCapture(pointerId, m_pointerCapturingComponentTag); | ||||||||
|
|
@@ -1322,7 +1335,7 @@ bool CompositionEventHandler::CapturePointer( | |||||||
| } | ||||||||
|
|
||||||||
| m_pointerCapturingComponentTag = tag; | ||||||||
| m_capturedPointers.push_back(pointer.PointerId()); | ||||||||
| m_capturedPointers.insert(pointer.PointerId()); | ||||||||
| return true; | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -1337,11 +1350,9 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo | |||||||
| bool result = false; | ||||||||
|
|
||||||||
| if (m_pointerCapturingComponentTag == tag) { | ||||||||
| auto it = std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId); | ||||||||
| if (it == m_capturedPointers.end()) { | ||||||||
| if (m_capturedPointers.erase(pointerId) == 0) { | ||||||||
| return false; | ||||||||
| } | ||||||||
| m_capturedPointers.erase(it); | ||||||||
|
|
||||||||
| if (std::shared_ptr<FabricUIManager> fabricuiManager = | ||||||||
| ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) { | ||||||||
|
|
@@ -1352,7 +1363,7 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo | |||||||
| ->OnPointerCaptureLost(); | ||||||||
| } | ||||||||
|
|
||||||||
| if (m_capturedPointers.size() == 0) { | ||||||||
| if (m_capturedPointers.empty()) { | ||||||||
| m_pointerCapturingComponentTag = -1; | ||||||||
| return true; | ||||||||
| } | ||||||||
|
|
||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,6 +14,7 @@ | |
| #include <winrt/Windows.Devices.Input.h> | ||
| #include <optional> | ||
| #include <set> | ||
| #include <unordered_set> | ||
|
|
||
| namespace winrt { | ||
| using namespace Windows::UI; | ||
|
|
@@ -141,7 +142,7 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE | |
| ReactTaggedView initialComponentView{nullptr}; | ||
| }; | ||
|
|
||
| static bool IsPointerWithinInitialTree(const ActiveTouch &activeTouch) noexcept; | ||
| bool IsPointerWithinInitialTree(const ActiveTouch &activeTouch) noexcept; | ||
| static bool IsEndishEventType(TouchEventType eventType) noexcept; | ||
| static const char *PointerTypeCStringFromUITouchType(UITouchType type) noexcept; | ||
| static facebook::react::PointerEvent CreatePointerEventFromActiveTouch( | ||
|
|
@@ -150,6 +151,9 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE | |
| static void | ||
| UpdateActiveTouch(ActiveTouch &activeTouch, facebook::react::Point ptScaled, facebook::react::Point ptLocal) noexcept; | ||
|
|
||
| std::vector<winrt::Microsoft::ReactNative::ComponentView> GetTouchableViewsInPathToRoot( | ||
| const winrt::Microsoft::ReactNative::ComponentView &componentView); | ||
|
|
||
| void UpdateCursor() noexcept; | ||
| void SetCursor(facebook::react::Cursor cursor, HCURSOR hcur) noexcept; | ||
|
|
||
|
|
@@ -161,12 +165,16 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE | |
| winrt::Microsoft::ReactNative::ReactContext m_context; | ||
|
|
||
| facebook::react::Tag m_pointerCapturingComponentTag{-1}; // Component that has captured input | ||
| std::vector<PointerId> m_capturedPointers; | ||
| std::unordered_set<PointerId> m_capturedPointers; | ||
| HCURSOR m_hcursor{nullptr}; | ||
| bool m_hcursorOwned{false}; // If we create the cursor, so we need to destroy it | ||
| facebook::react::Cursor m_currentCursor{facebook::react::Cursor::Auto}; | ||
| winrt::Microsoft::UI::Input::InputCursor m_inputCursor{nullptr}; | ||
|
|
||
| // Single-entry cache for GetTouchableViewsInPathToRoot to avoid repeated tree walks | ||
| facebook::react::Tag m_cachedEventPathTag{-1}; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Using // .h
std::optional<facebook::react::Tag> m_cachedEventPathTag;
std::vector<winrt::Microsoft::ReactNative::ComponentView> m_cachedEventPath;// .cpp — cache-read guard
if (componentView) {
auto tag = componentView.Tag();
if (m_cachedEventPathTag.has_value() && tag == *m_cachedEventPathTag) {
return m_cachedEventPath;
}
}
// ...
if (componentView) {
m_cachedEventPathTag = componentView.Tag();
m_cachedEventPath = results;
} |
||
| std::vector<winrt::Microsoft::ReactNative::ComponentView> m_cachedEventPath; | ||
|
|
||
| winrt::event_token m_pointerPressedToken; | ||
| winrt::event_token m_pointerReleasedToken; | ||
| winrt::event_token m_pointerMovedToken; | ||
|
|
||
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 cache is keyed solely on
componentView.Tag(). In Fabric, a parent view can be inserted, removed, or replaced between pointer events (via a state-driven re-render) while the leaf component retains its tag. When that happens the cached pathm_cachedEventPathbecomes stale, andHandleIncomingPointerEvent/IsPointerWithinInitialTreewill traverse the wrong set of ancestors — firing enter/leave events on views that are no longer in the path, or missing views that were added.A minimal fix is to clear the cache at the start of each top-level event dispatch (e.g., in
onPointerMoved,onPointerPressed, etc.) rather than relying on tag identity alone.