Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions examples/AppExampleFabric/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ import App from 'shared/src/App'
import { name as appName } from './app.json'
import { StrictMode } from 'react'

import { version } from 'react-native-worklets-core/package.json'
console.log(`Using react-native-worklets-core@${version}`)

// Setup a logger for filament
import { setLogger } from 'react-native-filament'
// A function that can wrap a console log call to add a prefix
Expand Down
1 change: 0 additions & 1 deletion examples/AppExampleFabric/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
"react-native-screens": "^4.19.0",
"react-native-video": "^6.18.0",
"react-native-worklets": "^0.7.1",
"react-native-worklets-core": "^1.6.2",
"shared": "workspace:^"
},
"devDependencies": {
Expand Down
1 change: 0 additions & 1 deletion examples/ExpoExample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"react-native-screens": "~4.16.0",
"react-native-video": "^6.18.0",
"react-native-worklets": "0.5.1",
"react-native-worklets-core": "^1.6.2",
"shared": "workspace:^"
},
"devDependencies": {
Expand Down
35 changes: 19 additions & 16 deletions examples/Shared/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { StyleSheet, Pressable, Text, View, ScrollView } from 'react-native'
import { NavigationContainer, useNavigation } from '@react-navigation/native'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
import {SafeAreaView} from 'react-native-safe-area-context'

import { AnimationTransitions } from './AnimationTransitions'
import { AnimationTransitionsRecording } from './AnimationTransitionsRecording'
Expand Down Expand Up @@ -50,6 +51,7 @@ function NavigationItem(props: { name: string; route: string }) {

function HomeScreen() {
return (
<SafeAreaView style={{ flex: 1 }}>
<ScrollView style={{ flex: 1 }}>
<NavigationItem name="▶️ Animation Transitions" route="AnimationTransitions" />
<NavigationItem name="📸 Camera Pan" route="CameraPan" />
Expand All @@ -69,6 +71,7 @@ function HomeScreen() {
<NavigationItem name="☁️ Skybox" route="SkyboxExample" />
<NavigationItem name="🔄 MorphTargets" route="MorphTargets" />
</ScrollView>
</SafeAreaView>
)
}

Expand All @@ -88,30 +91,30 @@ function App() {
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen
{/* <Stack.Screen
name="AnimationTransitions"
component={AnimationTransitions}
options={{
headerShown: false,
}}
/>
<Stack.Screen name="CameraPan" component={CameraPan} />
<Stack.Screen name="AnimationTransitionsRecording" component={AnimationTransitionsRecording} />
<Stack.Screen name="ImageExample" component={ImageExample} />
<Stack.Screen name="LoadFromFile" component={LoadFromFile} />
<Stack.Screen name="NoneTransparent" component={NoneTransparent} />
/> */}
{/* <Stack.Screen name="CameraPan" component={CameraPan} /> */}
{/* <Stack.Screen name="AnimationTransitionsRecording" component={AnimationTransitionsRecording} /> */}
{/* <Stack.Screen name="ImageExample" component={ImageExample} /> */}
{/* <Stack.Screen name="LoadFromFile" component={LoadFromFile} /> */}
{/* <Stack.Screen name="NoneTransparent" component={NoneTransparent} /> */}
<Stack.Screen name="MultipleInstances" component={MultipleInstances} />
<Stack.Screen name="AnimatedRotate" component={AnimatedRotate} />
<Stack.Screen name="AnimatedRotateSharedValues" component={AnimatedRotateSharedValues} />
<Stack.Screen name="ReanimatedRotation" component={ReanimatedRotation} />
<Stack.Screen name="PhysicsCoin" component={PhysicsCoin} />
<Stack.Screen name="FadeOut" component={FadeOut} />
<Stack.Screen name="CastShadow" component={CastShadow} />
<Stack.Screen name="ScaleEffect" component={ScaleEffect} />
<Stack.Screen name="ChangeMaterials" component={ChangeMaterials} />
{/* <Stack.Screen name="AnimatedRotate" component={AnimatedRotate} /> */}
{/* <Stack.Screen name="AnimatedRotateSharedValues" component={AnimatedRotateSharedValues} /> */}
{/* <Stack.Screen name="ReanimatedRotation" component={ReanimatedRotation} /> */}
{/* <Stack.Screen name="PhysicsCoin" component={PhysicsCoin} /> */}
{/* <Stack.Screen name="FadeOut" component={FadeOut} /> */}
{/* <Stack.Screen name="CastShadow" component={CastShadow} /> */}
{/* <Stack.Screen name="ScaleEffect" component={ScaleEffect} /> */}
{/* <Stack.Screen name="ChangeMaterials" component={ChangeMaterials} /> */}
<Stack.Screen name="Test" component={TestScreen} />
<Stack.Screen name="SkyboxExample" component={SkyboxExample} />
<Stack.Screen name="MorphTargets" component={MorphTargets} />
{/* <Stack.Screen name="MorphTargets" component={MorphTargets} /> */}
</Stack.Navigator>
</NavigationContainer>
</GestureHandlerRootView>
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
}
],
"scripts": {
"postinstall": "bunx patch-package"
},
"license": "MIT",
"bugs": {
Expand Down
10 changes: 5 additions & 5 deletions package/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ add_library(
../cpp/RNFChoreographer.cpp
../cpp/RNFChoreographerWrapper.cpp
../cpp/RNFListener.cpp
../cpp/jsi/RNFBoxedHybridObject.cpp
../cpp/jsi/RNFHybridObject.cpp
../cpp/jsi/RNFPromise.cpp
../cpp/jsi/RNFPromiseFactory.cpp
Expand Down Expand Up @@ -141,16 +142,15 @@ else()
)
endif()

# Link with RNWC:
find_package(react-native-worklets-core REQUIRED CONFIG)
message("RN Filament: react-native-worklets core found! Enabling Worklets support...")
message("RN Filament: react-native-worklets-core found in ${react-native-worklets-core_DIR}")

find_package(react-native-worklets REQUIRED) # <-- for Worklets
target_link_libraries(
${PACKAGE_NAME}
react-native-worklets-core::rnworklets
react-native-worklets::worklets
)
add_definitions(-DHAS_WORKLETS=1)


# Filament (local CMake project as a git submodule)
message("RN Filament: Adding pre-compiled libraries in ${FILAMENT_DIR}...")
file(GLOB FILAMENT_LIBRARIES "${FILAMENT_DIR}/lib/${ANDROID_ABI}/*.a")
Expand Down
2 changes: 1 addition & 1 deletion package/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ dependencies {
// For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation project(":react-native-worklets-core")
implementation project(":react-native-worklets")
}

if (isNewArchitectureEnabled()) {
Expand Down
40 changes: 25 additions & 15 deletions package/cpp/RNFFilamentProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "core/RNFEngineConfigHelper.h"
#include "jsi/RNFPromise.h"
#include "threading/RNFDispatcher.h"
#include "threading/RNFAsyncQueueImpl.h"

#include <memory>
#include <string>
Expand All @@ -31,7 +32,10 @@ void FilamentProxy::loadHybridMethods() {
registerHybridMethod("getCurrentDispatcher", &FilamentProxy::getCurrentDispatcher, this);
registerHybridGetter("hasWorklets", &FilamentProxy::getHasWorklets, this);
#if HAS_WORKLETS
registerHybridMethod("createWorkletContext", &FilamentProxy::createWorkletContext, this);
// Newly added APIs:
registerHybridMethod("createWorkletAsyncQueue", &FilamentProxy::createWorkletAsyncQueue, this);
registerHybridMethod("installDispatcher", &FilamentProxy::installDispatcher, this);
registerHybridMethod("box", &FilamentProxy::box, this);
#endif
}

Expand All @@ -44,23 +48,29 @@ bool FilamentProxy::getHasWorklets() {
}

#if HAS_WORKLETS
std::shared_ptr<RNWorklet::JsiWorkletContext> FilamentProxy::createWorkletContext() {
Logger::log(TAG, "Creating Worklet Context...");
auto jsDispatcher = getJSDispatcher();
auto runOnJS = [=](std::function<void()>&& function) { jsDispatcher->runAsync(std::move(function)); };
auto renderThreadDispatcher = getRenderThreadDispatcher();
auto runOnWorklet = [=](std::function<void()>&& function) { renderThreadDispatcher->runAsync(std::move(function)); };
auto& runtime = getMainJSRuntime();
auto workletContext = std::make_shared<RNWorklet::JsiWorkletContext>("FilamentRenderer", &runtime, runOnJS, runOnWorklet);
Logger::log(TAG, "Successfully created WorkletContext! Installing global Dispatcher...");

workletContext->invokeOnWorkletThread([=](RNWorklet::JsiWorkletContext*, jsi::Runtime& runtime) {
std::shared_ptr<worklets::AsyncQueue> FilamentProxy::createWorkletAsyncQueue() {
Logger::log(TAG, "Creating Worklet AsyncQueue...");
auto renderThreadDispatcher = getRenderThreadDispatcher();
// auto runOnWorklet = [=](std::function<void()>&& function) { renderThreadDispatcher->runAsync(std::move(function)); };
// TODO: i am pretty sure i should hold this dispatcher somewhere? or will the JS engine keep it alive as its NativeState?
auto asyncQueue = std::make_shared<RNFAsyncQueueImpl>(renderThreadDispatcher);

return asyncQueue;
}


jsi::Value FilamentProxy::installDispatcher(jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) {
auto renderThreadDispatcher = getRenderThreadDispatcher(); // todo: return dispatcher, and pass it here is cleaner i guess
// Note: one thing that is odd, is that this is called with the correct runtime, but on the "wrong" thread.
// this will still be called from the JS thread, but the runtime is the worklet runtime.
Dispatcher::installRuntimeGlobalDispatcher(runtime, renderThreadDispatcher);
Logger::log(TAG, "Successfully installed global Dispatcher in WorkletContext!");
});
return jsi::Value::undefined();
}

return workletContext;
std::shared_ptr<RNFBoxedHybridObject> FilamentProxy::box(const std::shared_ptr<HybridObject>& hybridObject) {
return std::make_shared<RNFBoxedHybridObject>(hybridObject);
}

#endif

jsi::Value FilamentProxy::getCurrentDispatcher(jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) {
Expand Down
17 changes: 6 additions & 11 deletions package/cpp/RNFFilamentProxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,13 @@
#include "bullet/RNFBulletWrapper.h"
#include "core/RNFEngineWrapper.h"
#include "jsi/RNFHybridObject.h"
#include "jsi/RNFBoxedHybridObject.h"
#include "test/RNFTestHybridObject.h"
#include "threading/RNFDispatcher.h"
#include "threading/RNFAsyncQueueImpl.h"

#include <ReactCommon/CallInvoker.h>

#ifdef HAS_WORKLETS
#if __has_include(<react-native-worklets-core/WKTJsiWorkletContext.h>)
// Old arch & CocoaPod headers on apple
#include <react-native-worklets-core/WKTJsiWorkletContext.h>
#else
// New arch android, where RNWC and RNF c++ modules are build inside the app's project
#include "WKTJsiWorkletContext.h"
#endif
#endif // HAS_WORKLETS

namespace margelo {

using namespace facebook;
Expand Down Expand Up @@ -93,14 +85,17 @@ class FilamentProxy : public HybridObject {

#if HAS_WORKLETS
/**
* TODO: update this comment
* Create a new Worklet Context that runs on the Filament Renderer Thread.
*
* The FilamentProxy does not hold a strong reference to the Worklet Context,
* because otherwise we would have a cyclic reference.
*
* The caller (JS) is responsible for keeping the returned reference strong.
*/
std::shared_ptr<RNWorklet::JsiWorkletContext> createWorkletContext();
std::shared_ptr<worklets::AsyncQueue> createWorkletAsyncQueue();
jsi::Value installDispatcher(jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t);
std::shared_ptr<RNFBoxedHybridObject> box(const std::shared_ptr<HybridObject>& hybridObject);
#endif

public:
Expand Down
31 changes: 31 additions & 0 deletions package/cpp/jsi/RNFBoxedHybridObject.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// BoxedHybridObject.cpp
// NitroModules
//
// Created by Marc Rousavy on 17.09.24.
//

#include "RNFBoxedHybridObject.h"

namespace margelo {

std::vector<jsi::PropNameID> RNFBoxedHybridObject::getPropertyNames(facebook::jsi::Runtime& runtime) {
return jsi::PropNameID::names(runtime, "unbox");
}

jsi::Value RNFBoxedHybridObject::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
std::string name = propName.utf8(runtime);

if (name == "unbox") {
return jsi::Function::createFromHostFunction(
runtime, jsi::PropNameID::forUtf8(runtime, "unbox"), 0,
[hybridObject = _hybridObject](jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t) -> jsi::Value {
return JSIConverter<std::shared_ptr<HybridObject>>::toJSI(runtime, hybridObject);
// return jsi::Object::createFromHostObject(runtime, hybridObject);
});
}

return jsi::Value::undefined();
}

} // namespace margelo::nitro
39 changes: 39 additions & 0 deletions package/cpp/jsi/RNFBoxedHybridObject.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// Created by Hanno Gödecke on 11.01.26.
// From https://github.com/mrousavy/nitro/blob/afa41bae947ecf738c33569ead42eaf0825cc6d5/packages/react-native-nitro-modules/cpp/core/BoxedHybridObject.hpp
// TODO: should be removable once migrated to nitro modules + RNW supporting nitro HybridObjects
//

#pragma once

#include "RNFHybridObject.h"
#include <jsi/jsi.h>
#include <memory>

namespace margelo {

using namespace facebook;

/**
* Represents a `HybridObject` that has been boxed into a `jsi::HostObject`.
*
* While `HybridObject`s are runtime agnostic, some threading/worklet libraries do not support copying over objects
* with `jsi::NativeState` and a prototype chain (which is what a `HybridObject` is), so Nitro offers support for
* boxing those `HybridObject`s into a type that those libraries support - which is a `jsi::HostObject`.
*
* Simply call `unbox()` on this `jsi::HostObject` from the new Runtime/context to get the `HybridObject` again.
*/
class RNFBoxedHybridObject final : public jsi::HostObject {
public:
explicit RNFBoxedHybridObject(const std::shared_ptr<HybridObject>& hybridObject) : _hybridObject(hybridObject) {}

public:
jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& propName) override;
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime& runtime) override;

private:
std::shared_ptr<HybridObject> _hybridObject;
};

} // namespace margelo::nitro

27 changes: 27 additions & 0 deletions package/cpp/threading/RNFAsyncQueueImpl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// Created by Hanno Gödecke on 11.01.26.
//

#if __has_include(<worklets/RunLoop/AsyncQueue.h>)
#include <worklets/RunLoop/AsyncQueue.h>
#elif __has_include(<RNWorklets/worklets/RunLoop/AsyncQueue.h>)
#include <RNWorklets/worklets/RunLoop/AsyncQueue.h>
#else
#error react-native-worklets Prefab not found!
#endif

#pragma once

namespace margelo {
class RNFAsyncQueueImpl : public worklets::AsyncQueue {
public:
explicit RNFAsyncQueueImpl(std::shared_ptr<margelo::Dispatcher> dispatcher) : _dispatcher(std::move(dispatcher)) {}

void push(std::function<void()> &&job) override {
_dispatcher->runAsync(std::move(job));
}

private:
std::shared_ptr<margelo::Dispatcher> _dispatcher;
};
} // namespace margelo
Loading
Loading