@@ -14,12 +14,18 @@ namespace Babylon
1414{
1515 using namespace facebook ;
1616
17+ namespace
18+ {
19+ Dispatcher g_inlineDispatcher{[](const std::function<void ()>& func) { func (); }};
20+ }
21+
1722 class ReactNativeModule : public jsi ::HostObject
1823 {
1924 public:
20- ReactNativeModule (jsi::Runtime& jsiRuntime, Dispatcher jsDispatcher)
25+ ReactNativeModule (jsi::Runtime& jsiRuntime, Dispatcher jsDispatcher, bool autoRender )
2126 : m_env{ Napi::Attach<facebook::jsi::Runtime&>(jsiRuntime) }
2227 , m_jsDispatcher{ std::move (jsDispatcher) }
28+ , m_autoRender{ autoRender }
2329 , m_isRunning{ std::make_shared<bool >(true ) }
2430 {
2531 // Initialize a JS promise that will be returned by whenInitialized, and completed when NativeEngine is initialized.
@@ -71,18 +77,28 @@ namespace Babylon
7177
7278 void UpdateView (void * windowPtr, size_t width, size_t height)
7379 {
74- // TODO: We shouldn't have to dispatch to the JS thread most of this, but not doing so results in a crash.
75- // I don't understand the issue yet, but for now just retain the pre-refactor logic. We'll need to
76- // resolve this to enable manual non-JS thread rendering. Note this only repros in release builds
77- // where we actually call ResetView.
78- m_jsDispatcher ([this , windowPtr, width, height]()
80+ // TODO: We shouldn't have to dispatch to the JS thread for CreateGraphics/UpdateWindow/UpdateSize, but not doing so results in a crash.
81+ // I don't understand the issue yet, but for now just retain the pre-refactor logic. We'll need to resolve this to enable manual
82+ // non-JS thread rendering. Note this only repros in release builds where we actually call ResetView.
83+
84+ // When auto rendering is enabled, we render from the JS thread. In this case, we dispatch to the JS thread to initialize/update graphics,
85+ // and stay on this thread (with an inline dispatcher) to interact with the JS runtime.
86+ // When auto rendering is disabled, we render from a different thread. In this case, we assume this function was called from the render thread
87+ // and do an inline dispatch (e.g. execute synchronously on the calling thread), and switch to the JS thread to interact with the JS runtime.
88+ auto renderDispatcher = m_autoRender ? m_jsDispatcher : g_inlineDispatcher;
89+ auto jsDispatcher = m_autoRender ? g_inlineDispatcher : m_jsDispatcher;
90+
91+ renderDispatcher ([this , windowPtr, width, height, jsDispatcher{ std::move (jsDispatcher) }]()
7992 {
8093 if (!m_graphics)
8194 {
8295 m_graphics = Graphics::CreateGraphics (windowPtr, width, height);
83- m_graphics->AddToJavaScript (m_env);
84- Plugins::NativeEngine::Initialize (m_env, true );
85- m_resolveInitPromise ();
96+ jsDispatcher ([this ]()
97+ {
98+ m_graphics->AddToJavaScript (m_env);
99+ Plugins::NativeEngine::Initialize (m_env, m_autoRender);
100+ m_resolveInitPromise ();
101+ });
86102 }
87103 else
88104 {
@@ -93,12 +109,24 @@ namespace Babylon
93109 });
94110 }
95111
112+ void RenderView ()
113+ {
114+ if (m_autoRender)
115+ {
116+ throw std::runtime_error{ " RenderView can only be called when automatic rendering is disabled." };
117+ }
118+
119+ m_graphics->RenderCurrentFrame ();
120+ }
121+
96122 void ResetView ()
97123 {
98124 // TODO: We shouldn't have to dispatch to the JS thread for this since we are already on the JS thread,
99125 // but there is an issue in NativeEngine where it will Dispatch a call to RenderCurrentFrame, then
100126 // get disposed, then try to actually render the frame. This results in immediately re-enabling
101127 // graphics after disabling it here. For now, retain the pre-refactor logic (queueing on the JS thread).
128+ // TODO: This is called from JS code (and therefore the JS thread), so we need to figure out a good way
129+ // to get on the proper (render) thread to make this call.
102130 m_jsDispatcher ([this ]()
103131 {
104132 if (m_graphics)
@@ -170,6 +198,7 @@ namespace Babylon
170198
171199 Napi::Env m_env;
172200 Dispatcher m_jsDispatcher{};
201+ bool m_autoRender{};
173202
174203 std::shared_ptr<bool > m_isRunning{};
175204 std::unique_ptr<Graphics> m_graphics{};
@@ -184,11 +213,11 @@ namespace Babylon
184213 std::weak_ptr<ReactNativeModule> g_nativeModule{};
185214 }
186215
187- void Initialize (facebook::jsi::Runtime& jsiRuntime, Dispatcher jsDispatcher)
216+ void Initialize (facebook::jsi::Runtime& jsiRuntime, Dispatcher jsDispatcher, bool autoRender )
188217 {
189218 if (!jsiRuntime.global ().hasProperty (jsiRuntime, JS_INSTANCE_NAME))
190219 {
191- auto nativeModule{ std::make_shared<ReactNativeModule>(jsiRuntime, jsDispatcher) };
220+ auto nativeModule{ std::make_shared<ReactNativeModule>(jsiRuntime, jsDispatcher, autoRender ) };
192221 jsiRuntime.global ().setProperty (jsiRuntime, JS_INSTANCE_NAME, jsi::Object::createFromHostObject (jsiRuntime, nativeModule));
193222 g_nativeModule = nativeModule;
194223 }
@@ -214,6 +243,18 @@ namespace Babylon
214243 }
215244 }
216245
246+ void RenderView ()
247+ {
248+ if (auto nativeModule{ g_nativeModule.lock () })
249+ {
250+ nativeModule->RenderView ();
251+ }
252+ else
253+ {
254+ throw std::runtime_error{ " RenderView must not be called before Initialize." };
255+ }
256+ }
257+
217258 void SetPointerButtonState (uint32_t pointerId, uint32_t buttonId, bool isDown, uint32_t x, uint32_t y)
218259 {
219260 if (auto nativeModule{ g_nativeModule.lock () })
0 commit comments