Sample CodevisionOSReviewed 2026-07-21View on Apple Developer

Rendering a windowed game in stereo

At a glance

Item Summary
Purpose Bring an iOS or iPadOS game to visionOS and enhance it.
App architecture The app shell selects a stereo rendering path; HeadPositionProvider supplies view state, dedicated renderer types own Metal frame work, and RealityKit deferred-lighting components/systems integrate the game scene into a windowed stereo surface.
Main patterns SwiftUI scene composition, UI–RealityKit bridge, Entity-component-system, Session owner boundary, Protocol capability boundary, Pluggable stereo renderer
Project style Code-rich sample with 45 scanned C/Objective-C header, Metal, Swift file(s) and 5021 implementation line(s).

Project structure

Source bundle/
├── RealityKit-Stereo-Rendering/RealityKitStereoRenderingApp.swift  # MetalVRRPortalSampleApp, WindowView, OnDismiss
├── RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Renderer.swift  # Renderer, DrawableProviding
├── RealityKit-Stereo-Rendering/DeferredLighting/Renderer/SinglePassDeferredRenderer.swift  # SinglePassDeferredRenderer
├── RealityKit-Stereo-Rendering/DeferredLighting/Renderer/TraditionalDeferredRenderer.swift  # TraditionalDeferredRenderer
├── RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift  # DeferredLightingComponent, DeferredLightingEntity, DeferredLightingSystem
├── RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Utilities/RenderDestination.swift  # RenderDestination
├── RealityKit-Stereo-Rendering/MetalVRREntity.swift  # RateFactors, RateFactorProviding, RateIndicator
└── Packages/RealityKit-Assets/Package.realitycomposerpro/ProjectData/main.json  # authored RealityKit content

Structure observations

  • The runtime boundary is WindowGroup + volumetric window + ImmersiveSpace + SwiftUI/UIKit host; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
  • The source is C/Objective-C header, Metal, Swift; role-named types and feature folders separate the major responsibilities.
  • Authored .reality, .rkassets, or Reality Composer Pro content is an implementation boundary; Swift loads or drives it rather than reproducing its entity graph.

Overall architecture

Reference code

RealityKit-Stereo-Rendering/RealityKitStereoRenderingApp.swift:13 — executable or app-lifecycle anchor.

struct MetalVRRPortalSampleApp: App {
    // ...
}

The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It separates lifecycle, presentation, stable state, framework/session work, and the RealityKit entity graph.

Ownership and state

Ownership evidence

RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:282 — representative stored state or nearest verified lifecycle anchor.

struct DeferredLightingView: View {
    // ...
    @State var renderEntity: MetalVRREntity?
    // ...
}
Owner Object or state Relationship Mutation authority
DeferredLightingView MetalVRREntity as renderEntity owns a SwiftUI-managed reference slot The owning type coordinates writes
DeferredLightingView Entity as root3DFrame creates and retains The owning type coordinates writes
DeferredLightingView Entity as root creates and retains The owning type coordinates writes
DeferredLightingView HeadPositionProvider as headTracker creates and retains The owning type coordinates writes

Ownership is intentionally narrow: environment, bindings, observed objects, weak links, and delegate callbacks do not prove lifetime ownership. Initialized stored services and wrapper-managed state do; entities added inside a RealityKit content closure belong to that entity graph.

Class and protocol design

Type Responsibility Depends on or conforms to
MetalVRRPortalSampleApp (RealityKit-Stereo-Rendering/RealityKitStereoRenderingApp.swift:13) Declares app lifecycle and top-level dependency lifetime. App
DeferredLightingView (RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:280) Presents UI and forwards gestures or lifecycle events. View
Renderer (RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Renderer.swift:13) Wraps a framework, input, rendering, or session capability. NSObject
DrawableProviding (RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Renderer.swift:353) Declares a replaceable capability or collaboration contract. Concrete framework collaborators
RenderDestination (RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Utilities/RenderDestination.swift:13) Declares a replaceable capability or collaboration contract. Concrete framework collaborators
RateFactorProviding (RealityKit-Stereo-Rendering/MetalVRREntity.swift:22) Declares a replaceable capability or collaboration contract. Concrete framework collaborators
DeferredLightingComponent (RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:12) Stores RealityKit entity data. TransientComponent
DeferredLightingSystem (RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:254) Updates entities matching a RealityKit component query. System

Verified protocol-oriented seams: ResolutionProbeGridRateFactorProviding, DeferredLightingEntityDrawableProviding, DeferredLightingEntityRenderDestination, MTKViewDrawableProviding, MTKViewRenderDestination. Framework conformances remain adapters, not proof of an app-wide protocol architecture.

Access control

