Quest Runtime Optimizer fails to analyze captured frame
I am trying to use the Quest Runtime Optimizer (v0.1.0 from the asset store) on an OpenXR project with Unity 2022.3.54f1. I am building and deploying an apk to a Meta Quest 3 (version 79.0). The Scan GameObjects functionnality works fine. However, when I try to "Capture Frame" from the Quest Runtime Optimizer editor windows, I get the following error: MetricAPI: 68946B12.json execode: 0 single positional indexer is out-of-bounds Traceback (most recent call last): File "C:\open\fbsource\arvr\projects\integrations\Unity\RuntimeOptimizer\Editor\PerformanceInsight\MetricAPI.py", line 1118, in main File "pandas\core\indexing.py", line 1191, in __getitem__ File "pandas\core\indexing.py", line 1752, in _getitem_axis File "pandas\core\indexing.py", line 1685, in _validate_integer IndexError: single positional indexer is out-of-bounds UnityEngine.Debug:LogError (object) Meta.XR.RuntimeOptimizer.Core.Util:DebugLogError (object) (at ./Library/PackageCache/com.meta.xr.runtimeoptimizer@0.1.0/Core/LogUtils.cs:19) Meta.XR.RuntimeOptimizer.Editor.PerformanceInsight.MetricAPI:GetCaptureMetric (string,string) (at ./Library/PackageCache/com.meta.xr.runtimeoptimizer@0.1.0/Editor/PerformanceInsight/MetricAPI.cs:68) Meta.XR.RuntimeOptimizer.Editor.PerformanceInsight.CaptureTool:ProcessCaptureToJsonObjFunc (object) (at ./Library/PackageCache/com.meta.xr.runtimeoptimizer@0.1.0/Editor/PerformanceInsight/CaptureTool.cs:787) System.Threading._ThreadPoolWaitCallback:PerformWaitCallback () The captured frame appears in the Quest Runtime Optimizer editor windows but when I click on it, I get a second error: Failed to generate perfetto Analysis for 68946B12 UnityEngine.Debug:LogError (object) Meta.XR.RuntimeOptimizer.Core.Util:DebugLogError (object) (at ./Library/PackageCache/com.meta.xr.runtimeoptimizer@0.1.0/Core/LogUtils.cs:19) Meta.XR.RuntimeOptimizer.Editor.RuntimeOptimizerWindow:OnGUI () (at ./Library/PackageCache/com.meta.xr.runtimeoptimizer@0.1.0/Editor/RuntimeOptimizer.cs:2205) UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&) Any help would be appreciated.198Views1like3CommentsGrabbed object with custom pose "wiggles" when moving controller or hands rapidly back and forth
I posted an issue at https://github.com/oculus-samples/Unreal-InteractionSDK-Sample/issues/16 but adding here for visibility also. I found that if I grabbed an object with a custom pose and moved the controller back and forth quickly the grabbed object doesn't seem to to track my virtual hand quite right. It kind of looks like the object "wiggles" or jiggles/lags behind the hands. This was only happening with pose mode set to "Snap Object to Pose". I tracked it down to CalculateInteractorSnapTransform() in OculusInteraction\Private\Interaction\Grabbable\IsdkGrabTransformerComponent.cpp but I'm not sure the best way to fix. I notice some of the other branches in UIsdkGrabTransformerComponent::UpdateTransform() end up calling TransformTarget->SetRelativeTransform(TargetRelativeTransform); which doesn't exhibit the issue. Hands tracking also seems to have the same issue. I asked Gemini for a solution and it suggested this code: FTransform UIsdkGrabTransformerComponent::CalculateInteractorSnapTransform( FTransform& TargetTransformIn, FTransform& InteractorTransformIn) { const FTransform OffsetInverse = InteractorSnapOffset.Inverse(); const FTransform FinalTransform = OffsetInverse * InteractorTransformIn; return FinalTransform; } It did fix the problem, but I'm not sure what other purposes the original code had as it was more complex. Thanks!53Views0likes0CommentsMeta files that fail on Mac due to hardcoded Windows-style path\filename
Is there a maintained list of Meta UE source files (.cpp/.h) that hardcode Windows-style backslashes in file paths (e.g., path\filename instead of path/filename)? On macOS, those paths aren’t valid and have been breaking packaging/deployment (and sometimes Xcode builds) for me. I’ve confirmed at least two instances and suspect more. A complete inventory would let me verify whether this is the root cause of my Mac build/deploy failures and fix them efficiently.68Views0likes2CommentsETA on MetaXR plugin with UE5.6 support?
Is there anyone on here who works for Meta and can give an indication of when the MetaXR and Meta Platform plugins will be upgraded to support ue5.6? Using UE5.5 with OVR API enabled via the MetaXR plugin creates a visual cutoff artifact in non-opaque materials and some opaque materials in a far right sliver of the right display, as discussed in This Thread. It seems that 5.6 potentially does not have this issue, so I'm left waiting either for a 5.5 version of the MetaXR plugin that fixes this artifact or a 5.6 version.107Views0likes1Comment[Quest 3] Accessing Camera via Android Camera2 API - Process Succeeds but Image is Black
Hello everyone, I'm developing a native Android application (integrated with Unreal Engine) for the Meta Quest 3. My goal is to programmatically capture a still image from the passthrough camera using the standard Android Camera2 API. First, I want to emphasize that the standard, system-level Passthrough feature works perfectly in the headset. To be clear, I already have Passthrough enabled and working correctly inside my Unreal app. I can see the live camera feed, which confirms the basic functionality is there. My issue is specifically with capturing a still image programmatically. While my code executes without errors and saves a file, the resulting image is always completely black. I'm aware that this was a known limitation on older OS versions. However, I was under the impression this was resolved starting with software v67, based on the official documentation here: https://developers.meta.com/horizon/documentation/native/android/pca-native-documentation Given that my headset is on a much newer version, I'm struggling to understand what I'm still missing. Here's a summary of my setup and what I've confirmed: Android Manifest & Permissions: MyAndroidManifest.xmlis configured according to the documentation and includes all necessary features and permissions. I can confirm through logs that the user grants the runtime permissions (CAMERA and HEADSET_CAMERA) successfully. Here are the relevant entries from my manifest: <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="horizonos.permission.HEADSET_CAMERA"/> <uses-feature android:name="android.hardware.camera2.any" android:required="true"/> <uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="true"/> Camera Discovery: I am using the standardCamera2API to find the correct passthrough camera device. This part of the code works perfectly—I successfully identify the camera ID by checking CameraCharacteristics for the Meta-specific metadata key (com.meta.extra_metadata.camera_source). The Problem: The Black Image My code successfully opens the camera, creates a capture session, captures an image, and saves a JPEG file. The entire process completes without any exceptions, and my native function receives a "success" code. However, the saved JPEG file is always completely black. My system version is: v79.1032 so as my per understanding of https://developers.meta.com/horizon/documentation/native/android/pca-native-documentation It should work My Java class which is called to save the image, hello method is invoked to capture the image: package com.jaszczurcompany.camerahelper; import android.Manifest; import android.app.Activity; import android.content.ContentValues; import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.camera2.*; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.provider.MediaStore; import android.util.Log; import android.util.Size; import android.view.Surface; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.io.OutputStream; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class CameraHelper { private static final String TAG = "FooTag"; // Zmieniono TAG, aby pasował do Twoich logów public static final int SUCCESS = 0; // ... (reszta kodów bez zmian) public static final int ERROR_NO_CAMERA_FEATURE = -2; public static final int ERROR_MISSING_PERMISSIONS = -3; public static final int ERROR_CAMERA_MANAGER_UNAVAILABLE = -4; public static final int ERROR_NO_PASSTHROUGH_CAMERA_FOUND = -5; public static final int ERROR_CAMERA_ACCESS_DENIED = -6; public static final int ERROR_CAMERA_SESSION_FAILED = -7; public static final int ERROR_CAPTURE_FAILED = -8; public static final int ERROR_IMAGE_SAVE_FAILED = -9; public static final int ERROR_TIMEOUT = -10; public static final int ERROR_INTERRUPTED = -11; public static final int ERROR_UNSUPPORTED_CONFIGURATION = -12; private static final String META_PASSTHROUGH_CAMERA_KEY_NAME = "com.meta.extra_metadata.camera_source"; private static final byte META_PASSTHROUGH_CAMERA_VALUE = 1; private final Activity activity; private CameraManager cameraManager; private CameraDevice cameraDevice; private CameraCaptureSession captureSession; private ImageReader imageReader; private Handler backgroundHandler; private HandlerThread backgroundThread; private SurfaceTexture dummyPreviewTexture; private Surface dummyPreviewSurface; private final CountDownLatch operationCompleteLatch = new CountDownLatch(1); private final AtomicInteger resultCode = new AtomicInteger(SUCCESS); private final Semaphore cameraOpenCloseLock = new Semaphore(1); public CameraHelper(@NonNull Activity activity) { this.activity = activity; } public int hello() { Log.d(TAG, "Starting hello() sequence using Camera2 API (Two-Session approach)."); // ... (wstępne sprawdzenia są takie same) if (!activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)) return ERROR_NO_CAMERA_FEATURE; cameraManager = (CameraManager) activity.getSystemService(Activity.CAMERA_SERVICE); if (cameraManager == null) return ERROR_CAMERA_MANAGER_UNAVAILABLE; if (!checkPermissions()) return ERROR_MISSING_PERMISSIONS; try { startBackgroundThread(); if (!cameraOpenCloseLock.tryAcquire(5, TimeUnit.SECONDS)) return ERROR_TIMEOUT; String cameraId = findPassthroughCameraId(cameraManager); if (cameraId == null) { setResult(ERROR_NO_PASSTHROUGH_CAMERA_FOUND); } else { openCamera(cameraId); } if (!operationCompleteLatch.await(15, TimeUnit.SECONDS)) setResult(ERROR_TIMEOUT); } catch (InterruptedException e) { Thread.currentThread().interrupt(); setResult(ERROR_INTERRUPTED); } catch (CameraAccessException e) { setResult(ERROR_CAMERA_ACCESS_DENIED); } finally { cleanup(); } Log.i(TAG, "hello() returned " + resultCode.get()); return resultCode.get(); } // Zmieniono nazwę, aby nie mylić z prośbą o uprawnienia private boolean checkPermissions() { String[] requiredPermissions = {Manifest.permission.CAMERA, "horizonos.permission.HEADSET_CAMERA"}; return Arrays.stream(requiredPermissions) .allMatch(p -> ContextCompat.checkSelfPermission(activity, p) == PackageManager.PERMISSION_GRANTED); } private String findPassthroughCameraId(CameraManager manager) throws CameraAccessException { // ... (bez zmian) final CameraCharacteristics.Key<Byte> metaCameraSourceKey = new CameraCharacteristics.Key<>(META_PASSTHROUGH_CAMERA_KEY_NAME, Byte.class); for (String cameraId : manager.getCameraIdList()) { CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); Byte cameraSourceValue = characteristics.get(metaCameraSourceKey); if (cameraSourceValue != null && cameraSourceValue == META_PASSTHROUGH_CAMERA_VALUE) return cameraId; } return null; } private void openCamera(String cameraId) throws CameraAccessException { // ... (konfiguracja imageReader i dummyPreviewSurface bez zmian) CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { setResult(ERROR_UNSUPPORTED_CONFIGURATION); return; } Size largestSize = Arrays.stream(map.getOutputSizes(ImageFormat.JPEG)).max(Comparator.comparing(s -> (long) s.getWidth() * s.getHeight())).orElse(new Size(1280, 720)); Log.d(TAG, "Selected JPEG size: " + largestSize); imageReader = ImageReader.newInstance(largestSize.getWidth(), largestSize.getHeight(), ImageFormat.JPEG, 1); imageReader.setOnImageAvailableListener(this::onImageAvailable, backgroundHandler); dummyPreviewTexture = new SurfaceTexture(10); dummyPreviewTexture.setDefaultBufferSize(640, 480); dummyPreviewSurface = new Surface(dummyPreviewTexture); if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) return; cameraManager.openCamera(cameraId, cameraStateCallback, backgroundHandler); } private final CameraDevice.StateCallback cameraStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { cameraOpenCloseLock.release(); cameraDevice = camera; createPreviewSession(); // ZACZNIJ OD SESJI PODGLĄDU } @Override public void onDisconnected(@NonNull CameraDevice camera) { /* ... bez zmian ... */ cameraOpenCloseLock.release(); setResult(ERROR_CAMERA_ACCESS_DENIED); camera.close(); } @Override public void onError(@NonNull CameraDevice camera, int error) { /* ... bez zmian ... */ cameraOpenCloseLock.release(); setResult(ERROR_CAMERA_ACCESS_DENIED); camera.close(); } }; /** KROK 1: Utwórz i uruchom sesję TYLKO dla podglądu, aby rozgrzać sensor. */ private void createPreviewSession() { try { CaptureRequest.Builder previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); previewRequestBuilder.addTarget(dummyPreviewSurface); cameraDevice.createCaptureSession(Collections.singletonList(dummyPreviewSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { captureSession = session; try { session.setRepeatingRequest(previewRequestBuilder.build(), null, backgroundHandler); Log.d(TAG, "Preview started for warm-up. Waiting 1s..."); backgroundHandler.postDelayed(() -> { // Po opóźnieniu, zatrzymaj podgląd i zacznij przechwytywanie stopPreviewAndStartCapture(); }, 1000); } catch (CameraAccessException e) { setResult(ERROR_CAPTURE_FAILED); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { setResult(ERROR_CAMERA_SESSION_FAILED); } }, backgroundHandler); } catch (CameraAccessException e) { setResult(ERROR_CAMERA_SESSION_FAILED); } } /** KROK 2: Zatrzymaj sesję podglądu i utwórz nową sesję do zrobienia zdjęcia. */ private void stopPreviewAndStartCapture() { try { captureSession.stopRepeating(); captureSession.close(); // Zamknij starą sesję Log.d(TAG, "Preview stopped. Creating capture session..."); // Utwórz nową sesję TYLKO dla ImageReader cameraDevice.createCaptureSession(Collections.singletonList(imageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { captureSession = session; captureImage(); // Zrób zdjęcie używając nowej sesji } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { setResult(ERROR_CAMERA_SESSION_FAILED); } }, backgroundHandler); } catch (CameraAccessException e) { setResult(ERROR_CAPTURE_FAILED); } } /** KROK 3: Zrób pojedyncze zdjęcie. */ private void captureImage() { try { Log.d(TAG, "Capturing image with dedicated session..."); CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(imageReader.getSurface()); captureSession.capture(captureBuilder.build(), null, backgroundHandler); } catch (CameraAccessException e) { setResult(ERROR_CAPTURE_FAILED); } } private void onImageAvailable(ImageReader reader) { // ... (bez zmian) try (Image image = reader.acquireLatestImage()) { if (image == null) return; ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); saveImage(bytes); } catch (Exception e) { setResult(ERROR_IMAGE_SAVE_FAILED); } } private void saveImage(byte[] bytes) { // ... (bez zmian) String fileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()) + ".jpg"; ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); } android.net.Uri uri = activity.getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values); if (uri == null) { setResult(ERROR_IMAGE_SAVE_FAILED); return; } try (OutputStream os = activity.getContentResolver().openOutputStream(uri)) { if (os == null) throw new java.io.IOException("Output stream is null."); os.write(bytes); setResult(SUCCESS); } catch (java.io.IOException e) { setResult(ERROR_IMAGE_SAVE_FAILED); } } private void setResult(int code) { // ... (bez zmian) if (resultCode.compareAndSet(SUCCESS, code)) { operationCompleteLatch.countDown(); } } // ... (metody start/stop background thread i cleanup są takie same, bez większych zmian) private void startBackgroundThread() { /* ... bez zmian ... */ backgroundThread = new HandlerThread("CameraBackground"); backgroundThread.start(); backgroundHandler = new Handler(backgroundThread.getLooper()); } private void stopBackgroundThread() { /* ... bez zmian ... */ if (backgroundThread != null) { backgroundThread.quitSafely(); try { backgroundThread.join(1000); } catch (InterruptedException e) { /* ignore */ } backgroundThread = null; backgroundHandler = null; } } private void cleanup() { try { if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) Log.e(TAG, "Cleanup timeout."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } try { if (captureSession != null) { captureSession.close(); captureSession = null; } if (cameraDevice != null) { cameraDevice.close(); cameraDevice = null; } if (imageReader != null) { imageReader.close(); imageReader = null; } if (dummyPreviewSurface != null) { dummyPreviewSurface.release(); dummyPreviewSurface = null; } if (dummyPreviewTexture != null) { dummyPreviewTexture.release(); dummyPreviewTexture = null; } } finally { cameraOpenCloseLock.release(); stopBackgroundThread(); } } } From logcat I see that it succeeded but the output image is still black.252Views1like1CommentMouse as Input device boundaries
Hello, I am currently working on a Unity application for the Meta Quest 3. The application uses the New Input System. The user input device is a Bluetooth mouse. I noticed that the cursor of the mouse seems to be bounded, as in it reaches the edge of an invisible window. However, I want the mouse movement input to be unbounded, so that I continuously get a correct mouse movement if I keep moving the mouse to the e.g. right indefinitely. I tried several methods I found online that did not work for me: Setting the cursor lockState to CursorLockMode.Locked caused the application to strongly lag upon using the mouse. Resetting the mouse position using Mouse.current.WarpCursorPosition() after obtaining the movement data did not change the behavior. I use Unity 6.0 (6000.0.53f1). Did anyone else have a similar issue?61Views0likes1CommentHow can I run a Unity app as a background process while I play another game on the meta quest 3?
I’m trying to develop a meta quest 3 application that runs in the background so I can record controller data (IMU and button actions) while I’m playing a PCVR game like SkyrimVR. my headset will be connected to my pc with an oculus link.198Views1like2CommentsPassthrough play mode doesn't work since Quest 3 update yesterday
I don't know how long the update was but it was working before the update and now it's not working. Normally I open unity and as long as I have quest link open on the headset and on my pc, clicking play in unity will launch the passthrough app preview on my headset. Now, when I go to launch link on the headset, it begins the colourful loading screen animation and then after a moment says "headset/pc disconnected - please disconnect and disconnect your headset" (or similar. Even though the desktop app shows the header as connective and active, and the quest home settings menu preview shows link as active.123Views0likes1CommentUnityWebRequest Not Working on Quest 3 - "cannot resolve destination host" error
Hello everyone, I'm currently developing an application for the Quest 3 using Unity. I've encountered an issue where UnityWebRequest isn't functioning as expected. When I attempt to make a request, I receive the error message "cannot resolve destination host." I've tried searching for a solution on the Unity forums but couldn't find any relevant answers. Has anyone else faced a similar problem? If so, could you please share any solutions or workarounds? Any assistance would be greatly appreciated. Thank you in advance!9.4KViews0likes12CommentsMetahuman crashes Unreal Engine in VR Preview for Quest 3
Hello, Maybe someone had this problem with metahuman and have found a solution? 1. I have a scene that is working on Quest3 build and also in VR Preview mode. 2. I've downloaded and added metahuman I've created. 3. I've enabled all the necessary plugins and compiled shaders. 4. I've dragged downloaded metahuman blueprint into the scene. 5. I'm running VR Preview and then Unreal Engine crashes* *When i package game for Quest3 and run from device it works. EDIT: I'm using UE 5.3.2 and MetaXR v60 Last log entries [2023.12.29-10.11.28:819][277]LogOnline: OSS: Created online subsystem instance for: :Context_2 [2023.12.29-10.11.28:859][277]LogSlate: New Slate User Created. Platform User Id 8, User Index 8, Is Virtual User: 1 [2023.12.29-10.11.28:862][277]LogSlate: Slate User Registered. User Index 8, Is Virtual User: 1 [2023.12.29-10.11.28:889][277]vr.PixelDensity = "1" [2023.12.29-10.11.28:894][277]PIE: Server logged in [2023.12.29-10.11.28:896][277]PIE: Play in editor total start time 0,422 seconds. [2023.12.29-10.11.28:899][277]LogWindows: WindowsPlatformFeatures enabled [2023.12.29-10.11.28:948][277]LogRenderer: Warning: Resizing VR buffer to 4128 by 2240 [2023.12.29-10.11.28:960][277]LogHMD: Allocating Oculus 4128 x 2240 depth rendertarget swapchain [2023.12.29-10.11.29:011][277]LogWindows: Error: appError called: Assertion failed: !EnumHasAnyFlags(TextureDesc.Flags, ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::ResolveTargetable) [File:D:\build\++UE5\Sync\Engine\Source\Runtime\D3D12RHI\Private\D3D12Texture.cpp] [Line: 529] [2023.12.29-10.11.29:011][277]LogWindows: Windows GetLastError: The operation completed successfully. (0) callstack from crash: UnrealEditor_D3D12RHI UnrealEditor_RenderCore UnrealEditor_RenderCore UnrealEditor_RenderCore UnrealEditor_Renderer UnrealEditor_Renderer UnrealEditor_Renderer UnrealEditor_Renderer UnrealEditor_Core UnrealEditor_Core UnrealEditor_RenderCore UnrealEditor_RenderCore UnrealEditor_Core UnrealEditor_Core kernel32 ntdll I'll try to install debugging symbols for engine and test again5.2KViews0likes4Comments