Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Magic Leap 2 Integration #840

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

austinbhale
Copy link
Contributor

@austinbhale austinbhale commented Jan 17, 2024

Hey Nick, the Magic Leap team is loaning us a device so I'll be working on its integration in my spare time. I'll be keeping this PR updated in the meantime, so that it's easier to show you updates and ask questions.

Progress

Supported Extensions

馃敶 not supported by this PR, unless requested in the comments
馃煛 work in progress
馃煝 active

Extension Supported
XR_EXT_conformance_automation 馃敶
XR_EXT_debug_utils 馃敶
XR_EXT_eye_gaze_interaction 馃煝
XR_EXT_hand_interaction 馃煝
XR_EXT_hand_tracking 馃煝
XR_EXT_local_floor 馃煝
XR_EXT_palm_pose 馃煝
XR_EXT_plane_detection 馃敶
XR_EXT_view_configuration_depth_range 馃敶
XR_ML_compat 馃敶
XR_ML_frame_end_info 馃敶
XR_ML_global_dimmer 馃敶
XR_ML_localization_map 馃敶
XR_ML_marker_understanding 馃敶
XR_ML_ml2_controller_interaction 馃煛
XR_ML_private 馃敶
XR_ML_session_performance_info 馃敶
XR_ML_system_notifications 馃敶
XR_ML_user_calibration 馃敶
XR_MSFT_unbounded_reference_space 馃煝

Known issues

  • Very apparent stutter (& sometimes colorful artifacts) when you either move your head too quickly or move around.
    This may be a limitation of the on-device SLAM, which causes perceived motion of stationary content. For example, reading UI text while moving around becomes illegible as it seemingly "repeats" multiple times until the tracking restabilizes. This is probably why ML2 offers advice on how to mitigate user micro-movements for UI. If StereoKit is in immediate mode, is it correct to assume there's no buffer in place?
    Please note that this visual artifact won't be seen in the mixed reality video.
  • Only Release mode works for StereoKitTest_NetAndroid

Initialization Log for Debug

