Asset flickering when switching ImmersiveSpaces

There is a flickering occurring on 3D assets when switching immersive spaces, which is not the nicest user experience. The flickering does occur either when loading the scenes directly from the RealityKitContent package, or from memory (pre-loaded assets).

Since we cannot upload a video illustrating the undesirable behaviour, I have to describe how to setup the project for you to observe it.

To replicate the issue, follow these steps:

  1. Create a new visionOS app using Xcode template, see image.
  2. Configure the project to launch directly into an immersive space (set Preferred Default Scene Session Role to Immersive Space Application Session Role in Info.plist), see image.
  3. Replace all swift files with those you will find in the attached texts.
  4. In the RealityKitContent package, create a scene named YellowSpheres as illustrated below.
  5. In the RealityKitContent package, create a scene named RedSpheres as illustrated below.
  6. Launch the app in debug mode via Xcode and onto the AVP device or simulator
  7. Continuously switch immersive spaces by pressing on buttons Show RedSpheres and Show YellowSpheres.
  8. Observe the 3d assets flicker upon opening of the immersive spaces.

//
//  AppModel.swift
//  TestFlickeringBetweenImmersiveSpaces
//

import SwiftUI

/// Maintains app-wide state
@MainActor
@Observable
class AppModel {
    var immersiveSpaceID = "ImmersiveSpace"
    enum ImmersiveSpaceState {
        case closed
        case inTransition
        case open
    }
    var immersiveSpaceState = ImmersiveSpaceState.closed
}

//
//  RedSpheresImmersiveView.swift
//  TestFlickeringBetweenImmersiveSpaces
//

import SwiftUI
import RealityKit
import RealityKitContent

struct RedSpheresImmersiveView: View {
    @Environment(AppModel.self) var appModel
    var body: some View {
        RealityView { content, attachments in
            // Add the initial RealityKit content
            if let immersiveContentEntity = try? await Entity(named: "RedSpheres", in: realityKitContentBundle) {
                immersiveContentEntity.position = [0.0, 0.1, -2.5]
                content.add(immersiveContentEntity)

                guard let showYellowSpheresButton = attachments.entity(for: "ShowYellowSpheres") else {
                    assertionFailure("missing attachment")
                    return
                }
                showYellowSpheresButton.position = [0.0, 1, -2.5]
                showYellowSpheresButton.scale = scaleForAttachements
                content.add(showYellowSpheresButton)

            }
        } update: { content, attachments in
            
        } placeholder: {
            
            ProgressView()
            
        } attachments: {
            
            Attachment(id: "ShowYellowSpheres") {
                Button {
                    // dismissCurrnt and open next immersive space
                    appModel.immersiveSpaceID = "YellowSpheres"
                } label: {
                    Text("Show YellowSpheres")
                }
            }
        }
    }

}

#Preview(immersionStyle: .mixed) {
    RedSpheresImmersiveView()
        .environment(AppModel())
}

//
//  YellowSpheresImmersiveView.swift
//  TestFlickeringBetweenImmersiveSpaces
//

import SwiftUI
import RealityKit
import RealityKitContent

struct YellowSpheresImmersiveView: View {
    @Environment(AppModel.self) var appModel
    var body: some View {
        RealityView { content, attachments in
            // Add the initial RealityKit content
            if let immersiveContentEntity = try? await Entity(named: "YellowSpheres", in: realityKitContentBundle) {
                immersiveContentEntity.position = [0.0, 0.1, -2.5]
                content.add(immersiveContentEntity)

                guard let showRedSpheresButton = attachments.entity(for: "ShowRedSpheres") else {
                    assertionFailure("missing attachment")
                    return
                }
                showRedSpheresButton.position = [0.0, 1, -2.5]
                showRedSpheresButton.scale = scaleForAttachements
                content.add(showRedSpheresButton)

            }
        } update: { content, attachments in
            
        } placeholder: {
            
            ProgressView()
            
        } attachments: {
            
            Attachment(id: "ShowRedSpheres") {
                Button {
                    // dismissCurrnt and open next immersive space
                    appModel.immersiveSpaceID = "RedSpheres"
                } label: {
                    Text("Show RedSpheres")
                }
            }
        }
    }
}

#Preview(immersionStyle: .mixed) {
    YellowSpheresImmersiveView()
        .environment(AppModel())
}

//
//  TestFlickeringBetweenImmersiveSpacesApp.swift
//  TestFlickeringBetweenImmersiveSpaces
//

import SwiftUI

let scaleForAttachements = SIMD3(2.5, 2.5, 2.5)

@main
struct TestFlickeringBetweenImmersiveSpacesApp: App {
    @Environment(\.openImmersiveSpace) var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
    @State private var appModel = AppModel()

    var body: some Scene {
        Group{
            ImmersiveSpace(id: "YellowSpheres") {
                YellowSpheresImmersiveView()
                    .environment(appModel)
                    .onAppear {
                        appModel.immersiveSpaceState = .open
                    }
                    .onDisappear {
                        appModel.immersiveSpaceState = .closed
                    }
            }
            .immersionStyle(selection: .constant(.mixed), in: .mixed)
            
            ImmersiveSpace(id: "RedSpheres") {
                RedSpheresImmersiveView()
                    .environment(appModel)
                    .onAppear {
                        appModel.immersiveSpaceState = .open
                    }
                    .onDisappear {
                        appModel.immersiveSpaceState = .closed
                    }
            }
            .immersionStyle(selection: .constant(.mixed), in: .mixed)
        }
        .onChange(of: appModel.immersiveSpaceID) { oldValue, newValue in
            Task {
                await dismissImmersiveSpace()
                let result = await openImmersiveSpace(id: appModel.immersiveSpaceID)
                switch result {
                case .opened:
                    print("ImmersiveSpace openned")
                case .error:
                    assertionFailure("could not open ImmersiveSpace")
                    
                case .userCancelled:
                    fallthrough
                @unknown default:
                    assertionFailure("could not open ImmersiveSpace")
                }
            }
        }
     }
}

Asset flickering when switching ImmersiveSpaces
 
 
Q