This tutorial demonstrates how to run Diligent Engine on Apple visionOS. A single app hosts two renderers that share the same scene: a 2D Preview window and a fully immersive space driven by Apple CompositorServices.

xrsimulator / xros).visionOS system name).Only the Metal backend is supported on visionOS. This tutorial is self-contained and does not use SampleBase / NativeAppBase, because neither fits the SwiftUI + CompositorServices entry point that visionOS requires.
Configure and build for the simulator:
For a device build, use -DCMAKE_OSX_SYSROOT=xros and supply a valid code signing identity.
On visionOS the application entry point is a SwiftUI App. Because Swift can't call C++ directly, the tutorial is split into three layers:
src/visionOS/) — declares the launcher window and the ImmersiveSpace, owns a UIView with a CAMetalLayer for preview and hands layers off to the C++ side.src/visionOS/VisionOSAppBridge.{h,mm}) — owns the C++ preview / immersive driver objects and exposes them to Swift.src/Tutorial30_{RenderEngine,Preview,Immersive,Scene}.{hpp,cpp}) — Diligent Engine code. Tutorial30_RenderEngine owns the process-wide Metal device and immediate context; Tutorial30_Scene is shared by the preview and immersive paths.The preview is paused while an immersive space is opening or active, so the shared immediate context and scene are not rendered from both SwiftUI paths at the same time. As an additional safeguard, the Objective-C++ bridge submits all preview and immersive rendering work to one shared serial render queue.
Tutorial30_Preview renders into a plain CAMetalLayer-backed UIView. This is the standard Metal swap chain path — identical to any iOS/macOS Diligent application — so it serves as a familiar starting point before the immersive renderer:
Rendering is scheduled by a CADisplayLink on the main thread and executed on the shared serial render queue. The preview uses a simple orbit camera with right-handed view and projection matrices (see below).
Tutorial30_Immersive does not create a swap chain. CompositorServices owns the textures and hands them out per frame, so the renderer only attaches a CompositorServicesSession to the shared Diligent device and context:
All of the CompositorServices frame pacing (timing query, world anchor update, pose prediction, drawable acquisition and present) is encapsulated in CompositorServicesSession in Diligent-GraphicsTools. The tutorial only provides two callbacks:
Each drawable contains one or two views (one per eye). For each view the renderer obtains the color and depth textures from CompositorServicesSession, computes view/projection matrices and submits the scene:
The immersive render loop runs on the shared serial render queue with USER_INTERACTIVE QoS, so it never blocks the main thread and cannot overlap the preview renderer.
Both renderers use right-handed view and projection matrices:
cp_drawable_compute_projection(..., right_up_back, ...) and a plain simd_inverse of the anchor-relative view transform.Tutorial30_Preview.cpp.Both also use reverse-Z: the depth target is cleared to 0, the PSO uses COMPARISON_FUNC_GREATER_EQUAL, and the immersive drawable is configured with cp_drawable_set_depth_range(FarZ, NearZ).
Because the RH convention flips NDC winding relative to the default D3D convention, the PSO sets FrontCounterClockwise = True with CULL_MODE_BACK. This matches the canonical VR pattern used by Tutorial28_HelloOpenXR.