first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE on)
include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
file(GLOB react_renderer_observers_events_SRC CONFIGURE_DEPENDS *.cpp)
add_library(react_renderer_observers_events OBJECT ${react_renderer_observers_events_SRC})
target_include_directories(react_renderer_observers_events PUBLIC ${REACT_COMMON_DIR})
target_link_libraries(react_renderer_observers_events
react_debug
react_performance_timeline
react_timing
react_renderer_core
react_renderer_runtimescheduler
react_featureflags
react_renderer_uimanager
react_utils
rrc_view)
target_compile_reactnative_options(react_renderer_observers_events PRIVATE)
target_compile_options(react_renderer_observers_events PRIVATE -Wpedantic)

View File

@@ -0,0 +1,259 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include "EventPerformanceLogger.h"
#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/timing/primitives.h>
#include <unordered_map>
namespace facebook::react {
namespace {
bool isTargetInRootShadowNode(
const SharedEventTarget& target,
const RootShadowNode::Shared& rootShadowNode) {
return target && rootShadowNode &&
target->getSurfaceId() == rootShadowNode->getSurfaceId();
}
bool hasPendingRenderingUpdates(
const SharedEventTarget& target,
const std::unordered_set<SurfaceId>&
surfaceIdsWithPendingRenderingUpdates) {
return target != nullptr &&
surfaceIdsWithPendingRenderingUpdates.contains(target->getSurfaceId());
}
struct StrKey {
size_t key;
StrKey(std::string_view s) : key(std::hash<std::string_view>{}(s)) {}
bool operator==(const StrKey& rhs) const {
return key == rhs.key;
}
};
struct StrKeyHash {
constexpr size_t operator()(const StrKey& strKey) const {
return strKey.key;
}
};
// Supported events for reporting, see
// https://www.w3.org/TR/event-timing/#sec-events-exposed
// Not all of these are currently supported by RN, but we map them anyway for
// future-proofing.
using SupportedEventTypeRegistry =
std::unordered_map<StrKey, std::string_view, StrKeyHash>;
const SupportedEventTypeRegistry& getSupportedEvents() {
static SupportedEventTypeRegistry SUPPORTED_EVENTS = {
{StrKey("topAuxClick"), "auxclick"},
{StrKey("topClick"), "click"},
{StrKey("topContextMenu"), "contextmenu"},
{StrKey("topDblClick"), "dblclick"},
{StrKey("topMouseDown"), "mousedown"},
{StrKey("topMouseEnter"), "mouseenter"},
{StrKey("topMouseLeave"), "mouseleave"},
{StrKey("topMouseOut"), "mouseout"},
{StrKey("topMouseOver"), "mouseover"},
{StrKey("topMouseUp"), "mouseup"},
{StrKey("topPointerOver"), "pointerover"},
{StrKey("topPointerEnter"), "pointerenter"},
{StrKey("topPointerDown"), "pointerdown"},
{StrKey("topPointerUp"), "pointerup"},
{StrKey("topPointerCancel"), "pointercancel"},
{StrKey("topPointerOut"), "pointerout"},
{StrKey("topPointerLeave"), "pointerleave"},
{StrKey("topGotPointerCapture"), "gotpointercapture"},
{StrKey("topLostPointerCapture"), "lostpointercapture"},
{StrKey("topTouchStart"), "touchstart"},
{StrKey("topTouchEnd"), "touchend"},
{StrKey("topTouchCancel"), "touchcancel"},
{StrKey("topKeyDown"), "keydown"},
{StrKey("topKeyPress"), "keypress"},
{StrKey("topKeyUp"), "keyup"},
{StrKey("topBeforeInput"), "beforeinput"},
{StrKey("topInput"), "input"},
{StrKey("topCompositionStart"), "compositionstart"},
{StrKey("topCompositionUpdate"), "compositionupdate"},
{StrKey("topCompositionEnd"), "compositionend"},
{StrKey("topDragStart"), "dragstart"},
{StrKey("topDragEnd"), "dragend"},
{StrKey("topDragEnter"), "dragenter"},
{StrKey("topDragLeave"), "dragleave"},
{StrKey("topDragOver"), "dragover"},
{StrKey("topDrop"), "drop"},
};
return SUPPORTED_EVENTS;
}
} // namespace
EventPerformanceLogger::EventPerformanceLogger(
std::weak_ptr<PerformanceEntryReporter> performanceEntryReporter)
: performanceEntryReporter_(std::move(performanceEntryReporter)) {}
EventTag EventPerformanceLogger::onEventStart(
std::string_view name,
SharedEventTarget target,
std::optional<HighResTimeStamp> eventStartTimeStamp) {
auto performanceEntryReporter = performanceEntryReporter_.lock();
if (performanceEntryReporter == nullptr) {
return EMPTY_EVENT_TAG;
}
const auto& supportedEvents = getSupportedEvents();
auto it = supportedEvents.find(name);
if (it == supportedEvents.end()) {
return 0;
}
auto reportedName = it->second;
auto eventTag = createEventTag();
// The event start timestamp may be provided by the caller in order to
// specify the platform specific event start time.
HighResTimeStamp timeStamp =
eventStartTimeStamp ? *eventStartTimeStamp : HighResTimeStamp::now();
{
std::lock_guard lock(eventsInFlightMutex_);
eventsInFlight_.emplace(
eventTag,
EventEntry{
.name = reportedName, .target = target, .startTime = timeStamp});
}
return eventTag;
}
void EventPerformanceLogger::onEventProcessingStart(EventTag tag) {
auto performanceEntryReporter = performanceEntryReporter_.lock();
if (performanceEntryReporter == nullptr) {
return;
}
auto timeStamp = HighResTimeStamp::now();
{
std::lock_guard lock(eventsInFlightMutex_);
auto it = eventsInFlight_.find(tag);
if (it != eventsInFlight_.end()) {
it->second.processingStartTime = timeStamp;
}
}
}
void EventPerformanceLogger::onEventProcessingEnd(EventTag tag) {
auto performanceEntryReporter = performanceEntryReporter_.lock();
if (performanceEntryReporter == nullptr) {
return;
}
auto timeStamp = HighResTimeStamp::now();
{
std::lock_guard lock(eventsInFlightMutex_);
auto it = eventsInFlight_.find(tag);
if (it == eventsInFlight_.end()) {
return;
}
auto& entry = it->second;
react_native_assert(
entry.processingStartTime.has_value() &&
"Attempting to set processingEndTime while processingStartTime is not set.");
entry.processingEndTime = timeStamp;
}
}
void EventPerformanceLogger::dispatchPendingEventTimingEntries(
HighResTimeStamp taskEndTime,
const std::unordered_set<SurfaceId>&
surfaceIdsWithPendingRenderingUpdates) {
auto performanceEntryReporter = performanceEntryReporter_.lock();
if (performanceEntryReporter == nullptr) {
return;
}
std::lock_guard lock(eventsInFlightMutex_);
auto it = eventsInFlight_.begin();
while (it != eventsInFlight_.end()) {
auto& entry = it->second;
if (entry.isWaitingForDispatch() || entry.isWaitingForMount) {
++it;
} else if (hasPendingRenderingUpdates(
entry.target, surfaceIdsWithPendingRenderingUpdates)) {
// We'll wait for mount to report the event
entry.isWaitingForMount = true;
entry.taskEndTime = taskEndTime;
++it;
} else {
react_native_assert(
entry.processingStartTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingStartTime defined.");
react_native_assert(
entry.processingEndTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingEndTime defined.");
performanceEntryReporter->reportEvent(
std::string(entry.name),
entry.startTime,
taskEndTime - entry.startTime,
entry.processingStartTime.value(),
entry.processingEndTime.value(),
taskEndTime,
entry.interactionId);
it = eventsInFlight_.erase(it);
}
}
}
void EventPerformanceLogger::shadowTreeDidMount(
const RootShadowNode::Shared& rootShadowNode,
HighResTimeStamp mountTime) noexcept {
auto performanceEntryReporter = performanceEntryReporter_.lock();
if (performanceEntryReporter == nullptr) {
return;
}
std::lock_guard lock(eventsInFlightMutex_);
auto it = eventsInFlight_.begin();
while (it != eventsInFlight_.end()) {
const auto& entry = it->second;
if (entry.isWaitingForMount &&
isTargetInRootShadowNode(entry.target, rootShadowNode)) {
react_native_assert(
entry.processingStartTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingStartTime defined.");
react_native_assert(
entry.processingEndTime.has_value() &&
"Attempted to report PerformanceEventTiming, which did not have processingEndTime defined.");
performanceEntryReporter->reportEvent(
std::string(entry.name),
entry.startTime,
mountTime - entry.startTime,
entry.processingStartTime.value(),
entry.processingEndTime.value(),
entry.taskEndTime.value(),
entry.interactionId);
it = eventsInFlight_.erase(it);
} else {
++it;
}
}
}
EventTag EventPerformanceLogger::createEventTag() {
sCurrentEventTag_++;
return sCurrentEventTag_;
}
} // namespace facebook::react

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#pragma once
#include <react/performance/timeline/PerformanceEntryReporter.h>
#include <react/renderer/core/EventLogger.h>
#include <react/renderer/runtimescheduler/RuntimeSchedulerEventTimingDelegate.h>
#include <react/renderer/uimanager/UIManagerMountHook.h>
#include <memory>
#include <mutex>
#include <optional>
#include <string_view>
#include <unordered_map>
namespace facebook::react {
class EventPerformanceLogger : public EventLogger,
public RuntimeSchedulerEventTimingDelegate,
public UIManagerMountHook {
public:
explicit EventPerformanceLogger(std::weak_ptr<PerformanceEntryReporter> performanceEntryReporter);
#pragma mark - EventLogger
EventTag onEventStart(
std::string_view name,
SharedEventTarget target,
std::optional<HighResTimeStamp> eventStartTimeStamp = std::nullopt) override;
void onEventProcessingStart(EventTag tag) override;
void onEventProcessingEnd(EventTag tag) override;
#pragma mark - RuntimeSchedulerEventTimingDelegate
void dispatchPendingEventTimingEntries(
HighResTimeStamp taskEndTime,
const std::unordered_set<SurfaceId> &surfaceIdsWithPendingRenderingUpdates) override;
#pragma mark - UIManagerMountHook
void shadowTreeDidMount(const RootShadowNode::Shared &rootShadowNode, HighResTimeStamp mountTime) noexcept override;
private:
struct EventEntry {
std::string_view name;
SharedEventTarget target{nullptr};
HighResTimeStamp startTime;
std::optional<HighResTimeStamp> processingStartTime;
std::optional<HighResTimeStamp> processingEndTime;
std::optional<HighResTimeStamp> taskEndTime;
bool isWaitingForMount{false};
// TODO: Define the way to assign interaction IDs to the event chains
// (T141358175)
PerformanceEntryInteractionId interactionId{0};
bool isWaitingForDispatch()
{
return !processingEndTime.has_value();
}
};
// Registry to store the events that are currently ongoing.
// Note that we could probably use a more efficient container for that,
// but since we only report discrete events, the volume is normally low,
// so a hash map should be just fine.
std::unordered_map<EventTag, EventEntry> eventsInFlight_;
std::mutex eventsInFlightMutex_;
std::weak_ptr<PerformanceEntryReporter> performanceEntryReporter_;
EventTag sCurrentEventTag_{EMPTY_EVENT_TAG};
EventTag createEventTag();
};
} // namespace facebook::react