Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

RealityKit System update and timing

Hi, I'm playing now with hand tracking. I want to get position of hand inside a system update function. I was not sure if transform I'm getting from hand attached AnchorEntity (with trackingMode: .predicted) would give same results as handAnchors(at:) from hand tracking provider, so I started to read them both and compare. For handAnchors i tried using context.scene.timebase.sourceTimebase!.sourceClock!.time.seconds and CACurrentMediaTime() as timestamp source. They seem to use exactly same clock, so that doesn't matter, but:

  • for some reason update handler is always called twice with same context.deltaTime, but first time the query finds 0 entities, second time it finds them all. The query is the standard EntityQuery(where: .has(MyComponent.self)) and in update (matching: Self.query, updatingSystemWhen: .rendering). Here's part of logs:
System update called, entity count: 0, dt: 0.01000458374619484, absTime: 4654.222593541
System update called, entity count: 11, dt: 0.01000458374619484, absTime: 4654.22262525
System update called, entity count: 0, dt: 0.009999999776482582, absTime: 4654.249390875
System update called, entity count: 11, dt: 0.009999999776482582, absTime: 4654.249425
  • accounting for the double update calling I started to calculate time delta of absolute time between calls and they're most of the time much bigger, or much smaller than advertised by system's context.deltaTime, only sometimes they kind of match, for example:
system: (dt: 0.01000458374619484)
scene : (dt: 0.021419291667371) (absTime: 4654.222628125001)

and the very next call

system: (dt: 0.010009 166784584522)
scene : (dt: 0.0013097083328830195) (absTime: 4654.223937833334)

but sometimes

system: (dt: 0.009999999776482582)
scene : (dt: 0.009 112249999816413) (absTime: 4654.351299 166668)

Shouldn't those be more or less equal, or am I missing something?

In the end it seems that getting hand position from AnchorEntity and with handAnchors(at:) gives kind of same results, but at different time points, so I'd love to understand what's the correct way to use them and why time flows differently :).

--Edit-- P.S. Had to put spaces everywhere in logs between "9" and "1", otherwise post was blocked due to "sensitive content" :D

Hi @brother_z

The logs are likely coming from multiple instances of your System.

A custom RealityKit System instance is created for each WindowGroup, and each ImmersiveSpace, on visionOS.

If your goal is to optimize performance by ensuring there is only one system performing component queries in your app, try this work around. I've not tried it myself and if you're not seeing performance issues, using this work around is a premature optimization.

static var mostRecentScene:RealityKit.Scene?
public init(scene: RealityKit.Scene) {
    Self.mostRecentScene = scene
}

public func update(context: SceneUpdateContext) {
    guard context.scene == Self.mostRecentScene else {return}

    let entities = context.entities(matching: self.query, updatingSystemWhen: .rendering)
    // ... 
}

Thanks @Vision Pro Engineer, had no idea about that and that explains the double system calls (I tried your solution and it works). In my case I have some things I want to calculate once and then apply to all found entities, so I pulled it outside the loop and was wondering what happens, because those calculations executed more than once give wrong results :).

Regarding the time flowing at different speeds I start to think that context.deltaTime is not really "time from when the update was last called", but more "at which time delta from previous frame results of this execution will materialize" that's why they're paced at frame rate and they "float around" the real time depending on system load, or something like that.

RealityKit System update and timing
 
 
Q