Skip to content
Merged
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
4 changes: 4 additions & 0 deletions Modules/@babylonjs/react-native/android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/src/")
set(BABYLON_NATIVE_DIR "${CMAKE_CURRENT_LIST_DIR}/../submodules/BabylonNative")
add_subdirectory(${BABYLON_NATIVE_DIR} ${BABYLON_NATIVE_DIR}/build/Android_${CMAKE_ANDROID_ARCH_ABI}/)

set(BABYLON_REACT_NATIVE_SHARED_DIR "${CMAKE_CURRENT_LIST_DIR}/../shared")
add_subdirectory(${BABYLON_REACT_NATIVE_SHARED_DIR} ${CMAKE_CURRENT_BINARY_DIR}/shared)

add_library(fbjni SHARED IMPORTED)
set_target_properties(fbjni PROPERTIES
IMPORTED_LOCATION ${FBJNI_LIBPATH}/${ANDROID_ABI}/libfbjni.so
Expand Down Expand Up @@ -63,6 +66,7 @@ target_link_libraries(BabylonNative
fbjni
jsi
turbomodulejsijni
BabylonReactNativeShared
AndroidExtensions
Graphics
JsRuntime
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#include <jsi/jsi.h>
#include <ReactCommon/CallInvokerHolder.h>

#include <DispatchFunction.h>

using namespace facebook;

namespace Babylon
{
namespace
Expand All @@ -36,19 +40,10 @@ namespace Babylon
{
public:
// This class must be constructed from the JavaScript thread
Native(facebook::jsi::Runtime* jsiRuntime, std::shared_ptr<facebook::react::CallInvoker> callInvoker, ANativeWindow* windowPtr)
: m_env{ Napi::Attach<facebook::jsi::Runtime&>(*jsiRuntime) }
Native(jsi::Runtime& jsiRuntime, std::shared_ptr<react::CallInvoker> callInvoker, ANativeWindow* windowPtr)
: m_env{ Napi::Attach<jsi::Runtime&>(jsiRuntime) }
{
JsRuntime::DispatchFunctionT dispatchFunction =
[env = m_env, callInvoker](std::function<void(Napi::Env)> func)
{
callInvoker->invokeAsync([env, func = std::move(func)]
{
func(env);
});
};

m_runtime = &JsRuntime::CreateForJavaScript(m_env, dispatchFunction);
m_runtime = &JsRuntime::CreateForJavaScript(m_env, CreateJsRuntimeDispatcher(m_env, jsiRuntime, callInvoker));

auto width = static_cast<size_t>(ANativeWindow_getWidth(windowPtr));
auto height = static_cast<size_t>(ANativeWindow_getHeight(windowPtr));
Expand Down Expand Up @@ -135,10 +130,10 @@ extern "C" JNIEXPORT void JNICALL Java_com_reactlibrary_BabylonNativeInterop_res

extern "C" JNIEXPORT jlong JNICALL Java_com_reactlibrary_BabylonNativeInterop_create(JNIEnv* env, jclass obj, jlong jsiRuntimeRef, jobject jsCallInvokerHolder, jobject surface)
{
auto jsiRuntime = reinterpret_cast<facebook::jsi::Runtime*>(jsiRuntimeRef);
auto callInvoker = facebook::jni::alias_ref<facebook::react::CallInvokerHolder::javaobject> {reinterpret_cast<facebook::react::CallInvokerHolder::javaobject>(jsCallInvokerHolder)}->cthis()->getCallInvoker();
auto jsiRuntime = reinterpret_cast<jsi::Runtime*>(jsiRuntimeRef);
auto callInvoker = jni::alias_ref<react::CallInvokerHolder::javaobject> {reinterpret_cast<react::CallInvokerHolder::javaobject>(jsCallInvokerHolder)}->cthis()->getCallInvoker();
ANativeWindow* windowPtr = ANativeWindow_fromSurface(env, surface);
auto native = new Babylon::Native(jsiRuntime, callInvoker, windowPtr);
auto native = new Babylon::Native(*jsiRuntime, callInvoker, windowPtr);
return reinterpret_cast<intptr_t>(native);
}

Expand Down
19 changes: 6 additions & 13 deletions Modules/@babylonjs/react-native/ios/BabylonNative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@
#include <sstream>
#include <unistd.h>

#include <DispatchFunction.h>

namespace Babylon
{
using namespace facebook;

class Native::Impl
{
public:
Impl(facebook::jsi::Runtime* jsiRuntime, std::shared_ptr<facebook::react::CallInvoker> callInvoker)
: env{ Napi::Attach<facebook::jsi::Runtime&>(*jsiRuntime) }
Impl(facebook::jsi::Runtime& jsiRuntime, std::shared_ptr<facebook::react::CallInvoker> callInvoker)
: env{ Napi::Attach<facebook::jsi::Runtime&>(jsiRuntime) }
, jsCallInvoker{ callInvoker }
{
}
Expand All @@ -39,23 +41,14 @@ namespace Babylon
Plugins::NativeInput* nativeInput{};
};

Native::Native(facebook::jsi::Runtime* jsiRuntime, std::shared_ptr<facebook::react::CallInvoker> callInvoker, void* windowPtr, size_t width, size_t height)
Native::Native(facebook::jsi::Runtime& jsiRuntime, std::shared_ptr<facebook::react::CallInvoker> callInvoker, void* windowPtr, size_t width, size_t height)
: m_impl{ std::make_unique<Native::Impl>(jsiRuntime, callInvoker) }
{
dispatch_sync(dispatch_get_main_queue(), ^{
m_impl->m_graphics = Graphics::InitializeFromWindow<void*>(windowPtr, width, height);
});

JsRuntime::DispatchFunctionT dispatchFunction =
[env = m_impl->env, callInvoker = m_impl->jsCallInvoker](std::function<void(Napi::Env)> func)
{
callInvoker->invokeAsync([env, func = std::move(func)]
{
func(env);
});
};

m_impl->runtime = &JsRuntime::CreateForJavaScript(m_impl->env, std::move(dispatchFunction));
m_impl->runtime = &JsRuntime::CreateForJavaScript(m_impl->env, CreateJsRuntimeDispatcher(m_impl->env, jsiRuntime, callInvoker));

m_impl->m_graphics->AddToJavaScript(m_impl->env);

Expand Down
2 changes: 1 addition & 1 deletion Modules/@babylonjs/react-native/ios/BabylonNative.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Babylon
{
public:
// This class must be constructed from the JavaScript thread
Native(facebook::jsi::Runtime* jsiRuntime, std::shared_ptr<facebook::react::CallInvoker> callInvoker, void* windowPtr, size_t width, size_t height);
Native(facebook::jsi::Runtime& jsiRuntime, std::shared_ptr<facebook::react::CallInvoker> callInvoker, void* windowPtr, size_t width, size_t height);
~Native();
void Refresh(void* windowPtr, size_t width, size_t height);
void Resize(size_t width, size_t height);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ + (void)setCurrentNativeInstance:(RCTBridge*)bridge mtkView:(MTKView*)mtkView wi

jsi::Runtime* jsiRuntime = GetJSIRuntime(currentBridge);
if (jsiRuntime) {
currentNativeInstance = std::make_unique<Babylon::Native>(GetJSIRuntime(currentBridge), currentBridge.jsCallInvoker, (__bridge void*)mtkView, width, height);
currentNativeInstance = std::make_unique<Babylon::Native>(*jsiRuntime, currentBridge.jsCallInvoker, (__bridge void*)mtkView, width, height);
}
}

Expand Down
4 changes: 4 additions & 0 deletions Modules/@babylonjs/react-native/ios/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/")
set(BABYLON_NATIVE_DIR "${CMAKE_CURRENT_LIST_DIR}/../submodules/BabylonNative")
add_subdirectory(${BABYLON_NATIVE_DIR} ${BABYLON_NATIVE_DIR}/build/ios/)

set(BABYLON_REACT_NATIVE_SHARED_DIR "${CMAKE_CURRENT_LIST_DIR}/../shared")
add_subdirectory(${BABYLON_REACT_NATIVE_SHARED_DIR} ${CMAKE_CURRENT_BINARY_DIR}/shared)

add_library(BabylonNative
${CMAKE_CURRENT_LIST_DIR}/BabylonNative.h
${CMAKE_CURRENT_LIST_DIR}/BabylonNative.cpp)
Expand All @@ -38,6 +41,7 @@ target_link_libraries(BabylonNative
Graphics
jsi
reactnative
BabylonReactNativeShared
JsRuntime
NativeWindow
NativeEngine
Expand Down
2 changes: 2 additions & 0 deletions Modules/@babylonjs/react-native/shared/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_library(BabylonReactNativeShared INTERFACE)
target_include_directories(BabylonReactNativeShared INTERFACE ".")
46 changes: 46 additions & 0 deletions Modules/@babylonjs/react-native/shared/DispatchFunction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

#include <Babylon/JsRuntime.h>

#include <jsi/jsi.h>
#include <ReactCommon/CallInvoker.h>

namespace Babylon
{
using namespace facebook;

// Creates a JsRuntime::DispatchFunctionT that integrates with the React Native execution environment.
inline JsRuntime::DispatchFunctionT CreateJsRuntimeDispatcher(Napi::Env env, jsi::Runtime& jsiRuntime, std::shared_ptr<react::CallInvoker> callInvoker)
{
return [env, &jsiRuntime, callInvoker](std::function<void(Napi::Env)> func)
{
// Ideally we would just use CallInvoker::invokeAsync directly, but currently it does not seem to integrate well with the React Native logbox.
// To work around this, we wrap all functions in a try/catch, and when there is an exception, we do the following:
// 1. Call the JavaScript setImmediate function.
// 2. Have the setImmediate callback call back into native code (throwFunc).
// 3. Re-throw the exception from throwFunc.
// This works because:
// 1. setImmediate queues the callback, and that queue is drained immediately following the invocation of the function passed to CallInvoker::invokeAsync.
// 2. The immediates queue is drained as part of the class bridge, which knows how to display the logbox for unhandled exceptions.
// In the future, CallInvoker::invokeAsync likely will properly integrate with logbox, at which point we can remove the try/catch and just call func directly.
callInvoker->invokeAsync([env, &jsiRuntime, func{std::move(func)}]
{
try
{
func(env);
}
catch (...)
{
auto ex{std::current_exception()};
auto setImmediate{jsiRuntime.global().getPropertyAsFunction(jsiRuntime, "setImmediate")};
auto throwFunc{jsi::Function::createFromHostFunction(jsiRuntime, jsi::PropNameID::forAscii(jsiRuntime, "throwFunc"), 0,
[ex](jsi::Runtime &, const jsi::Value &, const jsi::Value *, size_t) -> jsi::Value
{
std::rethrow_exception(ex);
})};
setImmediate.call(jsiRuntime, {std::move(throwFunc)});
}
});
};
}
}