Search code examples
iosswiftsprite-kitinstrumentsmac-catalyst

Getting SpriteKit statistics similar to SceneKit's showsStatistics


I'm working on a SpriteKit game and would like to profile it a bit. SceneKit has a really useful showsStatistics property on SCNView which when expanded shows me this graph:

SCNView showsStatistics

How would I go about getting per frame statistics such as these for a SpriteKit game? I don't know Instruments well enough, but as far as I can see, the Time Profiler template only shows cumulative function times, and the Game Performance template only shows Metal's render time.

Thanks very much.


Solution

  • I found the signpost functionality useful for this.

    In logging.swift:

    import Foundation
    import os.log
    
    extension OSLog {
      static var subsystem = Bundle.main.bundleIdentifier!
      static let poi = OSLog(subsystem: subsystem, category: .pointsOfInterest)
    }
    

    Then for scenes:

    class MyScene: SKScene {
      let signpostID = OSSignpostID(log: .poi)
    
      override func update(_ currentTime: TimeInterval) {
        os_signpost(.begin, log: .poi, name: "1_update", signpostID: signpostID)
        // update code here
        endOfUpdate()
      }
    
      /// Mark the end of the update phase and the start of actions
      func endOfUpdate() {
        os_signpost(.end, log: .poi, name: "1_update", signpostID: signpostID)
        os_signpost(.begin, log: .poi, name: "2_actions", signpostID: signpostID)
      }
    
      /// Mark the end of actions and the start of physics computations
      override func didEvaluateActions() {
        os_signpost(.end, log: .poi, name: "2_actions", signpostID: signpostID)
        os_signpost(.begin, log: .poi, name: "3_physics", signpostID: signpostID)
      }
    
      /// Mark the end of the render loop
      override func didFinishUpdate() {
        os_signpost(.end, log: .poi, name: "3_physics", signpostID: signpostID)
      }
    }
    

    Then use the Points of Interest instrument; I'd typically add it to the Game Performance profile. You'll get regions marked out for each phase of the render loop. (I never used constraints so the end of the update and the end of the physics simulation coincided in my case, and the above doesn't have markers there, but you can override didSimulatePhysics and didApplyConstraints if you need to capture more detail there. See the render loop description at https://developer.apple.com/documentation/spritekit/skscene/responding_to_frame-cycle_events)

    You can also add other signposts at appropriate points. I put them in to flag various game events (e.g., an enemy spawning, the player getting hit, etc.) and to mark the start/end of code sections that I suspected of being important for performance. Run under instruments in windowed mode, get to a point where the game has some lag issue, and then stop the tracing just afterwards. You can look at the trace to see what's going on.