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
8 changes: 0 additions & 8 deletions Modules/@babylonjs/react-native/EngineHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,6 @@ export function useEngine(): Engine | undefined {
}
})();

// NOTE: This is a workaround for https://github.com/BabylonJS/BabylonReactNative/issues/60
function heartbeat() {
if (!disposed) {
setTimeout(heartbeat, 10);
}
}
heartbeat();

return () => {
disposed = true;
setEngine(engine => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include <jsi/jsi.h>

#include "../../../../shared/Shared.h"

namespace Babylon
{
namespace
Expand All @@ -39,14 +41,29 @@ namespace Babylon
Native(facebook::jsi::Runtime* jsiRuntime, ANativeWindow* windowPtr)
: m_env{ Napi::Attach<facebook::jsi::Runtime&>(*jsiRuntime) }
{
auto looper_scheduler = std::make_shared<looper_scheduler_t>(looper_scheduler_t::get_for_current_thread());
struct DispatchData
{
using looper_scheduler_t = arcana::looper_scheduler<128>;

looper_scheduler_t scheduler;
Napi::FunctionReference flushedQueue;

JsRuntime::DispatchFunctionT dispatchFunction{[env = m_env, looper_scheduler = std::move(looper_scheduler)](std::function<void(Napi::Env)> func) {
(*looper_scheduler)([env, func = std::move(func)]()
DispatchData(Napi::Env env)
: scheduler{ looper_scheduler_t::get_for_current_thread() }
, flushedQueue{ GetFlushedQueue(env) }
{
func(env);
});
}};
}
};

JsRuntime::DispatchFunctionT dispatchFunction =
[env = m_env, data = std::make_shared<DispatchData>(m_env)](std::function<void(Napi::Env)> func)
{
(data->scheduler)([env, func = std::move(func), &data]()
{
func(env);
data->flushedQueue.Call({});
});
};

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

Expand Down Expand Up @@ -100,8 +117,6 @@ namespace Babylon
}

private:
using looper_scheduler_t = arcana::looper_scheduler<sizeof(std::weak_ptr<Napi::Env>) + sizeof(std::function<void(Napi::Env)>)>;

std::unique_ptr<Graphics> m_graphics{};
Napi::Env m_env;
JsRuntime* m_runtime;
Expand Down
28 changes: 21 additions & 7 deletions Modules/@babylonjs/react-native/ios/BabylonNative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <sstream>
#include <unistd.h>

#include "../shared/Shared.h"

namespace Babylon
{
using namespace facebook;
Expand All @@ -43,15 +45,27 @@ namespace Babylon
m_impl->m_graphics = Graphics::InitializeFromWindow<void*>(windowPtr, width, height);
});

auto run_loop_scheduler = std::make_shared<arcana::run_loop_scheduler>(arcana::run_loop_scheduler::get_for_current_thread());

JsRuntime::DispatchFunctionT dispatchFunction{[env = m_impl->env, run_loop_scheduler = std::move(run_loop_scheduler)](std::function<void(Napi::Env)> func)
struct DispatchData
{
(*run_loop_scheduler)([env, func = std::move(func)]()
arcana::run_loop_scheduler scheduler;
Napi::FunctionReference flushedQueue;

DispatchData(Napi::Env env)
: scheduler{ arcana::run_loop_scheduler::get_for_current_thread() }
, flushedQueue{ GetFlushedQueue(env) }
{
}
};

JsRuntime::DispatchFunctionT dispatchFunction =
[env = m_impl->env, data = std::make_shared<DispatchData>(m_impl->env)](std::function<void(Napi::Env)> func)
{
func(env);
});
}};
(data->scheduler)([env, func = std::move(func), &data]()
{
func(env);
data->flushedQueue.Call({});
});
};

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

Expand Down
18 changes: 18 additions & 0 deletions Modules/@babylonjs/react-native/shared/Shared.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <napi/napi.h>

// See https://github.com/BabylonJS/BabylonReactNative/issues/60 for original issue.
// This is a work around to poke the React Native message queue to run setImmediate callbacks.
// React Native uses a custom promise implementation based on setImmediate. The promise
// continuations will only continue once these setImmediate callbacks are triggered by the
// flushedQueue call. This is explicitly called at the end of JsRuntime's dispatch function
// to flush the queue.
inline Napi::FunctionReference GetFlushedQueue(Napi::Env env)
{
// HACK: The __fbBatchedBridge global is an internal implementation of React Native.
// This hack will break if React Native internals changes, but it should blow up right here.
auto batchedBridge{ env.Global().Get("__fbBatchedBridge").As<Napi::Object>() };
auto flushedQueue{ batchedBridge.Get("flushedQueue").As<Napi::Function>() };
return Napi::Persistent(flushedQueue);
}