@@ -19,6 +19,7 @@ const (
1919 VendorNVIDIA = "nvidia"
2020 VendorAMD = "amd"
2121 VendorIntel = "intel"
22+ VendorApple = "apple"
2223 VendorVulkan = "vulkan"
2324 VendorUnknown = "unknown"
2425)
@@ -29,7 +30,8 @@ const (
2930var UnifiedMemoryDevices = []string {
3031 "NVIDIA GB10" ,
3132 "GB10" ,
32- // Add more unified memory devices here as needed
33+ "NVIDIA Thor" ,
34+ "Thor" ,
3335}
3436
3537// GPUMemoryInfo contains real-time GPU memory usage information
@@ -196,6 +198,12 @@ func DetectGPUVendor() (string, error) {
196198 return VendorVulkan , nil
197199 }
198200
201+ // Check for Apple Silicon (macOS)
202+ if appleGPUs := getAppleGPUMemory (); len (appleGPUs ) > 0 {
203+ xlog .Debug ("GPU vendor detected via system_profiler" , "vendor" , VendorApple )
204+ return VendorApple , nil
205+ }
206+
199207 // No vendor detected
200208 return "" , nil
201209}
@@ -258,6 +266,12 @@ func GetGPUMemoryUsage() []GPUMemoryInfo {
258266 gpus = append (gpus , vulkanGPUs ... )
259267 }
260268
269+ // Try Apple Silicon (macOS only)
270+ if len (gpus ) == 0 {
271+ appleGPUs := getAppleGPUMemory ()
272+ gpus = append (gpus , appleGPUs ... )
273+ }
274+
261275 return gpus
262276}
263277
@@ -351,18 +365,44 @@ func getNVIDIAGPUMemory() []GPUMemoryInfo {
351365 usagePercent = float64 (usedBytes ) / float64 (totalBytes ) * 100
352366 }
353367 } else if isNA {
354- // Unknown device with N/A values - skip memory info
355- xlog .Debug ("nvidia-smi returned N/A for unknown device" , "device" , name )
356- gpus = append (gpus , GPUMemoryInfo {
357- Index : idx ,
358- Name : name ,
359- Vendor : VendorNVIDIA ,
360- TotalVRAM : 0 ,
361- UsedVRAM : 0 ,
362- FreeVRAM : 0 ,
363- UsagePercent : 0 ,
364- })
365- continue
368+ // Check if this is a Tegra/Jetson device — if so, it uses unified memory
369+ if isTegraDevice () {
370+ xlog .Debug ("nvidia-smi returned N/A on Tegra device, using system RAM" , "device" , name )
371+ sysInfo , err := GetSystemRAMInfo ()
372+ if err != nil {
373+ xlog .Debug ("failed to get system RAM for Tegra device" , "error" , err , "device" , name )
374+ gpus = append (gpus , GPUMemoryInfo {
375+ Index : idx ,
376+ Name : name ,
377+ Vendor : VendorNVIDIA ,
378+ TotalVRAM : 0 ,
379+ UsedVRAM : 0 ,
380+ FreeVRAM : 0 ,
381+ UsagePercent : 0 ,
382+ })
383+ continue
384+ }
385+
386+ totalBytes = sysInfo .Total
387+ usedBytes = sysInfo .Used
388+ freeBytes = sysInfo .Free
389+ if totalBytes > 0 {
390+ usagePercent = float64 (usedBytes ) / float64 (totalBytes ) * 100
391+ }
392+ } else {
393+ // Truly unknown device with N/A values - skip memory info
394+ xlog .Debug ("nvidia-smi returned N/A for unknown device" , "device" , name )
395+ gpus = append (gpus , GPUMemoryInfo {
396+ Index : idx ,
397+ Name : name ,
398+ Vendor : VendorNVIDIA ,
399+ TotalVRAM : 0 ,
400+ UsedVRAM : 0 ,
401+ FreeVRAM : 0 ,
402+ UsagePercent : 0 ,
403+ })
404+ continue
405+ }
366406 } else {
367407 // Normal GPU with dedicated VRAM
368408 totalMB , _ := strconv .ParseFloat (totalStr , 64 )
@@ -790,3 +830,84 @@ func getVulkanGPUMemory() []GPUMemoryInfo {
790830
791831 return gpus
792832}
833+
834+ // getAppleGPUMemory detects Apple Silicon GPUs using system_profiler (macOS only).
835+ // Apple Silicon uses unified memory, so GPU memory is reported as system RAM.
836+ func getAppleGPUMemory () []GPUMemoryInfo {
837+ if _ , err := exec .LookPath ("system_profiler" ); err != nil {
838+ return nil
839+ }
840+
841+ cmd := exec .Command ("system_profiler" , "SPDisplaysDataType" , "-json" )
842+ var stdout , stderr bytes.Buffer
843+ cmd .Stdout = & stdout
844+ cmd .Stderr = & stderr
845+
846+ if err := cmd .Run (); err != nil {
847+ xlog .Debug ("system_profiler failed" , "error" , err , "stderr" , stderr .String ())
848+ return nil
849+ }
850+
851+ var result struct {
852+ SPDisplaysDataType []struct {
853+ Name string `json:"_name"`
854+ Model string `json:"sppci_model"`
855+ Cores string `json:"sppci_cores"`
856+ DeviceType string `json:"sppci_device_type"`
857+ Vendor string `json:"spdisplays_vendor"`
858+ } `json:"SPDisplaysDataType"`
859+ }
860+
861+ if err := json .Unmarshal (stdout .Bytes (), & result ); err != nil {
862+ xlog .Debug ("failed to parse system_profiler output" , "error" , err )
863+ return nil
864+ }
865+
866+ var gpus []GPUMemoryInfo
867+ for i , display := range result .SPDisplaysDataType {
868+ if display .DeviceType != "spdisplays_gpu" {
869+ continue
870+ }
871+ if ! strings .Contains (strings .ToLower (display .Vendor ), "apple" ) {
872+ continue
873+ }
874+
875+ name := display .Model
876+ if name == "" {
877+ name = display .Name
878+ }
879+ if name == "" {
880+ name = "Apple GPU"
881+ }
882+
883+ // Apple Silicon uses unified memory — report system RAM
884+ ramInfo , err := GetSystemRAMInfo ()
885+ if err != nil {
886+ xlog .Debug ("Apple GPU detected but failed to get system RAM" , "error" , err )
887+ gpus = append (gpus , GPUMemoryInfo {
888+ Index : i ,
889+ Name : name ,
890+ Vendor : VendorApple ,
891+ })
892+ continue
893+ }
894+
895+ usagePercent := 0.0
896+ if ramInfo .Total > 0 {
897+ usagePercent = float64 (ramInfo .Used ) / float64 (ramInfo .Total ) * 100
898+ }
899+
900+ xlog .Debug ("Apple Silicon GPU detected (unified memory)" , "device" , name , "total_ram" , ramInfo .Total )
901+ gpus = append (gpus , GPUMemoryInfo {
902+ Index : i ,
903+ Name : name ,
904+ Vendor : VendorApple ,
905+ TotalVRAM : ramInfo .Total ,
906+ UsedVRAM : ramInfo .Used ,
907+ FreeVRAM : ramInfo .Free ,
908+ UsagePercent : usagePercent ,
909+ })
910+ }
911+
912+ return gpus
913+ }
0 commit comments