This debug engine does not support exception conditions. The condition(s) will be ignored.
Start debugging Android application ...
Forwarding debugger port 8832
Detecting existing process
> am start -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -n "net.stereokit.stereokittest_netandroid/crc64fb6d86f73153b469.MainActivity"
> Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=net.stereokit.stereokittest_netandroid/crc64fb6d86f73153b469.MainActivity }
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/StereoKitTest_NetAndroid.dll
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/Mono.Android.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Runtime.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/Java.Interop.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Runtime.InteropServices.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Collections.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Threading.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Threading.Thread.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Diagnostics.StackTrace.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Memory.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Reflection.Emit.Lightweight.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Reflection.Emit.ILGeneration.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Reflection.Primitives.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Console.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/StereoKit.dll
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/netstandard.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Collections.Concurrent.dll [External]
Thread started:  #2
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Numerics.Vectors.dll [External]
Loaded assembly: /data/data/net.stereokit.stereokittest_netandroid/files/.__override__/System.Linq.dll [External]
[test_netandroi] Late-enabling -Xcheck:jni
[test_netandroi] Unknown bits set in runtime_flags: 0x8000
[debug-app-helper] Checking if libmonodroid was unpacked to /data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64/libmonodroid.so
[debug-app-helper] Native libs extracted to /data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64, assuming application/android:extractNativeLibs == true
[debug-app-helper] Setting up for DSO lookup in app data directories
[debug-app-helper] Added filesystem DSO lookup location: /data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64
[debug-app-helper] Using runtime path: /data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64
[debug-app-helper] checking directory: `/data/user/0/net.stereokit.stereokittest_netandroid/files/.__override__/lib`
[debug-app-helper] directory does not exist: `/data/user/0/net.stereokit.stereokittest_netandroid/files/.__override__/lib`
[debug-app-helper] Checking whether Mono runtime exists at: /data/user/0/net.stereokit.stereokittest_netandroid/files/.__override__/libmonosgen-2.0.so
[debug-app-helper] Checking whether Mono runtime exists at: /data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64/libmonosgen-2.0.so
[debug-app-helper] Mono runtime found at: /data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64/libmonosgen-2.0.so
[test_netandroi] Attempt to remove non-JNI local reference, dumping thread
[DOTNET] JNI_OnLoad: JNI_OnLoad in pal_jni.c
[DOTNET] GetOptionalClassGRef: optional class com/android/org/conscrypt/OpenSSLEngineImpl was not found
[monodroid] Creating public update directory: `/data/user/0/net.stereokit.stereokittest_netandroid/files/.__override__`
[test_netandroi] Attempt to remove non-JNI local reference, dumping thread
[monodroid-debug] Trying to initialize the debugger with options: --debugger-agent=transport=dt_socket,loglevel=0,address=127.0.0.1:8832,server=y,embedding=1,timeout=1705611952
[monodroid-assembly] open_from_bundles: failed to load assembly StereoKitTest_NetAndroid.dll
[monodroid-gc] GREF GC Threshold: 46080
[monodroid-assembly] open_from_bundles: failed to load assembly Mono.Android.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Runtime.dll
[monodroid-assembly] open_from_bundles: failed to load assembly Java.Interop.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Runtime.InteropServices.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Collections.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Threading.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Threading.Thread.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Diagnostics.StackTrace.dll
[test_netandroi] Attempt to remove non-JNI local reference, dumping thread
[monodroid-assembly] open_from_bundles: failed to load assembly System.Memory.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Reflection.Emit.Lightweight.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Reflection.Emit.ILGeneration.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Reflection.Primitives.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Console.dll
[] Image Resolution : 24 x 24, MimeType : image/png
[] Image Resolution : 24 x 24, MimeType : image/png
[] Image Resolution : 162 x 162, MimeType : image/png
[chatty] uid=10152(net.stereokit.stereokittest_netandroid) identical 1 line
[] Image Resolution : 162 x 162, MimeType : image/png
[monodroid-assembly] open_from_bundles: failed to load assembly StereoKit.dll
[monodroid-assembly] open_from_bundles: failed to load assembly netstandard.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Collections.Concurrent.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Numerics.Vectors.dll
[monodroid-assembly] open_from_bundles: failed to load assembly System.Linq.dll
[StereoKit] Initializing StereoKit v0.3.9 Android x64...
[StereoKit] Initializing Platform
[EGL-MAIN] found extension DRI_Core version 2
[EGL-MAIN] found extension DRI_IMAGE_DRIVER version 1
[EGL-MAIN] found extension DRI_ConfigOptions version 2
[net.stereokit.stereokittest_netandroid] type=1400 audit(0.0:600): avc: denied { search } for comm=53746572656F4B6974204D61696E name="traces" dev="nvme0n1p37" ino=8871939 scontext=u:r:untrusted_app:s0:c152,c256,c512,c768 tcontext=u:object_r:trace_data_file:s0 tclass=dir permissive=0
[chatty] uid=10152(net.stereokit.stereokittest_netandroid) identical 6 lines
[net.stereokit.stereokittest_netandroid] type=1400 audit(0.0:607): avc: denied { search } for comm=53746572656F4B6974204D61696E name="traces" dev="nvme0n1p37" ino=8871939 scontext=u:r:untrusted_app:s0:c152,c256,c512,c768 tcontext=u:object_r:trace_data_file:s0 tclass=dir permissive=0
[EGL-MAIN] found extension DRI_TexBuffer version 2
[EGL-MAIN] found extension DRI2_Flush version 4
[EGL-MAIN] found extension DRI_IMAGE version 17
[EGL-MAIN] found extension DRI_IMAGE version 17
[EGL-MAIN] found extension DRI_RENDERER_QUERY version 1
[EGL-MAIN] found extension DRI_CONFIG_QUERY version 1
[EGL-MAIN] found extension DRI2_Fence version 2
[EGL-MAIN] found extension DRI2_Interop version 1
[EGL-MAIN] found extension DRI2_BufferDamage version 1
[EGL-MAIN] found extension DRI_Robustness version 1
[EGL-MAIN] found extension DRI_NoError version 1
[EGL-MAIN] found extension DRI2_Blob version 1
[StereoKit] [sk_gpu] EGL version 1.5
[StereoKit] [sk_gpu] Using OpenGL: OpenGL ES 3.2 Mesa 20.1.2 (git-6582e67fb7)
[StereoKit] [sk_gpu] Device: AMD MERO (DRM 3.37.0, 5.4.19-android-x86_64, LLVM 10.0)
[StereoKit] [sk_gpu] Debug info enabled.
[StereoKit] Starting a XR mode app
[OpenXR-Loader] Verbose [GENERAL | xrInitializeLoaderKHR | OpenXR-Loader] : Entering loader trampoline
[StereoKit] Runtime OpenXR Extensions:
[StereoKit] ___________________________________
[StereoKit] |     Usage | Extension
[StereoKit] |-----------|----------------------
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : Entering loader trampoline
[OpenXR-Loader] getActiveRuntimeCursor: Querying URI: content://org.khronos.openxr.runtime_broker/openxr/1/abi/x86_64/runtimes/active/0
[OpenXR-Loader] Null cursor when querying installable content resolver.
[OpenXR-Loader] getActiveRuntimeCursor: Querying URI: content://org.khronos.openxr.system_runtime_broker/openxr/1/abi/x86_64/runtimes/active/0
[OpenXR-Loader] Got runtime: package: , so filename: libopenxr_runtime.magicleap.so, native lib dir: /system/lib64, has functions: no
[libc] Access denied finding property "sys.mf.init.done"
[OpenXR-Loader] Info [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : RuntimeInterface::LoadRuntime forwarding xrInitializeLoaderKHR call to runtime after calling xrNegotiateLoaderRuntimeInterface.
[OpenXR-Loader] Info [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : RuntimeInterface::LoadRuntime succeeded loading runtime defined in manifest file  using interface version 1 and OpenXR API version 1.0
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : Completed loader trampoline
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : Entering loader trampoline
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateInstanceExtensionProperties | OpenXR-Loader] : Completed loader trampoline
[StereoKit] |   present | XR_EXT_conformance_automation
[StereoKit] |   present | XR_EXT_debug_utils
[StereoKit] |   present | XR_EXT_plane_detection
[StereoKit] |   present | XR_EXT_view_configuration_depth_range
[StereoKit] |   present | XR_KHR_opengl_enable
[StereoKit] |   present | XR_KHR_vulkan_enable
[StereoKit] |   present | XR_KHR_vulkan_enable2
[StereoKit] |   present | XR_ML_compat
[StereoKit] |   present | XR_ML_frame_end_info
[StereoKit] |   present | XR_ML_global_dimmer
[StereoKit] |   present | XR_ML_marker_understanding
[StereoKit] |   present | XR_ML_ml2_controller_interaction
[StereoKit] |   present | XR_ML_private
[StereoKit] |   present | XR_ML_session_performance_info
[StereoKit] |   present | XR_ML_system_notifications
[StereoKit] |   present | XR_ML_user_calibration
[StereoKit] |   present | XR_MND_headless
[StereoKit] | ACTIVATED | XR_EXT_eye_gaze_interaction
[StereoKit] | ACTIVATED | XR_EXT_hand_interaction
[StereoKit] | ACTIVATED | XR_EXT_hand_tracking
[StereoKit] | ACTIVATED | XR_EXT_local_floor
[StereoKit] | ACTIVATED | XR_EXT_palm_pose
[StereoKit] | ACTIVATED | XR_KHR_android_create_instance
[StereoKit] | ACTIVATED | XR_KHR_composition_layer_depth
[StereoKit] | ACTIVATED | XR_KHR_convert_timespec_time
[StereoKit] | ACTIVATED | XR_KHR_opengl_es_enable
[StereoKit] | ACTIVATED | XR_MSFT_unbounded_reference_space
[StereoKit] |___________|______________________
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateApiLayerProperties | OpenXR-Loader] : Entering loader trampoline
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateApiLayerProperties | OpenXR-Loader] : Entering loader trampoline
[StereoKit] OpenXR layer found: XR_APILAYER_LUNARG_core_validation
[StereoKit] OpenXR layer found: XR_APILAYER_LUNARG_api_dump
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateApiLayerProperties | OpenXR-Loader] : Entering loader trampoline
[OpenXR-Loader] Verbose [GENERAL | xrEnumerateApiLayerProperties | OpenXR-Loader] : Entering loader trampoline
[OpenXR-Loader] Verbose [GENERAL | xrCreateInstance | OpenXR-Loader] : Entering loader trampoline
[linker] library "/system/lib64/libopenxr_layer_core_validation.so" ("/system/lib64/libopenxr_layer_core_validation.so") needed or dlopened by "/data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64/libopenxr_loader.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64:/data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/base.apk!/lib/x86_64", permitted_paths="/data:/mnt/expand:/data/data/net.stereokit.stereokittest_netandroid"]
[OpenXR-Loader] Warning [GENERAL | xrCreateInstance | OpenXR-Loader] : ApiLayerInterface::LoadApiLayers skipping layer XR_APILAYER_LUNARG_core_validation, failed to load with message "dlopen failed: library "/system/lib64/libopenxr_layer_core_validation.so" needed or dlopened by "/data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64/libopenxr_loader.so" is not accessible for the namespace "classloader-namespace""
[OpenXR-Loader] Error [GENERAL | xrCreateInstance | OpenXR-Loader] : Failed loading layer information
[OpenXR-Loader] Info [GENERAL | xrCreateInstance | OpenXR-Loader] : RuntimeInterface::UnloadRuntime - Unloading RuntimeInterface
[OpenXR-Loader] Info [GENERAL |  | OpenXR-Loader] : RuntimeInterface being destroyed.
[OpenXR-Loader] Error [GENERAL | xrCreateInstance | OpenXR-Loader] : xrCreateInstance failed
[StereoKit] Couldn't create OpenXR instance [XR_ERROR_FILE_ACCESS_ERROR], is OpenXR installed and set as the active runtime?
[StereoKit] Couldn't initialize a XR mode app!
[StereoKit] 000: 0x0x7c94d2a9afcf 
[StereoKit] 001: 0x0x7c94d298a058 log_write
[StereoKit] 002: 0x0x7c94d298a1a8 
[StereoKit] 003: 0x0x7c94d298a653 log_errf
[StereoKit] 004: 0x0x7c94d2a9b528 
[StereoKit] 005: 0x0x7c94d2a88aca 
[StereoKit] 006: 0x0x7c94d299b7a3 sk_init
[StereoKit] System Platform failed to initialize!
[StereoKit] 000: 0x0x7c94d2a9afcf 
[StereoKit] 001: 0x0x7c94d298a058 log_write
[StereoKit] 002: 0x0x7c94d298a1a8 
[StereoKit] 003: 0x0x7c94d298a653 log_errf
[StereoKit] 004: 0x0x7c94d2a88af2 
[StereoKit] 005: 0x0x7c94d299b7a3 sk_init
[StereoKit] No messagebox capability for this platform!