Symbol Access Verified effect Likely rationale
@Environment(\.openWindow) private var openWindow (RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:285) private Use stays inside the declaration and same-file extensions permitted by Swift. Inference: Hide lifecycle-sensitive state or an implementation step.
fileprivate let buffer: MTLBuffer (RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Utilities/BufferView.swift:14) fileprivate Use is limited to declarations in this source file. Inference: Share with same-file helpers without making the symbol module-wide.
public let assetsBundle = Bundle.module (Packages/RealityKit-Assets/Sources/RealityKit-Assets/RealityKit-Assets.swift:11) public The symbol is available to importing modules, subject to its containing type’s visibility. Inference: Expose an intentional package or cross-target surface.

No reviewed Swift access example uses private(set), open; declarations without a modifier are internal.

Objective-C/C header and implementation visibility is documented separately from Swift lexical access control.

Reference code

RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:285 — representative visibility boundary.

struct DeferredLightingView: View {
    // ...
    @Environment(\.openWindow) private var openWindow
    // ...
}

Logic ownership and placement

Logic Owning type or file Placement rationale
Application/command lifecycle MetalVRRPortalSampleApp The executable boundary determines app, controller, scene, or command lifetime.
Presentation and user intent DeferredLightingView The view/controller forwards input and owns only presentation-local work.
Framework/session/render coordination Renderer The role-named collaborator isolates long-lived or per-frame work from presentation code.
Per-entity data and repeated behavior RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:254 Components carry data; the system queries and updates matching entities.
GPU and low-level resource work RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Renderer.swift:37 Buffer, texture, shader, or frame-resource mutation stays in a rendering/compute boundary.
Authored scene composition Packages/RealityKit-Assets/Package.realitycomposerpro/ProjectData/main.json Reality Composer Pro owns hierarchy, materials, animation, or behavior that Swift loads and coordinates.

Design patterns

Pattern Source evidence Purpose or tradeoff
SwiftUI scene composition RealityKit-Stereo-Rendering/RealityKitStereoRenderingApp.swift:13 Keeps windows, volumes, and immersive presentation visible at the app boundary.
UI–RealityKit bridge RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:327 Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle.
Entity-component-system RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:254 Stores per-entity data in components and advances repeated behavior in registered systems.
Session owner boundary RealityKit-Stereo-Rendering/HeadTracker.swift:21 Keeps authorization, session lifetime, and framework callbacks in a stable model, manager, or controller.
Protocol capability boundary RealityKit-Stereo-Rendering/ResolutionProbeGrid.swift:47 Defines a local capability with concrete conformers; this is the verified protocol-oriented seam.
Pluggable stereo renderer RealityKit-Stereo-Rendering/RealityKitStereoRenderingApp.swift:39 Isolates renderer implementations behind a common frame lifecycle while head pose and ECS lighting remain shared inputs.

Naming conventions

  • Role suffixes are evidence, not decoration: App: MetalVRRPortalSampleApp; Controller: ViewController; Provider: HeadPositionProvider; Renderer: Renderer, SinglePassDeferredRenderer, TraditionalDeferredRenderer; System: DeferredLightingSystem, ResolutionProbeSystem.
  • ECS names pair data and behavior: components DeferredLightingComponent; systems DeferredLightingSystem, ResolutionProbeSystem.
  • Protocols: DrawableProviding, RenderDestination, RateFactorProviding.
  • Commands use verb-led methods: beginFrame, beginDrawableCommands, endFrame, encodePass, encodeStage, encodeGBufferStage, encodeDirectionalLightingStage, encodeLightMaskStage.
  • Files and folders use primary-type or responsibility names such as Views, Models, Rendering, Components, Systems, Packages, or feature names where those boundaries exist.

Architecture takeaways

  • Treat MetalVRRPortalSampleApp as the owner of executable or scene lifecycle, not automatically as the owner of every entity created later.
  • Keep view/controller input near presentation, but move sessions, reconstruction, capture, rendering, shared game state, or documents into stable owners when their lifetime exceeds one callback or render pass.
  • Use components for per-entity data and systems for repeated simulation instead of centralizing every update in a view model or controller.
  • Count authored Reality Composer Pro assets as implementation; the Swift shell is not the complete architecture.

Source map

Source file Relevant symbols
RealityKit-Stereo-Rendering/RealityKitStereoRenderingApp.swift:13 MetalVRRPortalSampleApp, WindowView, OnDismiss
RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Renderer.swift:13 Renderer, DrawableProviding
RealityKit-Stereo-Rendering/DeferredLighting/Renderer/SinglePassDeferredRenderer.swift:11 SinglePassDeferredRenderer
RealityKit-Stereo-Rendering/DeferredLighting/Renderer/TraditionalDeferredRenderer.swift:14 TraditionalDeferredRenderer
RealityKit-Stereo-Rendering/DeferredLighting/DeferredLightingView.swift:12 DeferredLightingComponent, DeferredLightingEntity, DeferredLightingSystem, DeferredLightingView
RealityKit-Stereo-Rendering/DeferredLighting/Renderer/Utilities/RenderDestination.swift:13 RenderDestination
RealityKit-Stereo-Rendering/MetalVRREntity.swift:16 RateFactors, RateFactorProviding, RateIndicator, RateMapIndicators, PercentageIndicator, RenderTarget