I'm trying to make BabylonReactNative to work on macOS

I’m trying to port Babylon React Native to macOS.
so I built Babylon Native on macOS, then every lib*.a file generated, only except libBabylonNative.a
how can I generate it?

P. S.) I removed XR support on my repo, since react-native-permissions doesn’t support macOS and I don’t need XR. I’m testing on M1 Mac with react-native-macos 0.68

cc @BabylonNative team

Do you have some Xcode workspace like this one ?BabylonReactNative/contents.xcworkspacedata at master · BabylonJS/BabylonReactNative · GitHub

Nope, it seems to be CMake only makes .xcodeproj file from Build Phase.

I think it’s this cmake : BabylonReactNative/CMakeLists.txt at master · BabylonJS/BabylonReactNative · GitHub

Yes, I modified that cmake and ran it.
the result list.txt says now I have to make BabylonNative.cpp.o, then run ar to achive libBabylonNative.a.

but Clang has stuck on this line:

BabylonNative.mm:224:66: error: member access into incomplete type 'struct napi_env__'
jsi::Runtime& jsiRuntime{static_cast<napi_env>(m_env)->rt};

I cannot find member rt in struct napi_env__.

ping @bghgary

@jihoobyeon Can you provide a repro? It’s hard to tell what is wrong without one.

Okay, What I did on my Mac:

  1. Build Babylon Native on macOS.
  2. Open BabylonNative.xcodeproj and set target to install and run.
  3. Navigate to Modules/@babylonjs/react-native/shared/ folder.
  4. Rename BabylonNative.cpp to BabylonNative.mm.
  5. Compile it by Clang, with -std=c++20 flag.

Then that error came up.

why rename BabylonNative.cpp to .mm?
BN supports C++17, i don’t think we’ve tested with c++20. Not sure it will have an impact.

when I ran it with .cpp, Clang complained about referring some ObjC headers like MetalKit.h and throwed error. so I had to change it to .mm.

Can you keep the .cpp and add a new .mm that will call function from the .cpp?

It also throws error. am I doing right?

// BabylonNative.mm
#include "BabylonNative.h"

void Initialize(facebook::jsi::Runtime& jsiRuntime, BabylonNative::Dispatcher jsDispatcher) {
	BabylonNative::Initialize(jsiRuntime, jsDispatcher);
}

void Deinitialize() { BabylonNative::Deinitialize(); }

void UpdateView(BabylonNative::WindowType window, size_t width, size_t height) {
	BabylonNative::UpdateView(window, width, height);
}

void UpdateMSAA(uint8_t value) { BabylonNative::UpdateMSAA(value); }

void UpdateAlphaPremultiplied(bool enabled) {
	BabylonNative::UpdateAlphaPremultiplied(enabled);
}

void RenderView() { BabylonNative::RenderView(); }
void ResetView() { BabylonNative::ResetView(); }

// Error log
Undefined symbols for architecture arm64:
  "BabylonNative::Initialize(facebook::jsi::Runtime&, std::__1::function<void (std::__1::function<void ()>)>)", referenced from:
      Initialize(facebook::jsi::Runtime&, std::__1::function<void (std::__1::function<void ()>)>) in BabylonNative-42100d.o
  "BabylonNative::RenderView()", referenced from:
      RenderView() in BabylonNative-42100d.o
  "BabylonNative::UpdateMSAA(unsigned char)", referenced from:
      UpdateMSAA(unsigned char) in BabylonNative-42100d.o
  "BabylonNative::UpdateView(MTKView*, unsigned long, unsigned long)", referenced from:
      UpdateView(MTKView*, unsigned long, unsigned long) in BabylonNative-42100d.o
  "BabylonNative::Deinitialize()", referenced from:
      Deinitialize() in BabylonNative-42100d.o
  "BabylonNative::UpdateAlphaPremultiplied(bool)", referenced from:
      UpdateAlphaPremultiplied(bool) in BabylonNative-42100d.o
  "BabylonNative::ResetView()", referenced from:
      ResetView() in BabylonNative-42100d.o
  "___gxx_personality_v0", referenced from:
      Initialize(facebook::jsi::Runtime&, std::__1::function<void (std::__1::function<void ()>)>) in BabylonNative-42100d.o
  "_main", referenced from:
     implicit entry/start for main executable
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

It seems like there is collision in some enums from napi.h and js_native_api_types.h

// napi.h
typedef enum {
  napi_default = 0,
  napi_writable = 1 << 0,
  napi_enumerable = 1 << 1,
  napi_configurable = 1 << 2,
} napi_property_attributes;

typedef enum {
  // ES6 types (corresponds to typeof)
  napi_undefined,
  napi_null,
  napi_boolean,
  napi_number,
  napi_string,
  napi_symbol,
  napi_object,
  napi_function,
  napi_external,
} napi_valuetype;

typedef enum {
  napi_int8_array,
  napi_uint8_array,
  napi_uint8_clamped_array,
  napi_int16_array,
  napi_uint16_array,
  napi_int32_array,
  napi_uint32_array,
  napi_float32_array,
  napi_float64_array,
  // JSI doesn't support bigint.
  // napi_bigint64_array,
  // napi_biguint64_array,
} napi_typedarray_type;