Signed-off-by: Austin Hale <haleau@gmail.com>
@maluoi
Copy link
Collaborator

maluoi commented Jan 17, 2024

Hey! Nice to see this pop up, that looks pretty decent in the video! :) I believe I've seen someone running SK on ML2 before, but only briefly, this is a really nice run-down.

Do you have any info/logs on why Debug mode isn't working?

Their comments on "buffers" are phrased really weird. I think it's unrelated to any stuttering, I believe they're just trying to say that UI shouldn't move on its own when the user is trying to interact with it? Which is fine, StereoKit's core stuff doesn't move anything unless directed to by the user. The stuttering does sound concerning though, does it happen throughout the device, or is it specific to SK apps?

If eye gaze isn't working, is it possible the device needs to explicitly request permission with a pop-up? This is how eye tracking currently works on Quest Pro.

Does StereoKit already recognize the ML2 controllers at all? In theory if their input system is well built, the runtime should map the controllers to one of SK's existing bindings. You would be able to see a controller rendered in the Controller demo, and it would log the name of the input profile it uses.

Also very nice to see ML already using XR_EXT_plane_detection! I should look into supporting that for sure!

@paulmelis
Copy link
Contributor

Very apparent jitter (& sometimes colorful artifacts) when you either move your head too quickly or move around.
This may be a limitation of the on-device SLAM, which causes perceived motion of stationary content. For example, reading UI text while moving around becomes illegible as it seemingly "repeats" multiple times until the tracking restabilizes.

