diff --git a/package/android/CMakeLists.txt b/package/android/CMakeLists.txt index fc483bfc..5e893331 100644 --- a/package/android/CMakeLists.txt +++ b/package/android/CMakeLists.txt @@ -22,6 +22,7 @@ add_library( ../cpp/FilamentProxy.cpp ../cpp/Surface.cpp ../cpp/SurfaceProvider.cpp + ../cpp/Choreographer.cpp ../cpp/Listener.cpp ../cpp/jsi/HybridObject.cpp ../cpp/jsi/Promise.cpp @@ -34,6 +35,7 @@ add_library( src/main/cpp/JNISharedPtr.cpp src/main/cpp/FilamentInstaller.cpp src/main/cpp/java-bindings/JFilamentProxy.cpp + src/main/cpp/java-bindings/JChoreographer.cpp src/main/cpp/java-bindings/JFilamentView.cpp src/main/cpp/java-bindings/JSurfaceProvider.cpp ) diff --git a/package/android/src/main/cpp/AndroidFilamentProxy.cpp b/package/android/src/main/cpp/AndroidFilamentProxy.cpp index d4d4e545..4aed7b98 100644 --- a/package/android/src/main/cpp/AndroidFilamentProxy.cpp +++ b/package/android/src/main/cpp/AndroidFilamentProxy.cpp @@ -25,4 +25,8 @@ std::shared_ptr AndroidFilamentProxy::findFilamentView(int id) { return _proxy->cthis()->findFilamentView(id); } +std::shared_ptr AndroidFilamentProxy::createChoreographer() { + return _proxy->cthis()->createChoreographer(); +} + } // namespace margelo diff --git a/package/android/src/main/cpp/AndroidFilamentProxy.h b/package/android/src/main/cpp/AndroidFilamentProxy.h index a56b2c89..2981ac0f 100644 --- a/package/android/src/main/cpp/AndroidFilamentProxy.h +++ b/package/android/src/main/cpp/AndroidFilamentProxy.h @@ -25,6 +25,7 @@ class AndroidFilamentProxy : public FilamentProxy { // TODO(hanno): implement int loadModel(std::string path) override; std::shared_ptr findFilamentView(int id) override; + std::shared_ptr createChoreographer() override; private: jni::global_ref _proxy; diff --git a/package/android/src/main/cpp/java-bindings/JChoreographer.cpp b/package/android/src/main/cpp/java-bindings/JChoreographer.cpp new file mode 100644 index 00000000..d9f86b00 --- /dev/null +++ b/package/android/src/main/cpp/java-bindings/JChoreographer.cpp @@ -0,0 +1,39 @@ +// +// Created by Marc Rousavy on 23.02.24. +// + +#include "JChoreographer.h" +#include + +namespace margelo { + +void JChoreographer::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JChoreographer::initHybrid), + makeNativeMethod("start", JChoreographer::start), + makeNativeMethod("stop", JChoreographer::stop), + makeNativeMethod("onFrame", JChoreographer::onFrameLong), + }); +} + +JChoreographer::JChoreographer(const jni::alias_ref& javaThis) : _javaPart(jni::make_global(javaThis)) {} + +jni::local_ref JChoreographer::initHybrid(jni::alias_ref javaThis) { + return makeCxxInstance(javaThis); +} + +void JChoreographer::onFrameLong(jlong timestamp) { + onFrame(static_cast(timestamp)); +} + +void JChoreographer::start() { + static const auto method = javaClassLocal()->getMethod("start"); + method(_javaPart); +} + +void JChoreographer::stop() { + static const auto method = javaClassLocal()->getMethod("stop"); + method(_javaPart); +} + +} // namespace margelo \ No newline at end of file diff --git a/package/android/src/main/cpp/java-bindings/JChoreographer.h b/package/android/src/main/cpp/java-bindings/JChoreographer.h new file mode 100644 index 00000000..8d776651 --- /dev/null +++ b/package/android/src/main/cpp/java-bindings/JChoreographer.h @@ -0,0 +1,33 @@ +// +// Created by Marc Rousavy on 23.02.24. +// + +#pragma once + +#include "Choreographer.h" +#include + +namespace margelo { + +using namespace facebook; + +class JChoreographer : public jni::HybridClass, public Choreographer { +public: + static void registerNatives(); + + void start() override; + void stop() override; + void onFrameLong(jlong timestamp); + +private: + friend HybridBase; + jni::global_ref _javaPart; + static auto constexpr TAG = "JChoreographer"; + static auto constexpr kJavaDescriptor = "Lcom/margelo/filament/FilamentChoreographer;"; + +private: + explicit JChoreographer(const jni::alias_ref& javaThis); + static jni::local_ref initHybrid(jni::alias_ref javaThis); +}; + +} // namespace margelo diff --git a/package/android/src/main/cpp/java-bindings/JFilamentProxy.cpp b/package/android/src/main/cpp/java-bindings/JFilamentProxy.cpp index b2655f21..8093dcbd 100644 --- a/package/android/src/main/cpp/java-bindings/JFilamentProxy.cpp +++ b/package/android/src/main/cpp/java-bindings/JFilamentProxy.cpp @@ -3,6 +3,7 @@ // #include "JFilamentProxy.h" +#include "JChoreographer.h" #include "JFilamentView.h" #include "JNISharedPtr.h" #include @@ -32,6 +33,14 @@ std::shared_ptr JFilamentProxy::findFilamentView(int id) { return std::static_pointer_cast(sharedRef); } +std::shared_ptr JFilamentProxy::createChoreographer() { + static const auto method = javaClassLocal()->getMethod()>("createChoreographer"); + jni::local_ref choreographer = method(_javaPart); + jni::global_ref globalRef = jni::make_global(choreographer); + std::shared_ptr sharedRef = JNISharedPtr::make_shared_from_jni(globalRef); + return std::static_pointer_cast(sharedRef); +} + jsi::Runtime& JFilamentProxy::getRuntime() { if (_runtime == nullptr) { [[unlikely]]; diff --git a/package/android/src/main/cpp/java-bindings/JFilamentProxy.h b/package/android/src/main/cpp/java-bindings/JFilamentProxy.h index 290ee98d..e5781452 100644 --- a/package/android/src/main/cpp/java-bindings/JFilamentProxy.h +++ b/package/android/src/main/cpp/java-bindings/JFilamentProxy.h @@ -4,6 +4,7 @@ #pragma once +#include "Choreographer.h" #include "FilamentView.h" #include #include @@ -25,6 +26,7 @@ class JFilamentProxy : public jni::HybridClass { // TODO(hanno): implement int loadModel(const std::string& path); std::shared_ptr findFilamentView(int id); + std::shared_ptr createChoreographer(); jsi::Runtime& getRuntime(); diff --git a/package/android/src/main/java/com/margelo/filament/FilamentChoreographer.java b/package/android/src/main/java/com/margelo/filament/FilamentChoreographer.java new file mode 100644 index 00000000..c5cba420 --- /dev/null +++ b/package/android/src/main/java/com/margelo/filament/FilamentChoreographer.java @@ -0,0 +1,45 @@ +package com.margelo.filament; + +import android.view.Choreographer; + +import androidx.annotation.Keep; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; + +/** @noinspection JavaJniMissingFunction*/ +public class FilamentChoreographer { + /** @noinspection unused, FieldCanBeLocal */ + @DoNotStrip + @Keep + private final HybridData mHybridData; + private final Choreographer choreographer; + private final Choreographer.FrameCallback frameCallback; + private boolean isRunning; + + public FilamentChoreographer() { + mHybridData = initHybrid(); + choreographer = Choreographer.getInstance(); + frameCallback = timestamp -> { + if (!isRunning) return; + onFrame(timestamp); + }; + } + + private synchronized void start() { + if (!isRunning) { + isRunning = true; + choreographer.postFrameCallback(frameCallback); + } + } + + private synchronized void stop() { + if (isRunning) { + isRunning = false; + choreographer.removeFrameCallback(frameCallback); + } + } + + private native HybridData initHybrid(); + private native void onFrame(long timestamp); +} diff --git a/package/android/src/main/java/com/margelo/filament/FilamentProxy.java b/package/android/src/main/java/com/margelo/filament/FilamentProxy.java index 01113138..80c70787 100644 --- a/package/android/src/main/java/com/margelo/filament/FilamentProxy.java +++ b/package/android/src/main/java/com/margelo/filament/FilamentProxy.java @@ -36,6 +36,13 @@ class FilamentProxy { reactContext = context; } + /** @noinspection unused*/ + @DoNotStrip + @Keep + FilamentChoreographer createChoreographer() { + return new FilamentChoreographer(); + } + /** @noinspection unused*/ @DoNotStrip @Keep diff --git a/package/cpp/Choreographer.cpp b/package/cpp/Choreographer.cpp new file mode 100644 index 00000000..0ece67c2 --- /dev/null +++ b/package/cpp/Choreographer.cpp @@ -0,0 +1,26 @@ +// +// Created by Marc Rousavy on 23.02.24. +// + +#include "Choreographer.h" + +namespace margelo { + +void Choreographer::loadHybridMethods() { + registerHybridMethod("setOnFrameCallback", &Choreographer::addOnFrameListener, this); +} + +std::shared_ptr Choreographer::addOnFrameListener(Choreographer::OnFrameCallback onFrameCallback) { + _callbacks.push_back(std::move(onFrameCallback)); + return std::make_shared([]() { + // TODO: Find a safe way to remove this listener from the vector. + }); +} + +void Choreographer::onFrame(double timestamp) { + for (const auto& callback : _callbacks) { + callback(timestamp); + } +} + +} // namespace margelo \ No newline at end of file diff --git a/package/cpp/Choreographer.h b/package/cpp/Choreographer.h new file mode 100644 index 00000000..7f69d16a --- /dev/null +++ b/package/cpp/Choreographer.h @@ -0,0 +1,31 @@ +// +// Created by Marc Rousavy on 23.02.24. +// + +#pragma once + +#include "Listener.h" +#include "jsi/HybridObject.h" +#include + +namespace margelo { + +class Choreographer : public HybridObject { +public: + using OnFrameCallback = std::function; + + std::shared_ptr addOnFrameListener(OnFrameCallback onFrameCallback); + + virtual void start() = 0; + virtual void stop() = 0; + +protected: + void onFrame(double timestamp); + + void loadHybridMethods() override; + +private: + std::vector _callbacks; +}; + +} // namespace margelo diff --git a/package/cpp/FilamentProxy.h b/package/cpp/FilamentProxy.h index e29d428d..e5239fa4 100644 --- a/package/cpp/FilamentProxy.h +++ b/package/cpp/FilamentProxy.h @@ -4,11 +4,10 @@ #pragma once -#include - #include #include +#include "Choreographer.h" #include "FilamentView.h" #include "jsi/HybridObject.h" #include "test/TestHybridObject.h" @@ -21,6 +20,7 @@ class FilamentProxy : public HybridObject { private: virtual int loadModel(std::string path) = 0; virtual std::shared_ptr findFilamentView(int id) = 0; + virtual std::shared_ptr createChoreographer() = 0; std::shared_ptr createTestObject(); diff --git a/package/cpp/Listener.cpp b/package/cpp/Listener.cpp index 33bbae83..0345e47c 100644 --- a/package/cpp/Listener.cpp +++ b/package/cpp/Listener.cpp @@ -20,4 +20,8 @@ void Listener::remove() { _isRemoved = true; } +void Listener::loadHybridMethods() { + registerHybridMethod("remove", &Listener::remove, this); +} + } // namespace margelo \ No newline at end of file diff --git a/package/cpp/Listener.h b/package/cpp/Listener.h index 6157689d..eeefb3b0 100644 --- a/package/cpp/Listener.h +++ b/package/cpp/Listener.h @@ -4,16 +4,19 @@ #pragma once +#include "jsi/HybridObject.h" #include namespace margelo { -class Listener { +class Listener : public HybridObject { public: explicit Listener(std::function remove); ~Listener(); void remove(); + void loadHybridMethods() override; + private: std::function _remove; bool _isRemoved; diff --git a/package/cpp/SurfaceProvider.cpp b/package/cpp/SurfaceProvider.cpp index 4a8df03a..755da2ee 100644 --- a/package/cpp/SurfaceProvider.cpp +++ b/package/cpp/SurfaceProvider.cpp @@ -14,13 +14,8 @@ Listener SurfaceProvider::addOnSurfaceChangedListener(margelo::SurfaceProvider:: std::unique_lock lock(_mutex); _callbacks.push_back(std::move(callback)); - size_t index = _callbacks.size(); - - return Listener([weakThis = this, index]() { - if (weakThis != nullptr) { - std::unique_lock lock(weakThis->_mutex); - weakThis->_callbacks.erase(weakThis->_callbacks.begin() + index); - } + return Listener([]() { + // TODO: Find a safe way to remove this listener from the vector. }); }