Skip to content

Commit da82108

Browse files
authored
feat: Create Choreographer (#11)
Creates `Choreographer`, a platform specific instance that fires `onFrame` callbacks at the screen's refresh rate. The `Choreographer` instance runs on the Thread that created it. We could create such instances on the UI Thread or any other arbitrary Thread using either react-native-reanimated or react-native-worklets-core. Currently you can create a `Choreographer` using the `FilamentProxy`: ```ts const choreographer = FilamentProxy.createChoreographer() choreographer.addOnFrameCallback((timestamp: number) => { console.log(`onFrame at ${timestamp}`) }) choreographer.start() setTimeout(() => { choreographer.stop() }, 1500) ``` Create on the UI Thread: ```ts runOnUI(() => { 'worklet' const choreographer = FilamentProxy.createChoreographer() choreographer.addOnFrameCallback((timestamp: number) => { 'worklet' // TODO: This is currently not possible since we don't have a Worklets integration. This func cannot be called probably. // Reanimated did work on something pretty interesting where they abstract all of this on the JS side, meaning there's // no change required from our end - we just receive a jsi::Function as normal - but it's not yet public. // So to make that work, we need to integrate react-native-reanimated or react-native-worklets-core in RNF for now. }) })() ``` Note: You need to keep a strong reference on the `Choreographer`, otherwise it might get deleted by GC.
1 parent 7aaf536 commit da82108

31 files changed

+418
-23
lines changed

package/android/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_library(
2222
../cpp/FilamentProxy.cpp
2323
../cpp/Surface.cpp
2424
../cpp/SurfaceProvider.cpp
25+
../cpp/Choreographer.cpp
2526
../cpp/Listener.cpp
2627
../cpp/jsi/HybridObject.cpp
2728
../cpp/jsi/Promise.cpp
@@ -34,6 +35,7 @@ add_library(
3435
src/main/cpp/JNISharedPtr.cpp
3536
src/main/cpp/FilamentInstaller.cpp
3637
src/main/cpp/java-bindings/JFilamentProxy.cpp
38+
src/main/cpp/java-bindings/JChoreographer.cpp
3739
src/main/cpp/java-bindings/JFilamentView.cpp
3840
src/main/cpp/java-bindings/JSurfaceProvider.cpp
3941
)

package/android/src/main/cpp/AndroidFilamentProxy.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ std::shared_ptr<FilamentView> AndroidFilamentProxy::findFilamentView(int id) {
2525
return _proxy->cthis()->findFilamentView(id);
2626
}
2727

28+
std::shared_ptr<Choreographer> AndroidFilamentProxy::createChoreographer() {
29+
return _proxy->cthis()->createChoreographer();
30+
}
31+
2832
} // namespace margelo

package/android/src/main/cpp/AndroidFilamentProxy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class AndroidFilamentProxy : public FilamentProxy {
2525
// TODO(hanno): implement
2626
int loadModel(std::string path) override;
2727
std::shared_ptr<FilamentView> findFilamentView(int id) override;
28+
std::shared_ptr<Choreographer> createChoreographer() override;
2829

2930
private:
3031
jni::global_ref<JFilamentProxy::javaobject> _proxy;

package/android/src/main/cpp/Filament.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "FilamentInstaller.h"
2+
#include "JChoreographer.h"
23
#include "JFilamentProxy.h"
34
#include "JFilamentView.h"
45
#include "JSurfaceProvider.h"
@@ -11,5 +12,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
1112
margelo::JFilamentProxy::registerNatives();
1213
margelo::JSurfaceProvider::registerNatives();
1314
margelo::JFilamentView::registerNatives();
15+
margelo::JChoreographer::registerNatives();
1416
});
1517
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// Created by Marc Rousavy on 23.02.24.
3+
//
4+
5+
#include "JChoreographer.h"
6+
#include <android/choreographer.h>
7+
8+
namespace margelo {
9+
10+
void JChoreographer::registerNatives() {
11+
registerHybrid({
12+
makeNativeMethod("initHybrid", JChoreographer::initHybrid),
13+
makeNativeMethod("onFrame", JChoreographer::onFrameLong),
14+
});
15+
}
16+
17+
JChoreographer::JChoreographer(const jni::alias_ref<jhybridobject>& javaThis) : _javaPart(jni::make_global(javaThis)) {}
18+
19+
jni::local_ref<JChoreographer::jhybriddata> JChoreographer::initHybrid(jni::alias_ref<jhybridobject> javaThis) {
20+
return makeCxxInstance(javaThis);
21+
}
22+
23+
void JChoreographer::onFrameLong(jlong timestamp) {
24+
onFrame(static_cast<double>(timestamp));
25+
}
26+
27+
void JChoreographer::start() {
28+
static const auto method = javaClassLocal()->getMethod<void()>("start");
29+
method(_javaPart);
30+
}
31+
32+
void JChoreographer::stop() {
33+
static const auto method = javaClassLocal()->getMethod<void()>("stop");
34+
method(_javaPart);
35+
}
36+
37+
} // namespace margelo
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// Created by Marc Rousavy on 23.02.24.
3+
//
4+
5+
#pragma once
6+
7+
#include "Choreographer.h"
8+
#include <fbjni/fbjni.h>
9+
10+
namespace margelo {
11+
12+
using namespace facebook;
13+
14+
class JChoreographer : public jni::HybridClass<JChoreographer>, public Choreographer {
15+
public:
16+
static void registerNatives();
17+
18+
void start() override;
19+
void stop() override;
20+
void onFrameLong(jlong timestamp);
21+
22+
private:
23+
friend HybridBase;
24+
jni::global_ref<JChoreographer::javaobject> _javaPart;
25+
static auto constexpr TAG = "JChoreographer";
26+
static auto constexpr kJavaDescriptor = "Lcom/margelo/filament/FilamentChoreographer;";
27+
28+
private:
29+
explicit JChoreographer(const jni::alias_ref<jhybridobject>& javaThis);
30+
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> javaThis);
31+
};
32+
33+
} // namespace margelo

