*Coder Blog

Life, Technology, and Meteorology

Using IOKit to Detect Graphics Hardware

After Seasonality Core 2 was released a couple of weeks ago, I received email from a few users reporting problems they were experiencing with the app. The common thread in all the problems was having a single graphics card (in this case, it was the nVidia 7300). When the application launched, there would be several graphics artifacts in the map view (which is now written in OpenGL), and even outside the Seasonality Core window. It really sounded like I was trying to use OpenGL to do something that wasn’t compatible with the nVidia 7300.

I’m still in the process of working around the problem, but I wanted to make sure that any work-around would not affect the other 99% of my users who don’t have this graphics card. So I set out to try and find a method of detecting which graphics cards are installed in a user’s Mac. You can use the system_profiler terminal command to do this:

system_profiler SPDisplaysDataType

But running an external process from within the app is slow, and it can be difficult to parse the data reliably. Plus, if the system_profiler command goes away, the application code won’t work. I continued looking…

Eventually, I found that I might be able to get this information from IOKit. If you run the command ioreg -l, you’ll get a lengthy tree of hardware present in your Mac. I’ve used IOKit in my code before, so I figured I would try to do that again. Here is the solution I came up with:

// Check the PCI devices for video cards.  
CFMutableDictionaryRef match_dictionary = IOServiceMatching("IOPCIDevice");

// Create a iterator to go through the found devices.
io_iterator_t entry_iterator;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, 
                                 match_dictionary, 
                                 &entry_iterator) == kIOReturnSuccess) 
{
  // Actually iterate through the found devices.
  io_registry_entry_t serviceObject;
  while ((serviceObject = IOIteratorNext(entry_iterator))) {
    // Put this services object into a dictionary object.
    CFMutableDictionaryRef serviceDictionary;
    if (IORegistryEntryCreateCFProperties(serviceObject, 
                                          &serviceDictionary, 
                                          kCFAllocatorDefault, 
                                          kNilOptions) != kIOReturnSuccess) 
    {
      // Failed to create a service dictionary, release and go on.
      IOObjectRelease(serviceObject);
      continue;
    }
				
    // If this is a GPU listing, it will have a "model" key
    // that points to a CFDataRef.
    const void *model = CFDictionaryGetValue(serviceDictionary, @"model");
    if (model != nil) {
      if (CFGetTypeID(model) == CFDataGetTypeID()) {
        // Create a string from the CFDataRef.
        NSString *s = [[NSString alloc] initWithData:(NSData *)model 
                                            encoding:NSASCIIStringEncoding];
        NSLog(@"Found GPU: %@", s);
        [s release];
      }
    }
		
    // Release the dictionary created by IORegistryEntryCreateCFProperties.
    CFRelease(serviceDictionary);

    // Release the serviceObject returned by IOIteratorNext.
    IOObjectRelease(serviceObject);
  }

  // Release the entry_iterator created by IOServiceGetMatchingServices.
  IOObjectRelease(entry_iterator);
}

6 Comments

  1. thanks a lot for sharing this. I’m having trouble with people using Camouflage on a MacBook Pro with two graphic-cards and this seems the easiest way to find out how many cards are installed on the mac.

    Karsten

  2. Thanks so much for this!

    FWIW, you may want to consider adding a CFRetain immediately after IOServiceMatching, as according to the Apple docs IOServiceGetMatchingServices “consumes” a reference to the the object. Also (and I’m not quite sure about this), but may want to IOObjectRelease both entry_iterator and serviceObject (at the bottom of while loop). I’m a bit of an IOKit newbie, so I’m just trying to interpret the Apple docs as closely as possible.

    Again, thanks for pointing out how to do this:-)

  3. Philbert: A CFRetain isn’t needed for the dictionary returned by IOServiceMatching. IOServiceMatching returns a retained dictionary that requires a CFRelease. Passing that dictionary into IOServiceGetMatchingServices consumes the retain. Since the match_dictionary is not used after the call to IOServiceGetMatchingServices, I can skip an extra retain/release on that object.

    You are right about the IOObjectRelease on the entry_iterator and serviceObject though. I’ll fix the code above to reflect that. Thanks!

  4. How might you detect screens that are attached to a specific GPU? For example, I have a dual GPU macbook (of which only one GPU is active at any time). When I run “system_profiler SPDisplaysDataType” I see both GPUs, but only the “active” one will have screens attached (laptop display + thunderbolt). Any tips on pragmatically detecting this? I’d like to build a map from GPU ID to screen[].

  5. I haven’t gone as far as detecting which displays are on each GPU, but you might want to look at the code for gfxCardStatus to see if that provides any insight:

    http://gfx.io

  6. Hi, the article is really very helpful. Is it also possible to detect how much Video memory each graphics card has using IOKit.

Leave a Reply

Your email address will not be published.

*

© 2017 *Coder Blog

Theme by Anders NorenUp ↑