@@ -28,6 +28,10 @@ internal class InfoReader: Reader<GPUs> {
2828 private var gpus : GPUs = GPUs ( )
2929 private var displays : [ gpu_s ] = [ ]
3030 private var devices : [ device ] = [ ]
31+
32+ private var aneChannels : CFMutableDictionary ? = nil
33+ private var aneSubscription : IOReportSubscriptionRef ? = nil
34+ private var previousANEResidencies : [ ( on: Int64 , total: Int64 ) ] = [ ]
3135
3236 public override func setup( ) {
3337 if let list = SystemKit . shared. device. info. gpu {
@@ -39,6 +43,10 @@ internal class InfoReader: Reader<GPUs> {
3943 }
4044 let devices = PCIdevices . filter { $0. object ( forKey: " IOName " ) as? String == " display " }
4145
46+ #if arch(arm64)
47+ self . setupANE ( )
48+ #endif
49+
4250 devices. forEach { ( dict: NSDictionary ) in
4351 guard let deviceID = dict [ " device-id " ] as? Data , let vendorID = dict [ " vendor-id " ] as? Data else {
4452 error ( " device-id or vendor-id not found " , log: self . log)
@@ -205,7 +213,83 @@ internal class InfoReader: Reader<GPUs> {
205213 }
206214 }
207215
216+ #if arch(arm64)
217+ let aneValue = self . readANEUtilization ( )
218+ for i in self . gpus. list. indices where self . gpus. list [ i] . IOClass. lowercased ( ) . contains ( " agx " ) {
219+ self . gpus. list [ i] . aneUtilization = aneValue ?? 0
220+ }
221+ #endif
222+
208223 self . gpus. list. sort { !$0. state && $1. state }
209224 self . callback ( self . gpus)
210225 }
226+
227+ // MARK: - ANE utilization
228+
229+ private func setupANE( ) {
230+ guard let channel = IOReportCopyChannelsInGroup ( " SoC Stats " as CFString , " Cluster Power States " as CFString , 0 , 0 , 0 ) ? . takeRetainedValue ( ) else { return }
231+
232+ let size = CFDictionaryGetCount ( channel)
233+ guard let mutable = CFDictionaryCreateMutableCopy ( kCFAllocatorDefault, size, channel) ,
234+ let dict = mutable as? [ String : Any ] , dict [ " IOReportChannels " ] != nil else { return }
235+
236+ self . aneChannels = mutable
237+ var sub : Unmanaged < CFMutableDictionary > ?
238+ self . aneSubscription = IOReportCreateSubscription ( nil , mutable, & sub, 0 , nil )
239+ sub? . release ( )
240+ }
241+
242+ private func readANEUtilization( ) -> Double ? {
243+ guard let subscription = self . aneSubscription,
244+ let channels = self . aneChannels,
245+ let reportSample = IOReportCreateSamples ( subscription, channels, nil ) ? . takeRetainedValue ( ) ,
246+ let dict = reportSample as? [ String : Any ] else {
247+ return nil
248+ }
249+ let items = dict [ " IOReportChannels " ] as! CFArray
250+
251+ var currentResidencies : [ ( on: Int64 , total: Int64 ) ] = [ ]
252+
253+ for i in 0 ..< CFArrayGetCount ( items) {
254+ let item = unsafeBitCast ( CFArrayGetValueAtIndex ( items, i) , to: CFDictionary . self)
255+
256+ guard let group = IOReportChannelGetGroup ( item) ? . takeUnretainedValue ( ) as? String ,
257+ group == " SoC Stats " ,
258+ let subgroup = IOReportChannelGetSubGroup ( item) ? . takeUnretainedValue ( ) as? String ,
259+ subgroup == " Cluster Power States " ,
260+ let channel = IOReportChannelGetChannelName ( item) ? . takeUnretainedValue ( ) as? String ,
261+ channel. hasPrefix ( " ANE " ) else { continue }
262+
263+ let stateCount = IOReportStateGetCount ( item)
264+ guard stateCount == 2 else { continue }
265+
266+ var on : Int64 = 0
267+ var total : Int64 = 0
268+ for s in 0 ..< stateCount {
269+ let residency = IOReportStateGetResidency ( item, s)
270+ let name = IOReportStateGetNameForIndex ( item, s) ? . takeUnretainedValue ( ) as? String ?? " "
271+ total += residency
272+ if name != " INACT " {
273+ on += residency
274+ }
275+ }
276+
277+ currentResidencies. append ( ( on: on, total: total) )
278+ }
279+
280+ guard !currentResidencies. isEmpty else { return nil }
281+
282+ defer { self . previousANEResidencies = currentResidencies }
283+ guard self . previousANEResidencies. count == currentResidencies. count else { return nil }
284+
285+ var totalDeltaOn : Int64 = 0
286+ var totalDeltaAll : Int64 = 0
287+ for i in 0 ..< currentResidencies. count {
288+ totalDeltaOn += currentResidencies [ i] . on - self . previousANEResidencies [ i] . on
289+ totalDeltaAll += currentResidencies [ i] . total - self . previousANEResidencies [ i] . total
290+ }
291+
292+ guard totalDeltaAll > 0 else { return 0 }
293+ return Double ( totalDeltaOn) / Double( totalDeltaAll)
294+ }
211295}
0 commit comments