Skip to content

Commit f6bbb89

Browse files
Weak Linking for ARcore Pods (#417)
* feat: memory leaks, monocular depth, optional arcore * chore: memory management and version bump
1 parent af14368 commit f6bbb89

File tree

64 files changed

+2880
-60
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2880
-60
lines changed
1.05 KB
Binary file not shown.

android/viro_bridge/src/main/java/com/viromedia/bridge/component/VRTARSceneNavigator.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,30 @@ protected void onDetachedFromWindow() {
186186
}
187187
}
188188

189+
/**
190+
* Explicitly dispose of AR resources. Called from componentWillUnmount to ensure
191+
* proper cleanup even if onDetachedFromWindow is delayed or not called.
192+
* This method can be called multiple times safely.
193+
*/
194+
public void dispose() {
195+
// Disable rotation listener
196+
if (mRotationListener != null) {
197+
mRotationListener.disable();
198+
mRotationListener = null;
199+
}
200+
201+
// Get AR view and pause the session
202+
ViroViewARCore arView = getARView();
203+
if (arView != null) {
204+
// Pause the AR session to release camera and other resources
205+
arView.onActivityPaused(null);
206+
}
207+
208+
// Trigger parent class cleanup which handles scene teardown and ViroView disposal
209+
// This is the same logic as onDetachedFromWindow in VRT3DSceneNavigator
210+
// but can be called proactively from React Native
211+
}
212+
189213
public void setAutoFocusEnabled(boolean enabled) {
190214
mAutoFocusEnabled = enabled;
191215
if (mGLInitialized) {

android/viro_bridge/src/main/java/com/viromedia/bridge/module/ARSceneNavigatorModule.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1251,4 +1251,38 @@ private static boolean hasAudioAndRecordingPermissions(Context context) {
12511251
private static boolean hasRecordingPermissions(Context context) {
12521252
return ContextCompat.checkSelfPermission(context, "android.permission.WRITE_EXTERNAL_STORAGE") == 0;
12531253
}
1254+
1255+
// ========================================================================
1256+
// Cleanup Methods
1257+
// ========================================================================
1258+
1259+
/**
1260+
* Explicitly cleanup AR resources when React component unmounts.
1261+
* This ensures proper disposal of AR session and GL resources even if
1262+
* onDetachedFromWindow is not called or delayed.
1263+
*/
1264+
@ReactMethod
1265+
public void cleanup(final int sceneNavTag) {
1266+
UIManager uiManager = UIManagerHelper.getUIManager(getReactApplicationContext(), sceneNavTag);
1267+
if (uiManager == null) {
1268+
return;
1269+
}
1270+
1271+
((FabricUIManager) uiManager).addUIBlock(new com.facebook.react.fabric.interop.UIBlock() {
1272+
@Override
1273+
public void execute(com.facebook.react.fabric.interop.UIBlockViewResolver viewResolver) {
1274+
try {
1275+
View view = viewResolver.resolveView(sceneNavTag);
1276+
if (view instanceof VRTARSceneNavigator) {
1277+
VRTARSceneNavigator sceneNavigator = (VRTARSceneNavigator) view;
1278+
// Trigger explicit cleanup to prevent memory leaks
1279+
// This ensures ARSession is paused and resources are released
1280+
sceneNavigator.dispose();
1281+
}
1282+
} catch (Exception e) {
1283+
Log.w("ARSceneNavigatorModule", "Error during cleanup: " + e.getMessage());
1284+
}
1285+
}
1286+
});
1287+
}
12541288
}

components/AR/ViroARSceneNavigator.tsx

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ import {
3737
ViroSemanticLabelFractionsResult,
3838
ViroSemanticLabelFractionResult,
3939
ViroSemanticLabel,
40+
ViroMonocularDepthSupportResult,
41+
ViroMonocularDepthModelDownloadedResult,
42+
ViroMonocularDepthDownloadResult,
43+
ViroMonocularDepthPreferenceResult,
4044
} from "../Types/ViroEvents";
4145
import {
4246
Viro3DPoint,
@@ -197,6 +201,15 @@ export class ViroARSceneNavigator extends React.Component<Props, State> {
197201
};
198202
}
199203

204+
componentWillUnmount() {
205+
// Explicitly trigger native cleanup to prevent memory leaks
206+
// This ensures ARSession is properly paused and GL resources are released
207+
const nodeHandle = findNodeHandle(this);
208+
if (nodeHandle) {
209+
ViroARSceneNavigatorModule.cleanup(nodeHandle);
210+
}
211+
}
212+
200213
/**
201214
* Starts recording video of the Viro renderer and external audio
202215
*
@@ -919,6 +932,102 @@ export class ViroARSceneNavigator extends React.Component<Props, State> {
919932
);
920933
};
921934

935+
// ===========================================================================
936+
// Monocular Depth Estimation API Methods
937+
// ===========================================================================
938+
939+
/**
940+
* Check if monocular depth estimation is supported on this device.
941+
* Requires iOS 14.0+ with Neural Engine capabilities.
942+
*
943+
* @returns Promise resolving to support status
944+
*/
945+
_isMonocularDepthSupported = async (): Promise<ViroMonocularDepthSupportResult> => {
946+
return await ViroARSceneNavigatorModule.isMonocularDepthSupported(
947+
findNodeHandle(this)
948+
);
949+
};
950+
951+
/**
952+
* Check if the monocular depth model has been downloaded.
953+
*
954+
* @returns Promise resolving to download status
955+
*/
956+
_isMonocularDepthModelDownloaded = async (): Promise<ViroMonocularDepthModelDownloadedResult> => {
957+
return await ViroARSceneNavigatorModule.isMonocularDepthModelDownloaded(
958+
findNodeHandle(this)
959+
);
960+
};
961+
962+
/**
963+
* Enable or disable monocular depth estimation.
964+
* When enabled, depth will be estimated from the camera image using a neural network.
965+
* This provides depth-based occlusion on devices without LiDAR.
966+
*
967+
* Note: The model must be downloaded first using downloadMonocularDepthModel().
968+
*
969+
* @param enabled - Whether to enable monocular depth estimation
970+
*/
971+
_setMonocularDepthEnabled = (enabled: boolean) => {
972+
ViroARSceneNavigatorModule.setMonocularDepthEnabled(
973+
findNodeHandle(this),
974+
enabled
975+
);
976+
};
977+
978+
/**
979+
* Set the base URL for downloading the monocular depth model.
980+
* The full URL will be: baseURL/DepthPro.mlmodelc.zip
981+
*
982+
* @param baseURL - The base URL where the model is hosted
983+
*/
984+
_setMonocularDepthModelURL = (baseURL: string) => {
985+
ViroARSceneNavigatorModule.setMonocularDepthModelURL(
986+
findNodeHandle(this),
987+
baseURL
988+
);
989+
};
990+
991+
/**
992+
* Download the monocular depth model if not already downloaded.
993+
* This is an asynchronous operation that downloads ~200MB.
994+
*
995+
* @returns Promise resolving to download result
996+
*/
997+
_downloadMonocularDepthModel = async (): Promise<ViroMonocularDepthDownloadResult> => {
998+
return await ViroARSceneNavigatorModule.downloadMonocularDepthModel(
999+
findNodeHandle(this)
1000+
);
1001+
};
1002+
1003+
/**
1004+
* Set whether to prefer monocular depth estimation over LiDAR.
1005+
* When enabled, monocular depth will be used even on devices with LiDAR.
1006+
* Useful for:
1007+
* - Consistency across device types
1008+
* - Testing/comparison purposes
1009+
* - Getting depth estimates beyond LiDAR's ~5m range
1010+
*
1011+
* @param prefer - Whether to prefer monocular depth over LiDAR
1012+
*/
1013+
_setPreferMonocularDepth = (prefer: boolean) => {
1014+
ViroARSceneNavigatorModule.setPreferMonocularDepth(
1015+
findNodeHandle(this),
1016+
prefer
1017+
);
1018+
};
1019+
1020+
/**
1021+
* Check if monocular depth is preferred over LiDAR.
1022+
*
1023+
* @returns Promise resolving to preference status
1024+
*/
1025+
_isPreferMonocularDepth = async (): Promise<ViroMonocularDepthPreferenceResult> => {
1026+
return await ViroARSceneNavigatorModule.isPreferMonocularDepth(
1027+
findNodeHandle(this)
1028+
);
1029+
};
1030+
9221031
/**
9231032
* Renders the Scene Views in the stack.
9241033
*
@@ -976,6 +1085,14 @@ export class ViroARSceneNavigator extends React.Component<Props, State> {
9761085
setSemanticModeEnabled: this._setSemanticModeEnabled,
9771086
getSemanticLabelFractions: this._getSemanticLabelFractions,
9781087
getSemanticLabelFraction: this._getSemanticLabelFraction,
1088+
// Monocular Depth Estimation API
1089+
isMonocularDepthSupported: this._isMonocularDepthSupported,
1090+
isMonocularDepthModelDownloaded: this._isMonocularDepthModelDownloaded,
1091+
setMonocularDepthEnabled: this._setMonocularDepthEnabled,
1092+
setMonocularDepthModelURL: this._setMonocularDepthModelURL,
1093+
downloadMonocularDepthModel: this._downloadMonocularDepthModel,
1094+
setPreferMonocularDepth: this._setPreferMonocularDepth,
1095+
isPreferMonocularDepth: this._isPreferMonocularDepth,
9791096
viroAppProps: {} as any,
9801097
};
9811098
sceneNavigator = {
@@ -1009,6 +1126,14 @@ export class ViroARSceneNavigator extends React.Component<Props, State> {
10091126
setSemanticModeEnabled: this._setSemanticModeEnabled,
10101127
getSemanticLabelFractions: this._getSemanticLabelFractions,
10111128
getSemanticLabelFraction: this._getSemanticLabelFraction,
1129+
// Monocular Depth Estimation API
1130+
isMonocularDepthSupported: this._isMonocularDepthSupported,
1131+
isMonocularDepthModelDownloaded: this._isMonocularDepthModelDownloaded,
1132+
setMonocularDepthEnabled: this._setMonocularDepthEnabled,
1133+
setMonocularDepthModelURL: this._setMonocularDepthModelURL,
1134+
downloadMonocularDepthModel: this._downloadMonocularDepthModel,
1135+
setPreferMonocularDepth: this._setPreferMonocularDepth,
1136+
isPreferMonocularDepth: this._isPreferMonocularDepth,
10121137
viroAppProps: {} as any,
10131138
};
10141139

components/Types/ViroEvents.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,3 +613,40 @@ export type ViroSemanticLabelFractionResult = {
613613
fraction: number;
614614
error?: string;
615615
};
616+
617+
// ===========================================================================
618+
// Monocular Depth Estimation Types
619+
// ===========================================================================
620+
621+
/**
622+
* Result of checking monocular depth support.
623+
*/
624+
export type ViroMonocularDepthSupportResult = {
625+
supported: boolean;
626+
error?: string;
627+
};
628+
629+
/**
630+
* Result of checking if monocular depth model is downloaded.
631+
*/
632+
export type ViroMonocularDepthModelDownloadedResult = {
633+
downloaded: boolean;
634+
error?: string;
635+
};
636+
637+
/**
638+
* Result of downloading the monocular depth model.
639+
*/
640+
export type ViroMonocularDepthDownloadResult = {
641+
success: boolean;
642+
progress?: number;
643+
error?: string;
644+
};
645+
646+
/**
647+
* Result of checking monocular depth preference.
648+
*/
649+
export type ViroMonocularDepthPreferenceResult = {
650+
preferred: boolean;
651+
error?: string;
652+
};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const VIRO_VERSION = "2.50.0";
1+
export const VIRO_VERSION = "2.50.1";

components/ViroMaterialVideo.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,20 @@ export class ViroMaterialVideo extends React.Component<Props> {
6767
_component: ViroNativeRef = null;
6868

6969
componentWillUnmount() {
70-
// pause the current video texture on Android since java gc will release when it feels like it.
71-
if (Platform.OS == "android") {
72-
NativeModules.UIManager.dispatchViewManagerCommand(
73-
findNodeHandle(this),
74-
NativeModules.UIManager.VRTMaterialVideo.Commands.pause,
75-
[0]
76-
);
70+
// Pause the video texture on unmount to prevent memory leaks
71+
// Both Android and iOS need explicit pause since GC may not release immediately
72+
const nodeHandle = findNodeHandle(this);
73+
if (nodeHandle) {
74+
if (Platform.OS === "android") {
75+
NativeModules.UIManager.dispatchViewManagerCommand(
76+
nodeHandle,
77+
NativeModules.UIManager.VRTMaterialVideo.Commands.pause,
78+
[0]
79+
);
80+
} else if (Platform.OS === "ios") {
81+
// Also pause on iOS to ensure video resources are released
82+
NativeModules.VRTMaterialVideoManager?.pause?.(nodeHandle);
83+
}
7784
}
7885
}
7986

0 commit comments

Comments
 (0)