Oh thank god somebody is seeing the same issues I am! We've had our ML2 replaced twice by Magic Leap because of these issues and I could find nobody else with an ML2 who was experiencing this kind of ghosting.

Signed-off-by: Austin Hale <haleau@gmail.com>
Signed-off-by: Austin Hale <haleau@gmail.com>
@austinbhale
Copy link
Contributor Author

Do you have any info/logs on why Debug mode isn't working?

Just updated the failed log for Debug mode! I've also tried disabling "Fast Deployment" with no luck

This is how eye tracking currently works on Quest Pro

You're right, it's the same here too! The eye tracking & spatial mapping permissions are considered dangerous, so they must be granted at runtime: App Permissions. I put in a fix for granting those on the main thread once SK is running

The stuttering does sound concerning though, does it happen throughout the device, or is it specific to SK apps?

Turns out it's not specific to SK apps! Though it's more noticeable than their samples (C API & OpenXR). Even in the main menu, they seem to account for the effect by lerping the menu's orientation & translating a bit delayed so it appears "smoother" (video below). So I don't think there is much we can do here except put in a request to ML about it

ml2-menu-movement

Does StereoKit already recognize the ML2 controllers at all?

I'm not seeing it from the log 馃槵 I'll look into trying to enable it, but Debug mode would be helpful here