// js_native_api_types.h
typedef enum {
  napi_default = 0,
  napi_writable = 1 << 0,
  napi_enumerable = 1 << 1,
  napi_configurable = 1 << 2,

  // Used with napi_define_class to distinguish static properties
  // from instance properties. Ignored by napi_define_properties.
  napi_static = 1 << 10,

#if NAPI_VERSION >= 8
  // Default for class methods.
  napi_default_method = napi_writable | napi_configurable,

  // Default for object properties, like in JS obj[prop].
  napi_default_jsproperty = napi_writable |
                            napi_enumerable |
                            napi_configurable,
#endif  // NAPI_VERSION >= 8
} napi_property_attributes;

typedef enum {
  // ES6 types (corresponds to typeof)
  napi_undefined,
  napi_null,
  napi_boolean,
  napi_number,
  napi_string,
  napi_symbol,
  napi_object,
  napi_function,
  napi_external,
  napi_bigint,
} napi_valuetype;

typedef enum {
  napi_int8_array,
  napi_uint8_array,
  napi_uint8_clamped_array,
  napi_int16_array,
  napi_uint16_array,
  napi_int32_array,
  napi_uint32_array,
  napi_float32_array,
  napi_float64_array,
  napi_bigint64_array,
  napi_biguint64_array,
} napi_typedarray_type;

typedef enum {
  napi_ok,
  napi_invalid_arg,
  napi_object_expected,
  napi_string_expected,
  napi_name_expected,
  napi_function_expected,
  napi_number_expected,
  napi_boolean_expected,
  napi_array_expected,
  napi_generic_failure,
  napi_pending_exception,
  napi_cancelled,
  napi_escape_called_twice,
  napi_handle_scope_mismatch,
  napi_callback_scope_mismatch,
  napi_queue_full,
  napi_closing,
  napi_bigint_expected,
  napi_date_expected,
  napi_arraybuffer_expected,
  napi_detachable_arraybuffer_expected,
  napi_would_deadlock  // unused
} napi_status;

So I uninstalled BabylonNative headers and trying it again in local folder.

Okay, I changed method to method in docs.

I’m now able to made Playground.xcworkspace, so I ran it. then this error appears several times:
error: Unable to load contents of file list: '/Target Support Files/Pods-Playground/Pods-Playground-frameworks-Debug-input-files.xcfilelist' (in target 'Playground' from project 'Playground')

I updated my repo, so you can check it.

Sorry @jihoobyeon. I don’t think anyone on the team has much experience with React Native on macOS. I am able to repro your issue, but without knowing how the React Native for macOS part works or what you did to try to make it work, it is hard to determine what is at fault.

Maybe you can compare this with a working React Native for macOS project and see what is different?

Also, the first error in the build log is this:

…/BabylonReactNative/Apps/Playground/0.68/macos/Pods/Target Support Files/Pods-Playground/Pods-Playground.debug.xcconfig:1:1: error: unable to open configuration settings file

It seems the cocoapod configuration is messed up somehow.

OK. I understand since React Native for macOS is not popular.
Thank you for kind replies.

I will try myself more and notice here when I success.

2 Likes

Okay, I’m now here:

The app has launched, but only shows FPS info.
since there are none of error messages, and rate of FPS changes sometimes, it seems to Babylon Native is running somewhere of my Mac, but not on React Native app - which it should be.

For repro:

  1. Clone my fork.
  2. At terminal, navigate into Apps/Playground and do npm ci, npm run select 0.65.
  3. Return to root folder and navigate into Modules/@babylonjs/react-native-macos/macos. there will be a .xcodeproj file now.
  4. Open that Xcode project and set “Minimum deployments” to macOS 10.15.
  5. At target MachineIndependent, set “Precompile prefix Header” to No.
  6. Replace reference of AppRuntime_JSI.cpp to AppRuntime_JavaScriptCore.cpp, since React Native for macOS uses JavaScriptCore engine.
  7. If you are using latest Xcode, you should add -Wno-deprecated-declarations to compiler options and chage move to std::move in some files.
  8. Run project with target ALL_BUILD. if alert screen appears, choose “Update and Build”. it will make binaries for both Apple Silicon(arm64) and Intel(x86_64) Macs.
1 Like

yes, when you get the BJS version display then the JS engine and SJ script work. If you get the FPS, then most of thing are ok.
I would suggest to try to do a GPU frame capture with Xcode and see where rendering is done.

1 Like

Hmm… I see many symbols in App Lifecycle trace.
But this is my first GPU frame capture in my life, so I’m not sure what indicates spot where rendering has done.
So I uploaded trace file for you.

trace.zip (5.6 MB)

And, I found that await BabylonNative.initializationPromise; in ReactNativeEngine.js seems to not works. I tried console.log(BabylonNative); and it returned {}.
For comparison, I also did console.log(BabylonNative); at Windows - where it renders perfectely - and there, it logged Exception in HostObject::get: It is illegal to retrieve the name associated with a property symbol.

So it seems to ReactNativeEngine.js couldn’t find shared/BabylonNative.h in macOS. is this may be a clue?