package/android/src/main/cpp/java-bindings/JFilamentProxy.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//
44

55
#include "JFilamentProxy.h"
6+
#include "JChoreographer.h"
67
#include "JFilamentView.h"
78
#include "JNISharedPtr.h"
89
#include <fbjni/fbjni.h>
@@ -32,6 +33,14 @@ std::shared_ptr<FilamentView> JFilamentProxy::findFilamentView(int id) {
3233
return std::static_pointer_cast<FilamentView>(sharedRef);
3334
}
3435

36+
std::shared_ptr<Choreographer> JFilamentProxy::createChoreographer() {
37+
static const auto method = javaClassLocal()->getMethod<jni::alias_ref<JChoreographer::javaobject>()>("createChoreographer");
38+
jni::local_ref<JChoreographer::javaobject> choreographer = method(_javaPart);
39+
jni::global_ref<JChoreographer::javaobject> globalRef = jni::make_global(choreographer);
40+
std::shared_ptr<JChoreographer> sharedRef = JNISharedPtr::make_shared_from_jni<JChoreographer>(globalRef);
41+
return std::static_pointer_cast<Choreographer>(sharedRef);
42+
}
43+
3544
jsi::Runtime& JFilamentProxy::getRuntime() {
3645
if (_runtime == nullptr) {
3746
[[unlikely]];

package/android/src/main/cpp/java-bindings/JFilamentProxy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#pragma once
66

7+
#include "Choreographer.h"
78
#include "FilamentView.h"
89
#include <ReactCommon/CallInvokerHolder.h>
910
#include <fbjni/fbjni.h>
@@ -25,6 +26,7 @@ class JFilamentProxy : public jni::HybridClass<JFilamentProxy> {
2526
// TODO(hanno): implement
2627
int loadModel(const std::string& path);
2728
std::shared_ptr<FilamentView> findFilamentView(int id);
29+
std::shared_ptr<Choreographer> createChoreographer();
2830

2931
jsi::Runtime& getRuntime();
3032

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.margelo.filament;
2+
3+
import android.view.Choreographer;
4+
5+
import androidx.annotation.Keep;
6+
7+
import com.facebook.jni.HybridData;
8+
import com.facebook.proguard.annotations.DoNotStrip;
9+
10+
/** @noinspection JavaJniMissingFunction*/
11+
public class FilamentChoreographer {
12+
/** @noinspection unused, FieldCanBeLocal */
13+
@DoNotStrip
14+
@Keep
15+
private final HybridData mHybridData;
16+
private final Choreographer choreographer;
17+
private boolean isRunning;
18+
19+
public FilamentChoreographer() {
20+
mHybridData = initHybrid();
21+
choreographer = Choreographer.getInstance();
22+
}
23+
24+
private void onFrameCallback(long timestamp) {
25+
if (!isRunning) return;
26+
onFrame(timestamp);
27+
choreographer.postFrameCallback(this::onFrameCallback);
28+
}
29+
30+
/** @noinspection unused */
31+
@DoNotStrip
32+
@Keep
33+
private synchronized void start() {
34+
if (!isRunning) {
35+
isRunning = true;
36+
choreographer.postFrameCallback(this::onFrameCallback);
37+
}
38+
}
39+
40+
/** @noinspection unused */
41+
@DoNotStrip
42+
@Keep
43+
private synchronized void stop() {
44+
if (isRunning) {
45+
isRunning = false;
46+
choreographer.removeFrameCallback(this::onFrameCallback);
47+
}
48+
}
49+
50+
private native HybridData initHybrid();
51+
private native void onFrame(long timestamp);
52+
}

package/android/src/main/java/com/margelo/filament/FilamentProxy.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ class FilamentProxy {
3636
reactContext = context;
3737
}
3838

39+
/** @noinspection unused*/
40+
@DoNotStrip
41+
@Keep
42+
FilamentChoreographer createChoreographer() {
43+
return new FilamentChoreographer();
44+
}
45+
3946
/** @noinspection unused*/
4047
@DoNotStrip
4148
@Keep

0 commit comments

Comments
 (0)