Also very nice to see ML already using XR_EXT_plane_detection!

Support for spatial perception is still experimental but their sample for XR_EXT_plane_detection looked good!

I updated the list for what this PR could contain. I guess only controller support is needed at the moment? I thought about also supporting the occlusion filter and spatial anchors but it seems too experimental in their OpenXR code. Global dimmer would be fun to implement into SK too if you'd want it

@maluoi
Copy link
Collaborator

maluoi commented Jan 19, 2024

Thanks for the logs! These two lines stand out:

[linker] library "/system/lib64/libopenxr_layer_core_validation.so" ("/system/lib64/libopenxr_layer_core_validation.so") needed or dlopened by "/data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64/libopenxr_loader.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/lib/x86_64:/data/app/net.stereokit.stereokittest_netandroid-ATnHt-yg11pKXGgJRbFHNQ==/base.apk!/lib/x86_64", permitted_paths="/data:/mnt/expand:/data/data/net.stereokit.stereokittest_netandroid"]
[StereoKit] Couldn't create OpenXR instance [XR_ERROR_FILE_ACCESS_ERROR], is OpenXR installed and set as the active runtime?

It looks like the OpenXR Loader is trying to load libopenxr_layer_core_validation.so, a system library, and isn't getting permissions for it? This feels kinda reminiscent of a problem I ran into with Oculus a while back.

Not sure if it's the same sort of issue, or if this is even the correct fix for it, but it might be worth trying. I added this line to the manifest, I'd try the same thing, but with libopenxr_layer_core_validation.so? Maybe?

I'm not seeing it [controllers] from the log 馃槵 I'll look into trying to enable it, but Debug mode would be helpful here

There's also the in-app log in StereoKitTest's hand menu :) But yeah, if you need to add the profile, check something like this one for reference. Someday I should make those customizable from top-level, but it's not too tricky to add there.

I thought about also supporting the occlusion filter and spatial anchors but it seems too experimental in their OpenXR code. Global dimmer would be fun to implement into SK too if you'd want it

Which were the occlusion filter and spatial anchors? I didn't quite spot those. I see XR_ML_marker_understanding, which is... April Tags. Not my favorite markers, but I guess they're practical.

Global dimmer is pretty intriguing! I have no idea where that would go though :D I'm tempted to say it might be better implemented at top level, but if you can find a good spot for it in the core, I don't think I'd object.

@austinbhale
Copy link
Contributor Author

BTW, just captured a better depiction of the quite nauseating effect from ML2. It's difficult to see on camera but this is pretty much the effect that your eyes see! It also happens in the sample apps, especially when reading GUI text

image

MLBlur.1.mov

@maluoi
Copy link
Collaborator

maluoi commented Jan 19, 2024

This reminds me a bit of the HoloLens 2 scanning artifacts! I wonder if maybe it's a similar sort of thing? IIRC, the R, G, and B channels of the HL2 display are actually drawn separately at different times! So you could see colored ghosts if you were moving your head quickly.

ML2 is similarly using a waveguide display, but I don't really know the details of their implementation. It's possible this might be a similar issue with a more artifact-ey implementation, or it could be off in a different direction and be some display persistence thing? I'm not really sure!

Edit: an interesting experiment might be to turn the text color to primary R, G, or B, and see if that improves the smearing. White would basically be the worst-case, since it contains all 3 colors.

@paulmelis
Copy link
Contributor

This reminds me a bit of the HoloLens 2 scanning artifacts! I wonder if maybe it's a similar sort of thing? IIRC, the R, G, and B channels of the HL2 display are actually drawn separately at different times! So you could see colored ghosts if you were moving your head quickly.

