From 003e7035d3c8e0fa14036ff257ca7916359f6b2c Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Mon, 26 Aug 2024 07:43:10 -0700 Subject: [PATCH] [0.74] Use Turbo Modules for Desktop instead of CxxModules (#13602) * Use Turbo Modules for Desktop instead of CxxModules (#13265) * fix * format * fabric should always set useTurboModulesOnly --------- Co-authored-by: Vladimir Morozov --- ...-dbe41975-86df-4a59-9be8-e2b51b4c50c1.json | 7 + ...mingModule.cpp => DesktopTimingModule.cpp} | 229 ++++++++++++++++-- .../{TimingModule.h => DesktopTimingModule.h} | 53 +++- vnext/Desktop/React.Windows.Desktop.vcxproj | 4 +- .../Microsoft.ReactNative.vcxproj | 2 + .../ReactHost/ReactInstanceWin.cpp | 98 +++++--- vnext/ReactWindows-Desktop.sln | 10 + vnext/Shared/DevSettings.h | 3 + vnext/Shared/OInstance.cpp | 8 +- 9 files changed, 343 insertions(+), 71 deletions(-) create mode 100644 change/react-native-windows-dbe41975-86df-4a59-9be8-e2b51b4c50c1.json rename vnext/Desktop/Modules/{TimingModule.cpp => DesktopTimingModule.cpp} (55%) rename vnext/Desktop/Modules/{TimingModule.h => DesktopTimingModule.h} (70%) diff --git a/change/react-native-windows-dbe41975-86df-4a59-9be8-e2b51b4c50c1.json b/change/react-native-windows-dbe41975-86df-4a59-9be8-e2b51b4c50c1.json new file mode 100644 index 00000000000..274d955f383 --- /dev/null +++ b/change/react-native-windows-dbe41975-86df-4a59-9be8-e2b51b4c50c1.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Enable Timer TurboModule for Desktop", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Desktop/Modules/TimingModule.cpp b/vnext/Desktop/Modules/DesktopTimingModule.cpp similarity index 55% rename from vnext/Desktop/Modules/TimingModule.cpp rename to vnext/Desktop/Modules/DesktopTimingModule.cpp index 0d9f8330663..5dcab20cda5 100644 --- a/vnext/Desktop/Modules/TimingModule.cpp +++ b/vnext/Desktop/Modules/DesktopTimingModule.cpp @@ -10,7 +10,7 @@ using namespace std; #include #include -#include "TimingModule.h" +#include "DesktopTimingModule.h" #include #include @@ -18,8 +18,11 @@ using namespace std; using namespace facebook::xplat; using namespace folly; -namespace facebook { -namespace react { +namespace facebook::react { + +//============================================================================== +// TimerQueue +//============================================================================== bool operator<(const Timer &leftTimer, const Timer &rightTimer) { return rightTimer.DueTime < leftTimer.DueTime; @@ -65,17 +68,21 @@ bool TimerQueue::IsEmpty() const { return m_timerVector.empty(); } -/*static*/ void Timing::ThreadpoolTimerCallback(PTP_CALLBACK_INSTANCE, PVOID Parameter, PTP_TIMER) noexcept { - static_cast(Parameter)->OnTimerRaised(); +//============================================================================== +// TimingHelper +//============================================================================== + +/*static*/ void TimingHelper::ThreadpoolTimerCallback(PTP_CALLBACK_INSTANCE, PVOID Parameter, PTP_TIMER) noexcept { + static_cast(Parameter)->OnTimerRaised(); } -void Timing::OnTimerRaised() noexcept { +void TimingHelper::OnTimerRaised() noexcept { if (auto inst = m_wkInstance.lock()) { if (auto nativeThread = m_nativeThread.lock()) { // Make sure we execute it on native thread for native modules // Capture weak_ptr "this" because callback will be executed on native // thread even if "this" is destroyed. - nativeThread->runOnQueue([weakThis = std::weak_ptr(shared_from_this())]() { + nativeThread->runOnQueue([weakThis = std::weak_ptr(shared_from_this())]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; @@ -126,7 +133,7 @@ void Timing::OnTimerRaised() noexcept { } } -void Timing::createTimer( +void TimingHelper::createTimer( std::weak_ptr instance, uint64_t id, double duration, @@ -159,7 +166,7 @@ void Timing::createTimer( TimersChanged(); } -void Timing::TimersChanged() noexcept { +void TimingHelper::TimersChanged() noexcept { if (m_timerQueue.IsEmpty()) { // TimerQueue is empty. // Stop the kernel timer only when it is about to fire @@ -186,7 +193,7 @@ void Timing::TimersChanged() noexcept { } } -bool Timing::KernelTimerIsAboutToFire() noexcept { +bool TimingHelper::KernelTimerIsAboutToFire() noexcept { // Here we assume if kernel timer is going to fire within 2 frames (about // 33ms), we return true I am not sure the 2 frames assumption is good enough. // We may need adjustment after performance analysis. @@ -197,12 +204,12 @@ bool Timing::KernelTimerIsAboutToFire() noexcept { return false; } -void Timing::SetInstance(std::weak_ptr instance) noexcept { +void TimingHelper::SetInstance(std::weak_ptr instance) noexcept { if (m_wkInstance.expired()) m_wkInstance = instance; } -void Timing::SetKernelTimer(DateTime dueTime) noexcept { +void TimingHelper::SetKernelTimer(DateTime dueTime) noexcept { m_dueTime = dueTime; FILETIME FileDueTime; ULARGE_INTEGER ulDueTime; @@ -220,13 +227,13 @@ void Timing::SetKernelTimer(DateTime dueTime) noexcept { SetThreadpoolTimer(m_threadpoolTimer, &FileDueTime, 0, 0); } -void Timing::InitializeKernelTimer() noexcept { +void TimingHelper::InitializeKernelTimer() noexcept { // Create ThreadPoolTimer - m_threadpoolTimer = CreateThreadpoolTimer(&Timing::ThreadpoolTimerCallback, static_cast(this), NULL); + m_threadpoolTimer = CreateThreadpoolTimer(&TimingHelper::ThreadpoolTimerCallback, static_cast(this), NULL); assert(m_threadpoolTimer && "CreateThreadpoolTimer failed."); } -void Timing::deleteTimer(uint64_t id) noexcept { +void TimingHelper::deleteTimer(uint64_t id) noexcept { if (m_timerQueue.IsEmpty()) return; if (m_timerQueue.Remove(id)) { @@ -234,18 +241,18 @@ void Timing::deleteTimer(uint64_t id) noexcept { } } -void Timing::StopKernelTimer() noexcept { +void TimingHelper::StopKernelTimer() noexcept { // Cancel pending callbacks SetThreadpoolTimer(m_threadpoolTimer, NULL, 0, 0); m_dueTime = DateTime::max(); } -void Timing::setSendIdleEvents(bool /*sendIdleEvents*/) noexcept { +void TimingHelper::setSendIdleEvents(bool /*sendIdleEvents*/) noexcept { // It seems we don't need this API. Leave it empty for now. assert(false && "not implemented"); } -Timing::~Timing() { +TimingHelper::~TimingHelper() { if (m_threadpoolTimer) { StopKernelTimer(); WaitForThreadpoolTimerCallbacks(m_threadpoolTimer, true); @@ -253,7 +260,11 @@ Timing::~Timing() { } } -TimingModule::TimingModule(std::shared_ptr &&timing) : m_timing(std::move(timing)) {} +//============================================================================== +// TimingModule +//============================================================================== + +TimingModule::TimingModule(std::shared_ptr &&timing) : m_timing(std::move(timing)) {} std::string TimingModule::getName() { return "Timing"; @@ -291,9 +302,183 @@ std::vector TimingModule::getMethods() noexcept { std::unique_ptr CreateTimingModule( const std::shared_ptr &nativeThread) noexcept { - auto module = std::make_unique(std::make_shared(nativeThread)); + auto module = std::make_unique(std::make_shared(nativeThread)); return std::move(module); } -} // namespace react -} // namespace facebook +//============================================================================== +// Timing +//============================================================================== + +void Timing::Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept { + m_reactContext = reactContext; +} + +/*static*/ void Timing::ThreadpoolTimerCallback(PTP_CALLBACK_INSTANCE, PVOID Parameter, PTP_TIMER) noexcept { + static_cast(Parameter)->OnTimerRaised(); +} + +void Timing::OnTimerRaised() noexcept { + // Make sure we execute it on native thread for native modules + // Capture weak_ptr "this" because callback will be executed on native + // thread even if "this" is destroyed. + m_reactContext.JSDispatcher().Post([weakThis = std::weak_ptr(shared_from_this())]() { + auto strongThis = weakThis.lock(); + if (!strongThis) { + return; + } + + if ((!strongThis->m_threadpoolTimer) || strongThis->m_timerQueue.IsEmpty()) { + return; + } + + winrt::Microsoft::ReactNative::JSValueArray readyTimers; + auto now = std::chrono::system_clock::now(); + auto now_ms = std::chrono::time_point_cast(now); + + // Fire timers which will be expired in 10ms + while (!strongThis->m_timerQueue.IsEmpty() && now_ms > strongThis->m_timerQueue.Front().DueTime - 10ms) { + // Pop first timer from the queue and add it to list of timers ready + // to fire + auto next = strongThis->m_timerQueue.Front(); + strongThis->m_timerQueue.Pop(); + + // VSO:1916882 potential overflow + readyTimers.push_back(next.Id); + + // If timer is repeating push it back onto the queue for the next + // repetition 'next.Period' being greater than 10ms is intended to + // prevent infinite loops + if (next.Repeat) + strongThis->m_timerQueue.Push(Timer{next.Id, now_ms + next.Period, next.Period, true}); + } + + if (!readyTimers.empty()) { + strongThis->m_reactContext.CallJSFunction( + L"JSTimers", L"callTimers", winrt::Microsoft::ReactNative::JSValueArray{std::move(readyTimers)}); + } + + if (!strongThis->m_timerQueue.IsEmpty()) { + strongThis->SetKernelTimer(strongThis->m_timerQueue.Front().DueTime); + } else { + strongThis->m_dueTime = DateTime::max(); + } + }); +} + +void Timing::createTimer(uint64_t id, double duration, double jsSchedulingTime, bool repeat) noexcept { + auto now = std::chrono::system_clock::now(); + auto now_ms = std::chrono::time_point_cast(now); + + // Convert double duration to std::chrono::duration + auto period = TimeSpan{(int64_t)duration}; + // Convert int64_t scheduletime to std::chrono::time_point + DateTime scheduledTime = DateTime(TimeSpan((int64_t)jsSchedulingTime)); + // Calculate the initial due time -- scheduleTime plus duration + auto initialDueTime = scheduledTime + period; + + if (scheduledTime + period <= now_ms && !repeat) { + m_reactContext.CallJSFunction( + L"JSTimers", + L"callTimers", + winrt::Microsoft::ReactNative::JSValueArray{winrt::Microsoft::ReactNative::JSValueArray{id}}); + return; + } + + // Make sure duration is always larger than 16ms to avoid unnecessary wakeups. + period = TimeSpan{duration < 16 ? 16 : (int64_t)duration}; + m_timerQueue.Push(Timer{id, initialDueTime, period, repeat}); + + TimersChanged(); +} + +void Timing::TimersChanged() noexcept { + if (m_timerQueue.IsEmpty()) { + // TimerQueue is empty. + // Stop the kernel timer only when it is about to fire + if (KernelTimerIsAboutToFire()) { + StopKernelTimer(); + } + return; + } + // If front timer has the same target time as ThreadpoolTimer, + // we will keep ThreadpoolTimer unchanged. + if (m_timerQueue.Front().DueTime == m_dueTime) { + // do nothing + } + // If current front timer's due time is earlier than current + // ThreadpoolTimer's, we need to reset the ThreadpoolTimer to current front + // timer + else if (m_timerQueue.Front().DueTime < m_dueTime) { + SetKernelTimer(m_timerQueue.Front().DueTime); + } + // If current front timer's due time is later than current kernel timer's, + // we will reset kernel timer only when it is about to fire + else if (KernelTimerIsAboutToFire()) { + SetKernelTimer(m_timerQueue.Front().DueTime); + } +} + +bool Timing::KernelTimerIsAboutToFire() noexcept { + // Here we assume if kernel timer is going to fire within 2 frames (about + // 33ms), we return true I am not sure the 2 frames assumption is good enough. + // We may need adjustment after performance analysis. + auto now = std::chrono::system_clock::now(); + auto now_ms = std::chrono::time_point_cast(now); + if (m_dueTime - now_ms <= 33ms) + return true; + return false; +} + +void Timing::SetKernelTimer(DateTime dueTime) noexcept { + m_dueTime = dueTime; + FILETIME FileDueTime; + ULARGE_INTEGER ulDueTime; + auto now = std::chrono::system_clock::now(); + auto now_ms = std::chrono::time_point_cast(now); + TimeSpan period = dueTime - now_ms; + ulDueTime.QuadPart = (ULONGLONG) - (period.count() * 10000); + FileDueTime.dwHighDateTime = ulDueTime.HighPart; + FileDueTime.dwLowDateTime = ulDueTime.LowPart; + + if (!m_threadpoolTimer) { + InitializeKernelTimer(); + } + + SetThreadpoolTimer(m_threadpoolTimer, &FileDueTime, 0, 0); +} + +void Timing::InitializeKernelTimer() noexcept { + // Create ThreadPoolTimer + m_threadpoolTimer = CreateThreadpoolTimer(&Timing::ThreadpoolTimerCallback, static_cast(this), NULL); + assert(m_threadpoolTimer && "CreateThreadpoolTimer failed."); +} + +void Timing::deleteTimer(uint64_t id) noexcept { + if (m_timerQueue.IsEmpty()) + return; + if (m_timerQueue.Remove(id)) { + TimersChanged(); + } +} + +void Timing::StopKernelTimer() noexcept { + // Cancel pending callbacks + SetThreadpoolTimer(m_threadpoolTimer, NULL, 0, 0); + m_dueTime = DateTime::max(); +} + +void Timing::setSendIdleEvents(bool /*sendIdleEvents*/) noexcept { + // It seems we don't need this API. Leave it empty for now. + assert(false && "not implemented"); +} + +Timing::~Timing() { + if (m_threadpoolTimer) { + StopKernelTimer(); + WaitForThreadpoolTimerCallbacks(m_threadpoolTimer, true); + CloseThreadpoolTimer(m_threadpoolTimer); + } +} + +} // namespace facebook::react diff --git a/vnext/Desktop/Modules/TimingModule.h b/vnext/Desktop/Modules/DesktopTimingModule.h similarity index 70% rename from vnext/Desktop/Modules/TimingModule.h rename to vnext/Desktop/Modules/DesktopTimingModule.h index 7e3ebb1c0ce..54533e0dc6d 100644 --- a/vnext/Desktop/Modules/TimingModule.h +++ b/vnext/Desktop/Modules/DesktopTimingModule.h @@ -3,7 +3,10 @@ #pragma once +#include "../../codegen/NativeTimingSpec.g.h" + #include +#include #include #include @@ -13,8 +16,7 @@ #include -namespace facebook { -namespace react { +namespace facebook::react { using DateTime = std::chrono::time_point; using TimeSpan = std::chrono::milliseconds; @@ -66,10 +68,11 @@ class TimerQueue { // Timing timing; // timing.createTimer(instance, id, duration, jsScheduleTime, repeat); // timing.delete(id); -class Timing : public std::enable_shared_from_this { +class TimingHelper : public std::enable_shared_from_this { public: - Timing(const std::shared_ptr &nativeThread) : m_nativeThread(nativeThread) {} - ~Timing(); + TimingHelper(const std::shared_ptr &nativeThread) + : m_nativeThread(nativeThread) {} + ~TimingHelper(); void createTimer( std::weak_ptr instance, uint64_t id, @@ -105,14 +108,46 @@ class Timing : public std::enable_shared_from_this { // false); Timing.deleteTimer(timerID); class TimingModule : public facebook::xplat::module::CxxModule { public: - TimingModule(std::shared_ptr &&timing); + TimingModule(std::shared_ptr &&timing); std::string getName() override; virtual std::map getConstants() noexcept override; virtual std::vector getMethods() noexcept override; private: - std::shared_ptr m_timing; + std::shared_ptr m_timing; +}; + +REACT_MODULE(Timing) +struct Timing : public std::enable_shared_from_this { + ~Timing(); + + REACT_INIT(Initialize) + void Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept; + + REACT_METHOD(createTimer) + void createTimer(uint64_t id, double duration, double jsSchedulingTime, bool repeat) noexcept; + + REACT_METHOD(deleteTimer) + void deleteTimer(uint64_t id) noexcept; + + REACT_METHOD(setSendIdleEvents) + void setSendIdleEvents(bool sendIdleEvents) noexcept; + + private: + static void CALLBACK + ThreadpoolTimerCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_TIMER Timer) noexcept; + void OnTimerRaised() noexcept; + void SetKernelTimer(DateTime dueTime) noexcept; + void InitializeKernelTimer() noexcept; + void TimersChanged() noexcept; + void StopKernelTimer() noexcept; + bool KernelTimerIsAboutToFire() noexcept; + + private: + winrt::Microsoft::ReactNative::ReactContext m_reactContext; + TimerQueue m_timerQueue; + PTP_TIMER m_threadpoolTimer{}; + DateTime m_dueTime; }; -} // namespace react -} // namespace facebook +} // namespace facebook::react diff --git a/vnext/Desktop/React.Windows.Desktop.vcxproj b/vnext/Desktop/React.Windows.Desktop.vcxproj index b1e26759c14..1b7b522c805 100644 --- a/vnext/Desktop/React.Windows.Desktop.vcxproj +++ b/vnext/Desktop/React.Windows.Desktop.vcxproj @@ -175,7 +175,7 @@ - + Create @@ -263,7 +263,7 @@ - + diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index b0fd50abcce..46555f18a5f 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -121,6 +121,7 @@ OLD_CPPWINRT is a workaround to make target version to 19H1 --> + DISABLE_XAML_GENERATED_MAIN; REACTWINDOWS_BUILD; RN_PLATFORM=windows; NOMINMAX; @@ -131,6 +132,7 @@ WIN32=0; WINRT=1; _HAS_AUTO_PTR_ETC; + _USE_MATH_DEFINES; _SILENCE_CXX20_U8PATH_DEPRECATION_WARNING; %(PreprocessorDefinitions) diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp index 8a5bddfca50..33aad10d8b7 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp @@ -83,7 +83,14 @@ #include "Modules/LogBoxModule.h" #include "Modules/NativeUIManager.h" #include "Modules/PaperUIManagerModule.h" +#else +#include "Modules/DesktopTimingModule.h" #endif +#include "Modules/ExceptionsManager.h" +#include "Modules/PlatformConstantsWinModule.h" +#include "Modules/ReactRootViewTagGenerator.h" +#include "Modules/SourceCode.h" +#include "Modules/StatusBarManager.h" #if !defined(CORE_ABI) || defined(USE_FABRIC) #include @@ -397,42 +404,44 @@ void ReactInstanceWin::LoadModules( } #endif - ::Microsoft::ReactNative::ExceptionsManager::SetRedBoxHander( - winrt::Microsoft::ReactNative::ReactPropertyBag(m_reactContext->Properties()), m_redboxHandler); - registerTurboModule( - L"ExceptionsManager", - winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::ExceptionsManager>()); + if (devSettings->useTurboModulesOnly) { + ::Microsoft::ReactNative::ExceptionsManager::SetRedBoxHander( + winrt::Microsoft::ReactNative::ReactPropertyBag(m_reactContext->Properties()), m_redboxHandler); + registerTurboModule( + L"ExceptionsManager", + winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::ExceptionsManager>()); - registerTurboModule( - L"StatusBarManager", - winrt::Microsoft::ReactNative::MakeModuleProvider<::Microsoft::ReactNative::StatusBarManager>()); + registerTurboModule( + L"StatusBarManager", + winrt::Microsoft::ReactNative::MakeModuleProvider<::Microsoft::ReactNative::StatusBarManager>()); - registerTurboModule( - L"PlatformConstants", - winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::PlatformConstants>()); + registerTurboModule( + L"PlatformConstants", + winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::PlatformConstants>()); - uint32_t hermesBytecodeVersion = 0; + uint32_t hermesBytecodeVersion = 0; #if defined(USE_HERMES) && defined(ENABLE_DEVSERVER_HBCBUNDLES) - hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; + hermesBytecodeVersion = ::hermes::hbc::BYTECODE_VERSION; #endif - std::string bundleUrl = (devSettings->useWebDebugger || devSettings->liveReloadCallback) - ? facebook::react::DevServerHelper::get_BundleUrl( - devSettings->sourceBundleHost, - devSettings->sourceBundlePort, - devSettings->debugBundlePath, - devSettings->platformName, - devSettings->bundleAppId, - devSettings->devBundle, - devSettings->useFastRefresh, - devSettings->inlineSourceMap, - hermesBytecodeVersion) - : devSettings->bundleRootPath; - ::Microsoft::ReactNative::SourceCode::SetScriptUrl( - winrt::Microsoft::ReactNative::ReactPropertyBag(m_reactContext->Properties()), bundleUrl); + std::string bundleUrl = (devSettings->useWebDebugger || devSettings->liveReloadCallback) + ? facebook::react::DevServerHelper::get_BundleUrl( + devSettings->sourceBundleHost, + devSettings->sourceBundlePort, + devSettings->debugBundlePath, + devSettings->platformName, + devSettings->bundleAppId, + devSettings->devBundle, + devSettings->useFastRefresh, + devSettings->inlineSourceMap, + hermesBytecodeVersion) + : devSettings->bundleRootPath; + ::Microsoft::ReactNative::SourceCode::SetScriptUrl( + winrt::Microsoft::ReactNative::ReactPropertyBag(m_reactContext->Properties()), bundleUrl); - registerTurboModule( - L"SourceCode", winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::SourceCode>()); + registerTurboModule( + L"SourceCode", winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::SourceCode>()); + } registerTurboModule( L"DevSettings", winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::DevSettings>()); @@ -446,6 +455,17 @@ void ReactInstanceWin::LoadModules( winrt::Microsoft::ReactNative::MakeTurboModuleProvider<::Microsoft::ReactNative::LinkingManager>()); registerTurboModule(L"Timing", winrt::Microsoft::ReactNative::MakeModuleProvider<::Microsoft::ReactNative::Timing>()); +#else + +#if defined(USE_FABRIC) + if (Microsoft::ReactNative::IsFabricEnabled(m_reactContext->Properties())) { + registerTurboModule( + L"Timing", winrt::Microsoft::ReactNative::MakeModuleProvider<::Microsoft::ReactNative::Timing>()); + } else +#endif + { + registerTurboModule(L"Timing", winrt::Microsoft::ReactNative::MakeModuleProvider<::facebook::react::Timing>()); + } #endif registerTurboModule( @@ -582,6 +602,7 @@ void ReactInstanceWin::InitializeBridgeless() noexcept { strongThis->Queue().Post([this, weakThis]() noexcept { if (auto strongThis = weakThis.GetStrongPtr()) { auto devSettings = strongThis->CreateDevSettings(); + devSettings->useTurboModulesOnly = true; try { if (devSettings->useFastRefresh || devSettings->liveReloadCallback) { @@ -739,11 +760,21 @@ void ReactInstanceWin::InitializeWithBridge() noexcept { // Objects that must be created on the UI thread if (auto strongThis = weakThis.GetStrongPtr()) { InitUIDependentCalls(); - strongThis->Queue().Post([this, weakThis]() noexcept { if (auto strongThis = weakThis.GetStrongPtr()) { auto devSettings = strongThis->CreateDevSettings(); + auto getBoolProperty = [properties = ReactPropertyBag{m_options.Properties}]( + const wchar_t *ns, const wchar_t *name, bool defaultValue) noexcept -> bool { + ReactPropertyId propId{ns == nullptr ? ReactPropertyNamespace() : ReactPropertyNamespace(ns), name}; + std::optional propValue = properties.Get(propId); + return propValue.value_or(defaultValue); + }; + + devSettings->omitNetworkingCxxModules = getBoolProperty(nullptr, L"OmitNetworkingCxxModules", false); + devSettings->useWebSocketTurboModule = getBoolProperty(nullptr, L"UseWebSocketTurboModule", false); + devSettings->useTurboModulesOnly = getBoolProperty(L"DevSettings", L"UseTurboModulesOnly", false); + std::vector cxxModules; auto nmp = std::make_shared(); @@ -835,12 +866,7 @@ void ReactInstanceWin::InitializeWithBridge() noexcept { // We need to keep the instance wrapper alive as its destruction shuts down the native queue. m_options.TurboModuleProvider->SetReactContext( winrt::make(Mso::Copy(m_reactContext))); - auto omitNetCxxPropName = ReactPropertyBagHelper::GetName(nullptr, L"OmitNetworkingCxxModules"); - auto omitNetCxxPropValue = m_options.Properties.Get(omitNetCxxPropName); - devSettings->omitNetworkingCxxModules = winrt::unbox_value_or(omitNetCxxPropValue, false); - auto useWebSocketTurboModulePropName = ReactPropertyBagHelper::GetName(nullptr, L"UseWebSocketTurboModule"); - auto useWebSocketTurboModulePropValue = m_options.Properties.Get(useWebSocketTurboModulePropName); - devSettings->useWebSocketTurboModule = winrt::unbox_value_or(useWebSocketTurboModulePropValue, false); + auto bundleRootPath = devSettings->bundleRootPath; auto jsiRuntimeHolder = devSettings->jsiRuntimeHolder; auto instanceWrapper = facebook::react::CreateReactInstance( diff --git a/vnext/ReactWindows-Desktop.sln b/vnext/ReactWindows-Desktop.sln index c22e20b0029..87c3b33978b 100644 --- a/vnext/ReactWindows-Desktop.sln +++ b/vnext/ReactWindows-Desktop.sln @@ -8,6 +8,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fmt", "fmt\fmt.vcxproj", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ReactCommon", "ReactCommon\ReactCommon.vcxproj", "{A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}" ProjectSection(ProjectDependencies) = postProject + {74085F13-2DDE-45E5-A0CA-927AC9D0B953} = {74085F13-2DDE-45E5-A0CA-927AC9D0B953} {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {A990658C-CE31-4BCC-976F-0FC6B1AF693D} EndProjectSection EndProject @@ -76,6 +77,9 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "React.Windows.Desktop.IntegrationTests", "Desktop.IntegrationTests\React.Windows.Desktop.IntegrationTests.vcxproj", "{E0D269B4-D7F0-4C4E-92CD-B2C06109A2BB}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "React.Windows.IntegrationTests", "IntegrationTests\React.Windows.IntegrationTests.vcxproj", "{700A84FD-F92A-43F1-8D06-B0E0745DF9B5}" + ProjectSection(ProjectDependencies) = postProject + {88BAB0FA-E1AC-4DA7-A30C-F91702A8EADB} = {88BAB0FA-E1AC-4DA7-A30C-F91702A8EADB} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Chakra", "Chakra\Chakra.vcxitems", "{C38970C0-5FBF-4D69-90D8-CBAC225AE895}" EndProject @@ -88,8 +92,14 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "React.Windows.Desktop.ABITests", "Desktop.ABITests\React.Windows.Desktop.ABITests.vcxproj", "{44DCED9B-9C4C-48FE-8545-0930192BBC16}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "React.Windows.Test", "Test\React.Windows.Test.vcxproj", "{CD0415C6-D908-4212-9481-49BE41F58D27}" + ProjectSection(ProjectDependencies) = postProject + {88BAB0FA-E1AC-4DA7-A30C-F91702A8EADB} = {88BAB0FA-E1AC-4DA7-A30C-F91702A8EADB} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "React.Windows.Desktop.Test.DLL", "Desktop.Test.DLL\React.Windows.Desktop.Test.DLL.vcxproj", "{473FE8E8-26DA-4B46-A7B3-7B4A758075D0}" + ProjectSection(ProjectDependencies) = postProject + {88BAB0FA-E1AC-4DA7-A30C-F91702A8EADB} = {88BAB0FA-E1AC-4DA7-A30C-F91702A8EADB} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}" EndProject diff --git a/vnext/Shared/DevSettings.h b/vnext/Shared/DevSettings.h index 0d21853a7f2..b6ddcb0745e 100644 --- a/vnext/Shared/DevSettings.h +++ b/vnext/Shared/DevSettings.h @@ -114,6 +114,9 @@ struct DevSettings { // Enable concurrent mode by installing runtimeScheduler bool useRuntimeScheduler{false}; + + // If true, then use only Turbo Modules instead of CxxModules. + bool useTurboModulesOnly{false}; }; } // namespace react diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index ece2c869a9c..a4608ed59cc 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -596,6 +596,10 @@ InstanceImpl::~InstanceImpl() { std::vector> InstanceImpl::GetDefaultNativeModules( std::shared_ptr nativeQueue) { std::vector> modules; + if (m_devSettings->useTurboModulesOnly) { + return modules; + } + auto transitionalProps{ReactPropertyBagHelper::CreatePropertyBag()}; // These modules are instantiated separately in MSRN (Universal Windows). @@ -613,9 +617,9 @@ std::vector> InstanceImpl::GetDefaultNativeModules nativeQueue)); } - // Applications using the Windows ABI feature should loade the networking TurboModule variants instead. + // Applications using the Windows ABI feature should load the networking TurboModule variants instead. if (!m_devSettings->omitNetworkingCxxModules) { - // Use in case the host app provides its a non-Blob-compatilbe HTTP module. + // Use in case the host app provides its a non-Blob-compatible HTTP module. if (!Microsoft::React::GetRuntimeOptionBool("Blob.DisableModule")) { modules.push_back(std::make_unique( m_innerInstance,