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/Filament.cpp b/package/android/src/main/cpp/Filament.cpp index 23612648..cf708b8f 100644 --- a/package/android/src/main/cpp/Filament.cpp +++ b/package/android/src/main/cpp/Filament.cpp @@ -1,4 +1,5 @@ #include "FilamentInstaller.h" +#include "JChoreographer.h" #include "JFilamentProxy.h" #include "JFilamentView.h" #include "JSurfaceProvider.h" @@ -11,5 +12,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) { margelo::JFilamentProxy::registerNatives(); margelo::JSurfaceProvider::registerNatives(); margelo::JFilamentView::registerNatives(); + margelo::JChoreographer::registerNatives(); }); } 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..8e750915 --- /dev/null +++ b/package/android/src/main/cpp/java-bindings/JChoreographer.cpp @@ -0,0 +1,37 @@ +// +// Created by Marc Rousavy on 23.02.24. +// + +#include "JChoreographer.h" +#include + +namespace margelo { + +void JChoreographer::registerNatives() { + registerHybrid({ + makeNativeMethod("initHybrid", JChoreographer::initHybrid), + 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..0abe8811 --- /dev/null +++ b/package/android/src/main/java/com/margelo/filament/FilamentChoreographer.java @@ -0,0 +1,52 @@ +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 boolean isRunning; + + public FilamentChoreographer() { + mHybridData = initHybrid(); + choreographer = Choreographer.getInstance(); + } + + private void onFrameCallback(long timestamp) { + if (!isRunning) return; + onFrame(timestamp); + choreographer.postFrameCallback(this::onFrameCallback); + } + + /** @noinspection unused */ + @DoNotStrip + @Keep + private synchronized void start() { + if (!isRunning) { + isRunning = true; + choreographer.postFrameCallback(this::onFrameCallback); + } + } + + /** @noinspection unused */ + @DoNotStrip + @Keep + private synchronized void stop() { + if (isRunning) { + isRunning = false; + choreographer.removeFrameCallback(this::onFrameCallback); + } + } + + 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..36373b99 --- /dev/null +++ b/package/cpp/Choreographer.cpp @@ -0,0 +1,28 @@ +// +// Created by Marc Rousavy on 23.02.24. +// + +#include "Choreographer.h" + +namespace margelo { + +void Choreographer::loadHybridMethods() { + registerHybridMethod("addOnFrameListener", &Choreographer::addOnFrameListener, this); + registerHybridMethod("start", &Choreographer::start, this); + registerHybridMethod("stop", &Choreographer::stop, 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.cpp b/package/cpp/FilamentProxy.cpp index 0783c964..c903f2f0 100644 --- a/package/cpp/FilamentProxy.cpp +++ b/package/cpp/FilamentProxy.cpp @@ -22,6 +22,7 @@ void FilamentProxy::loadHybridMethods() { registerHybridMethod("loadModel", &FilamentProxy::loadModel, this); registerHybridMethod("findFilamentView", &FilamentProxy::findFilamentView, this); registerHybridMethod("createTestObject", &FilamentProxy::createTestObject, this); + registerHybridMethod("createChoreographer", &FilamentProxy::createChoreographer, this); } } // 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..a48f51b0 100644 --- a/package/cpp/Listener.h +++ b/package/cpp/Listener.h @@ -4,16 +4,20 @@ #pragma once +#include "jsi/HybridObject.h" #include namespace margelo { -class Listener { +class Listener : public HybridObject { public: + Listener(Listener&& listener) : _remove(std::move(listener._remove)), _isRemoved(listener._isRemoved) {} 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. }); } diff --git a/package/cpp/core/EngineWrapper.cpp b/package/cpp/core/EngineWrapper.cpp index ee193c6b..ddbf356e 100644 --- a/package/cpp/core/EngineWrapper.cpp +++ b/package/cpp/core/EngineWrapper.cpp @@ -28,7 +28,7 @@ void EngineWrapper::setSurfaceProvider(std::shared_ptr surfaceP Listener listener = surfaceProvider->addOnSurfaceChangedListener( SurfaceProvider::Callback{.onSurfaceCreated = [=](std::shared_ptr surface) { this->setSurface(surface); }, .onSurfaceDestroyed = [=](std::shared_ptr surface) { this->destroySurface(); }}); - _listener = std::make_unique(std::move(listener)); + _listener = std::make_shared(std::move(listener)); } void EngineWrapper::setSurface(std::shared_ptr surface) { diff --git a/package/cpp/core/EngineWrapper.h b/package/cpp/core/EngineWrapper.h index d3ccecb5..56528fff 100644 --- a/package/cpp/core/EngineWrapper.h +++ b/package/cpp/core/EngineWrapper.h @@ -30,7 +30,7 @@ class EngineWrapper : public HybridObject { private: std::shared_ptr _engine; std::shared_ptr _surfaceProvider; - std::unique_ptr _listener; + std::shared_ptr _listener; }; } // namespace margelo diff --git a/package/example/ios/FilamentExample.xcodeproj/project.pbxproj b/package/example/ios/FilamentExample.xcodeproj/project.pbxproj index e611ca5d..75300f0e 100644 --- a/package/example/ios/FilamentExample.xcodeproj/project.pbxproj +++ b/package/example/ios/FilamentExample.xcodeproj/project.pbxproj @@ -582,7 +582,10 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -650,7 +653,10 @@ "-DFOLLY_USE_LIBCPP=1", "-DFOLLY_CFG_NO_COROUTINES=1", ); - OTHER_LDFLAGS = "$(inherited) "; + OTHER_LDFLAGS = ( + "$(inherited)", + " ", + ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/package/ios/src/AppleChoreographer.h b/package/ios/src/AppleChoreographer.h new file mode 100644 index 00000000..4b8b5021 --- /dev/null +++ b/package/ios/src/AppleChoreographer.h @@ -0,0 +1,26 @@ +// +// AppleChoreographer.h +// Pods +// +// Created by Marc Rousavy on 23.02.24. +// + +#pragma once + +#include "Choreographer.h" +#include "DisplayLinkListener.h" + +namespace margelo { + +class AppleChoreographer : public Choreographer { +public: + explicit AppleChoreographer(); + + void stop() override; + void start() override; + +private: + DisplayLinkListener* _displayLink; +}; + +} // namespace margelo diff --git a/package/ios/src/AppleChoreographer.mm b/package/ios/src/AppleChoreographer.mm new file mode 100644 index 00000000..7b4861b9 --- /dev/null +++ b/package/ios/src/AppleChoreographer.mm @@ -0,0 +1,26 @@ +// +// AppleChoreographer.m +// DoubleConversion +// +// Created by Marc Rousavy on 23.02.24. +// + +#include "AppleChoreographer.h" + +namespace margelo { + +AppleChoreographer::AppleChoreographer() { + _displayLink = [[DisplayLinkListener alloc] initWithCallback:^(double timestamp) { + onFrame(timestamp); + }]; +} + +void AppleChoreographer::stop() { + [_displayLink stop]; +} + +void AppleChoreographer::start() { + [_displayLink start]; +} + +} // namespace margelo diff --git a/package/ios/src/AppleFilamentProxy.h b/package/ios/src/AppleFilamentProxy.h index 81d694d1..e16cb5be 100644 --- a/package/ios/src/AppleFilamentProxy.h +++ b/package/ios/src/AppleFilamentProxy.h @@ -23,6 +23,7 @@ class AppleFilamentProxy : public FilamentProxy { public: int loadModel(std::string path) override; std::shared_ptr findFilamentView(int modelId) override; + std::shared_ptr createChoreographer() override; private: jsi::Runtime* _runtime; diff --git a/package/ios/src/AppleFilamentProxy.mm b/package/ios/src/AppleFilamentProxy.mm index 9498c128..fe2a826d 100644 --- a/package/ios/src/AppleFilamentProxy.mm +++ b/package/ios/src/AppleFilamentProxy.mm @@ -6,6 +6,7 @@ // #import "AppleFilamentProxy.h" +#import "AppleChoreographer.h" #import "AppleFilamentView.h" #import "FilamentMetalView.h" #import "FilamentView.h" @@ -42,4 +43,9 @@ return std::static_pointer_cast(result); } +std::shared_ptr AppleFilamentProxy::createChoreographer() { + std::shared_ptr choreographer = std::make_shared(); + return std::static_pointer_cast(choreographer); +} + } // namespace margelo diff --git a/package/ios/src/DisplayLinkListener.h b/package/ios/src/DisplayLinkListener.h new file mode 100644 index 00000000..925417fb --- /dev/null +++ b/package/ios/src/DisplayLinkListener.h @@ -0,0 +1,21 @@ +// +// DisplayLinkListener.h +// Pods +// +// Created by Marc Rousavy on 23.02.24. +// + +#pragma once + +#import +#import + +@interface DisplayLinkListener : NSObject +typedef void (^OnFrameCallback)(double timestamp); + +- (instancetype)initWithCallback:(OnFrameCallback)callback; +- (void)start; +- (void)stop; +- (void)onFrame:(CADisplayLink*)displayLink; + +@end diff --git a/package/ios/src/DisplayLinkListener.m b/package/ios/src/DisplayLinkListener.m new file mode 100644 index 00000000..d49b071d --- /dev/null +++ b/package/ios/src/DisplayLinkListener.m @@ -0,0 +1,37 @@ +// +// DisplayLinkListener.m +// DoubleConversion +// +// Created by Marc Rousavy on 23.02.24. +// + +#import "DisplayLinkListener.h" +#import +#import + +@implementation DisplayLinkListener { + CADisplayLink* _Nullable _displayLink; + OnFrameCallback _callback; +} +- (instancetype)initWithCallback:(OnFrameCallback)callback { + if (self = [super init]) { + _callback = callback; + } + return self; +} + +- (void)start { + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onFrame:)]; + [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; +} + +- (void)stop { + [_displayLink invalidate]; + _displayLink = nil; +} + +- (void)onFrame:(CADisplayLink*)displayLink { + _callback(displayLink.timestamp); +} + +@end diff --git a/package/src/index.tsx b/package/src/index.tsx index 9dd2f791..489d264a 100644 --- a/package/src/index.tsx +++ b/package/src/index.tsx @@ -1,8 +1,5 @@ -import { testHybridObject } from './test/TestHybridObject' +import { runTests } from './test/RunTests' export * from './FilamentView' -const TEST_HYBRID_OBJECT = true -if (__DEV__ && TEST_HYBRID_OBJECT) { - testHybridObject() -} +runTests() diff --git a/package/src/native/FilamentProxy.ts b/package/src/native/FilamentProxy.ts index b78c30d9..c742c0aa 100644 --- a/package/src/native/FilamentProxy.ts +++ b/package/src/native/FilamentProxy.ts @@ -1,5 +1,15 @@ import { FilamentNativeModule } from './FilamentNativeModule' +interface Listener { + remove(): void +} + +interface Choreographer { + addOnFrameListener(onFrame: (timestamp: number) => void): Listener + start(): void + stop(): void +} + interface TestHybridObject { int: number string: string @@ -17,7 +27,10 @@ export interface TFilamentProxy { * @param path A web URL (http:// or https://), local file (file://) or resource ID. */ loadModel(path: string): number - + /** + * Create a new Choreographer instance running on the caller Thread. + */ + createChoreographer(): Choreographer /** * @private */ diff --git a/package/src/test/RunTests.ts b/package/src/test/RunTests.ts new file mode 100644 index 00000000..81d2b182 --- /dev/null +++ b/package/src/test/RunTests.ts @@ -0,0 +1,25 @@ +import { testChoreographer } from './TestChoreographer' +import { testHybridObject } from './TestHybridObject' + +async function wrapTest(name: string, func: () => void | Promise): Promise { + console.log(`-------- BEGIN TEST: ${name}`) + try { + await func() + console.log(`-------- END TEST: ${name}`) + } catch (e) { + console.error(`-------- ERROR IN TEST ${name}:`, e) + } +} + +// TODO: Write proper tests for that that actually run on a device/simulator. +// I want to make sure Hybrid Objects and Choreographers all work fine and test those in the CI. +export function runTests() { + const TEST_HYBRID_OBJECTS = true + if (__DEV__ && TEST_HYBRID_OBJECTS) { + const run = async () => { + await wrapTest('HybridObject', testHybridObject) + await wrapTest('Choreographer', testChoreographer) + } + run() + } +} diff --git a/package/src/test/TestChoreographer.ts b/package/src/test/TestChoreographer.ts new file mode 100644 index 00000000..ac8027d6 --- /dev/null +++ b/package/src/test/TestChoreographer.ts @@ -0,0 +1,28 @@ +import { FilamentProxy } from '../native/FilamentProxy' + +function timeout(ms: number): Promise { + return new Promise((resolve) => { + setTimeout(() => resolve(), ms) + }) +} + +export async function testChoreographer() { + console.log('Creating Choreographer...') + const choreographer = FilamentProxy.createChoreographer() + console.log('Created Choreographer!', choreographer, 'Adding onFrame listener...') + + let calls = 0 + choreographer.addOnFrameListener((_timestamp) => { + calls++ + }) + choreographer.start() + await timeout(1000) + console.log(`onFrame called ${calls} times in 1000ms, so it's running at ${calls} FPS! Stopping...`) + const finalCalls = calls + choreographer.stop() + await timeout(500) + if (finalCalls > calls) { + throw new Error(`Choreographer::onFrame has been called ${finalCalls - calls} times after stoppingg!`) + } + console.log('Choreographer successfully stopped!') +} diff --git a/package/src/test/TestHybridObject.ts b/package/src/test/TestHybridObject.ts index 02195d1e..5e637435 100644 --- a/package/src/test/TestHybridObject.ts +++ b/package/src/test/TestHybridObject.ts @@ -1,7 +1,6 @@ import { FilamentProxy } from '../native/FilamentProxy' export function testHybridObject() { - console.log('------ BEGIN HybridObject tests...') // 1. Creation console.log('Creating HybridObject...') const hybridObject = FilamentProxy.createTestObject() @@ -37,6 +36,4 @@ export function testHybridObject() { // 8. Create a new one const newObject = hybridObject.createNewHybridObject() console.log(`Created new hybrid object!`, newObject) - - console.log('------ END HybridObject tests!') }