You can indeed easily see the color separation with head movement, and GUI text as Austin mentioned. But the weird thing I've found is that the ghosting doesn't happen in all situations (and it doesn't appear to be frame-rate related). For example, just loading the google home page in the browser app easily shows color separation in the google logo, with nothing particularly graphics-heavy loaded. But take another graphics-light situation, such as the LeapBrush app, which allows you to draw lines/ribbons in 3D using the controller. When you draw a few lines and move your head around these lines there's hardly any ghosting in them. But the app also show help text on the controller buttons and moving the controller in your field-of-view, while keeping your head still, very much shows ghosting of that text. Yet, put the controller stationary in a place (e.g. on a table) and make the same relative movements with your head and no ghosting is visible on the text.

@austinbhale
Copy link
Contributor Author

I talked to some representatives from Magic Leap 2 and this is what they mentioned:

  • Since these are world-locked holograms, the device tries to predict a user鈥檚 movement and has some interpolation error when the movement is unexpected
  • User should specify a focus point on a stabilization plane (e.g., via eye tracking)
  • User should specify the near and far clipping planes
  • The R, G, B channels of the display are drawn at different times
  • The refresh rate is upscaled from 90 -> 120 hz, and the pixels don鈥檛 turn off fast enough

The Graphics team is constantly trying to improve this effect, with the goal that the developer doesn't have to manually set a focus distance by default.

For this PR, I'm waiting on their recommended spatial anchor and controller interaction OpenXR samples.

Another tidbit I learned is about their future release of aligning holograms to the camera image. For their version of mixed reality capture, they'll be having a 3rd-party plugin in Unity for this alignment. It always sounds like some extra hurdles for them to fully focus on OpenXR, since their customers are primarily using Unity. However, they are sharing StereoKit around internally and even had some more requests for it! :)

@paulmelis
Copy link
Contributor

@austinbhale Nice feedback! Some questions though:

Since these are world-locked holograms, the device tries to predict a user鈥檚 movement and has some interpolation error when the movement is unexpected

I guess this is the main reason I see the different color separation behaviour with LeapBrush and the controller overlay, as described above. Even when using very erratic head movement and the controller lying stationary I see no color separation. If it really was in the display tech itself it should be noticeable even in this situation.

Makes me wonder if they do position prediction during the RGB cycle, but it simply is not precise enough, hence the color separation.

User should specify a focus point on a stabilization plane (e.g., via eye tracking)

Is this specific to the Unity integration for ML2? I mean, how would you even do this in SK?

The refresh rate is upscaled from 90 -> 120 hz, and the pixels don鈥檛 turn off fast enough

Have you managed to get above 60 FPS rendering using StereoKit? So far, I only manage 60FPS, even with simple scenes (based on a simple custom FPS counter).

@maluoi
Copy link
Collaborator

maluoi commented Mar 15, 2024

Since these are world-locked holograms, the device tries to predict a user鈥檚 movement and has some interpolation error when the movement is unexpected

On HoloLens, we called this Late Stage Reprojection, and there were two methods of doing it. One was to submit a depth buffer to the compositor, and let the compositor reproject based on the depth at each pixel. The other (older) one was to just pick a point, or plane, and let the compositor reproject as if everything was on that plane.

When we did have to specify a plane, it was a UWP API call, I don't believe the point/plane method survived to make it into OpenXR. The depth method seems to have pretty much superseded the plane/point method by this point in time! I guess I'd probably be looking for an ML API for this, or an unpublished OpenXR extension?

Another tidbit I learned is about their future release of aligning holograms to the camera image. For their version of mixed reality capture, they'll be having a 3rd-party plugin in Unity for this alignment.

HoloLens did this one through XR_MSFT_first_person_observer, which both Unity and SK already support :( I wish it had a better name.

Have you managed to get above 60 FPS rendering using StereoKit? So far, I only manage 60FPS, even with simple scenes (based on a simple custom FPS counter).

If you're locked at 60, I might hunt around for some ML specific Android Manifest items?

@paulmelis
Copy link
Contributor

If you're locked at 60, I might hunt around for some ML specific Android Manifest items?

Hmmm, from https://forum.magicleap.cloud/t/how-do-i-run-at-120-hz/939/2:

Our device is capped at 60hz to help developers achieve stable framerate, even if the application is running for an extended period of time.

The ML2 automatically interpolates between frames. If your app is running at 60 Hz, it will be interpolated and rendered at 120 Hz.

@maluoi
Copy link
Collaborator

maluoi commented Mar 15, 2024

Ohhh, I see. Render at 60hz, reproject to 120hz. Might even be a side-effect or "number magic" related to the way they draw R G and B channels separately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants