首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何解决核心基础/IO工具包中较新的多GPU苹果笔记本电脑上的CGDirectDisplayID更改问题?

如何解决核心基础/IO工具包中较新的多GPU苹果笔记本电脑上的CGDirectDisplayID更改问题?
EN

Stack Overflow用户
提问于 2010-05-18 23:26:10
回答 4查看 4K关注 0票数 12

在Mac中,每个显示器都有一个唯一的CGDirectDisplayID编号分配给它。您可以使用CGGetActiveDisplayList()或[NSScreen screens]来访问它们等。每苹果的文档

显示ID可以在进程和系统重新引导过程中持续存在,并且通常保持不变,只要某些显示参数不改变。

在2010年年中更新的MacBook Pro上,苹果开始使用自动切换的英特尔/nVidia图形。笔记本电脑有两个GPU,一个低功耗的英特尔和一个高性能的nVidia。以前的双GPU笔记本(2009年型号)没有自动GPU开关,并要求用户进行设置更改,注销,然后再次登录,以使GPU开关发生。甚至更旧的系统也只有一个GPU。

2010年年中,当显示器从一个GPU切换到另一个GPU时,CGDirectDisplayID的显示就不一样了。例如:

  1. 笔记本电脑启动。
  2. 内置的液晶显示屏是由英特尔芯片组驱动。显示ID:30002
  3. 插入外部显示
  4. 内置的液晶显示屏切换到nVidia芯片组.显示ID更改:30004
  5. 外部显示由nVidia芯片组驱动。
  6. ...at这一点,英特尔的芯片组是休眠的.
  7. 用户解锁外部显示
  8. 内置的液晶显示屏切换回英特尔芯片组。它的显示ID更改为原来的:30002

我的问题是,当旧的显示ID由于GPU的更改而改变时,如何将旧的显示ID与新的显示ID相匹配?

思考:

我注意到显示ID只改变了2,但我没有足够的测试Mac来确定这是否是所有新的MacBook Pro的通用的,或者仅仅是我的。某种程度上来说,如果“只需检查显示ID,它是+/-2从另一个”工作,无论如何。

Tried:

CGDisplayRegisterReconfigurationCallback()没有匹配的逻辑,它在显示更改之前和之后通知。将类似这样的内容放入已注册的方法中不起作用:

代码语言:javascript
复制
// Run before display settings change:
CGDirectDisplayID directDisplayID = ...;
io_service_t    servicePort = CGDisplayIOServicePort(directDisplayID);
CFDictionaryRef oldInfoDict = IODisplayCreateInfoDictionary(servicePort, kIODisplayMatchingInfo);

// ...display settings change...

// Run after display settings change:
CGDirectDisplayID directDisplayID = ...;
io_service_t    servicePort = CGDisplayIOServicePort(directDisplayID);
CFDictionaryRef newInfoDict = IODisplayCreateInfoDictionary(servicePort, kIODisplayMatchingInfo);
BOOL match = IODisplayMatchDictionaries(oldInfoDict, newInfoDict, 0);

if (match)
    NSLog(@"Displays are a match");
else
    NSLog(@"Displays are not a match");

上面发生的事情是:

  1. 在显示设置更改之前,我正在缓存oldInfoDict
  2. 等待显示设置更改
  3. 然后利用oldInfoDictnewInfoDict进行IODisplayMatchDictionaries()比较
  4. IODisplayMatchDictionaries()返回一个BOOL,要么是相同的,要么是不同的。

不幸的是,如果相同的显示改变了GPU的显示,IODisplayMatchDictionaries()就不会返回YES。

代码语言:javascript
复制
// oldInfoDict  (Display ID: 30002)
oldInfoDict: {
    DisplayProductID = 40144;
    DisplayVendorID = 1552;
    IODisplayLocation = "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/IGPU@2/AppleIntelFramebuffer/display0/AppleBacklightDisplay";
}

// newInfoDict  (Display ID: 30004)
newInfoDict: {
    DisplayProductID = 40144;
    DisplayVendorID = 1552;
    IODisplayLocation = "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/P0P2@1/IOPCI2PCIBridge/GFX0@0/NVDA,Display-A@0/NVDA/display0/AppleBacklightDisplay";
}

如您所见,当GPU的GPU被切换时,IODisplayLocation键会发生变化,因此IODisplayMatchDictionaries()无法工作。

理论上,我可以将DisplayProductIDDisplayVendorID键进行比较,但我正在编写最终用户软件,并且担心用户插入两个或更多相同的监视器(这意味着他们将拥有相同的DisplayProductID/DisplayVendorID)。换句话说,这是一个不太完美的解决方案,对潜在的小故障开放。

如有任何帮助,将不胜感激!)

EN

回答 4

Stack Overflow用户

发布于 2018-09-21 17:32:01

使用可以通过以下方法获得的CFUUIDRef:

CGDisplayCreateUUIDFromDisplayID(CGDirectDisplayID displayID)和您可以使用以下方法获得显示ID:

CGDisplayGetDisplayIDFromUUID(CFUUIDRef uuid)

这就是我所使用的唯一标识显示的方法,即使它们的CGDirectDisplayID发生了变化,例如插入到了不同的端口。遗憾的是,苹果并没有正确地记录这些功能,但我在多台带有多种显示器的Mac电脑上进行的测试表明,在重启后获得的CFUUIDRef是唯一和一致的-even --不管CGDirectDisplayID是否因任何原因而改变。

要检查显示是否是新的/唯一的,将其CGDirectDisplayID转换为CFUUIDRef,然后比较UUID,这是一种多对一的关系,许多CGDirectDisplayID将映射到单个CFUUIDRef。

这些API调用在10.7-10.12的ApplicationServices中可用,从10.13开始在ColorSync中可用。

票数 7
EN

Stack Overflow用户

发布于 2013-11-20 16:34:25

我找不到比你列出的“尝试”更好的办法。但是,我找到了一个解决方案,解决了只比较供应商id和产品id的模糊性问题。

代码中的oldInfoDictnewInfoDict包含键kIODisplayEDIDKey (在IOGraphicsTypes.h中定义)的附加条目,该条目包含每个连接显示的EDID。我的观察表明,这些数据作为一个整体在GPU交换机之间保持持久性。例如:

代码语言:javascript
复制
    CGDirectDisplayID displayId = [[[screen deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue];
    io_service_t displayPort = CGDisplayIOServicePort(displayId);

    if (displayPort == MACH_PORT_NULL)
        return nil;  // No physical device to get a name from.

    CFDictionaryRef infoDict = IODisplayCreateInfoDictionary(displayPort, kIODisplayOnlyPreferredName);

    NSData *displayEdid = (NSData *)CFDictionaryGetValue(infoDict, CFSTR(kIODisplayEDIDKey));
    NSLog(@"EDID: %@", displayEdid);

    CFRelease(infoDict);

查看维基百科中EDID的数据描述,这个blob已经包含制造商、产品id和串行。因此,通过使用EDID数据来比较显示就足够了(例如,如果您只想比较一个较短的数字,可以使用它的散列)。

票数 4
EN

Stack Overflow用户

发布于 2010-05-21 13:50:35

虽然我不是专业人士,但我相信答案是允许苹果在用户更改显示时使用http://developer.apple.com/mac/library/documentation/GraphicsImaging/Reference/Quartz_Services_Ref/Reference/reference.html#//apple_ref/doc/c_ref/CGDisplayChangeSummaryFlags。回调的信息包含用于添加和删除CGDirectDisplayID的标志。

用户不应该在操作期间添加或移除显卡,所以我会在启动时播放制作列表,每当您获得“删除”标志时,设置下一个“添加”操作来替换列表中的ID。

每次CGDisplayRegisterReconfigurationCallback调用您的函数时,我都会尝试打印您返回的信息。看看你是否得到了一个带有“删除”标志的DeviceUID,然后再调用另一个带有'add‘标志的。对照CGGetActiveDisplayList检查这些id也将有助于理解正在发生的事情。

这是我最好的选择,希望能帮上忙!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2861871

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档