Metal框架详细解析(十一) —— 基本组件之器件选择 - 图形渲染的器件选择(二)

版本记录

版本号 时间
V1.0 2018.10.07 星期日

前言

很多做视频和图像的,相信对这个框架都不是很陌生,它渲染高级3D图形,并使用GPU执行数据并行计算。接下来的几篇我们就详细的解析这个框架。感兴趣的看下面几篇文章。
1. Metal框架详细解析(一)—— 基本概览
2. Metal框架详细解析(二) —— 器件和命令(一)
3. Metal框架详细解析(三) —— 渲染简单的2D三角形(一)
4. Metal框架详细解析(四) —— 关于GPU Family 4(一)
5. Metal框架详细解析(五) —— 关于GPU Family 4之关于Imageblocks(二)
6. Metal框架详细解析(六) —— 关于GPU Family 4之关于Tile Shading(三)
7. Metal框架详细解析(七) —— 关于GPU Family 4之关于光栅顺序组(四)
8. Metal框架详细解析(八) —— 关于GPU Family 4之关于增强的MSAA和Imageblock采样覆盖控制(五)
9. Metal框架详细解析(九) —— 关于GPU Family 4之关于线程组共享(六)
10. Metal框架详细解析(十) —— 基本组件(一)

Device Selection and Fallback for Graphics Rendering - 图形渲染的器件选择和后退

演示如何使用多个GPU并高效渲染到显示器。

本篇是macOS相关,要是看关于iOS的请忽略这篇文章。


Overview - 概览

macOS支持具有多个GPU和显示的系统。 一个例子是MacBook Pro,它具有低功耗集成GPU,高性能独立GPU,强大的外部GPU和其他显示器。 Metal应用必须仔细选择能够最大化特定显示效率和性能的GPU。 他们还应该优雅地响应任何GPU或显示更改,例如当用户断开外部GPU或在显示器之间移动窗口时。


Drawables, Displays, and GPUs

应用程序中的每个视图都显示在一个显示器上,每个显示器由一个GPU驱动。 要在视图中显示图形内容,视图的显示将显示来自显示器驱动GPU的渲染图形。

如果您的应用使用不会驱动视图显示的GPU进行渲染,则系统必须先将渲染GPU中的可绘制内容复制到显示GPU,然后才能显示它。 这种传输可能很昂贵,因为GPU之间的带宽受连接它们的总线的限制。 外部GPU的费用更高,因为他们的Thunderbolt 3总线带宽比内部PCI Express总线少得多。

呈现drawable的最快路径是使用驱动视图显示的GPU渲染可绘制的路径。 一个例子是带有独立GPU和集成GPU的MacBook Pro,集成GPU可以在某些条件下驱动MacBook Pro的显示器(由热状态,电池寿命或应用程序需求引起)。

另一个例子是Mac连接到外部GPU,外部GPU驱动外部显示器。


Transition Smoothly Between Devices - 在设备之间平滑转换

示例的视图控制器管理所有Metal设备,每个设备代表不同的GPU。当示例运行viewDidLoad方法时,视图控制器会为系统可用的每个设备初始化一个新的AAPLRenderer。该示例一次只使用一个设备,但它会为每个设备初始化一个渲染器,以便在所有设备上预加载和镜像应用程序的Metal资源。因此,当应用程序在运行时在GPU之间切换时,样本在设备之间平滑过渡,因为等效资源已经可用并加载到每个设备上。这种预加载和镜像策略避免了如果样本在切换时需要加载资源则会出现的显着延迟。

注意:预加载和镜像资源允许您在设备之间平滑过渡,但它也会增加应用程序的总内存使用量。您必须仔细确定应预先加载和镜像哪些资源,以及只有当您的应用程序在设备之间切换时才应加载哪些资源。


Set the Optimal Device for the View’s Display - 为视图的显示设置最佳设备

视图显示后,示例将获取显示视图的显示的CGDirectDisplayID值。 该示例使用此标识符来获取驱动显示的Metal设备。

// Get the display ID of the display in which the view appears
CGDirectDisplayID viewDisplayID = (CGDirectDisplayID) [_view.window.screen.deviceDescription[@"NSScreenNumber"] unsignedIntegerValue];

// Get the Metal device that drives the display
id<MTLDevice> newPreferredDevice = CGDirectDisplayCopyCurrentMetalDevice(viewDisplayID);

该示例为视图控制器的MTKView设置此设备,并选择与该设备关联的AAPLRenderer来执行应用程序的渲染。 此设置可确保系统使用驱动显示器的设备进行渲染,并避免将任何可绘制的数据从一个GPU复制到另一个GPU。


Handle Display Change Notifications - 处理显示更改通知

为了跟上视图显示的最佳设备,样本注册了两个系统通知:

  • NSApplicationDidChangeScreenParametersNotification。 当Mac的显示配置发生变化时,系统会发布此通知。 例如,用户将外部显示器与系统连接或断开连接。 另一个例子是当驱动显示器的GPU发生变化时,例如当启用自动图形切换(Automatic Graphics Switching)并且系统在离散和集成GPU之间切换以驱动显示器时。

  • NSWindowDidChangeScreenNotification。 当任何窗口(包括包含应用程序视图的窗口)移动到不同的显示时,系统会发布此通知。

// Register for the NSApplicationDidChangeScreenParametersNotification, which triggers
// when the system's display configuration changes
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(handleScreenChanges:)
                                             name:NSApplicationDidChangeScreenParametersNotification
                                           object:nil];

// Register for the NSWindowDidChangeScreenNotification, which triggers when the window
// changes screens
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(handleScreenChanges:)
                                             name:NSWindowDidChangeScreenNotification
                                           object:nil];

在这两种情况下,系统都会调用示例的handleScreenChanges:方法来处理通知。 然后,样本通过选择与驱动显示器的设备对应的AAPLRenderer对象,为视图的显示选择最佳设备。


Set a GPU Eject Policy - 设置GPU弹出策略

默认情况下,当应用程序使用的外部GPU从系统中删除时,macOS会完全重新启动应用程序。 应用通常通过以下方式处理重新启动:

  • 1) 当系统调用应用程序的应用程序的application willEncodeRestorableState:方法时,在macOS退出应用程序之前,保存尽可能多的状态。

  • 2) 在系统调用应用程序的application:didDecodeRestorableState:方法时,在macOS重新启动应用程序之后,恢复任何已保存的状态。

该示例避免了此应用程序重新启动例程,而是选择处理外部GPU移除本身,而不需要macOS退出并重新启动应用程序。 示例的Info.plist文件具有带有wait值的GPUEjectPolicy键,表示应用程序将通过响应Metal发布的相应通知来显式处理外部GPU的删除。


Register for External GPU Notifications - 注册外部GPU通知

该示例调用MTLCopyAllDevicesWithObserver()函数以获取系统可用的所有Metal设备。 此方法允许样本提供MTLDeviceNotificationHandler块,该块在系统中添加或删除外部GPU时执行。 此处理程序提供两个参数:

  • device。 添加或删除的设备。
  • notifyName。 描述触发通知的事件的值。
MTLDeviceNotificationHandler notificationHandler;

AAPLViewController * __weak controller = self;
notificationHandler = ^(id<MTLDevice> device, MTLDeviceNotificationName name)
{
    [controller markHotPlugNotificationForDevice:device name:name];
};

// Query all supported metal devices with an observer, so the app can receive notifications
// when external GPUs are added to or removed from the system
id<NSObject> metalDeviceObserver = nil;
NSArray<id<MTLDevice>> * availableDevices =
    MTLCopyAllDevicesWithObserver(&metalDeviceObserver,
                                  notificationHandler);

Respond to External GPU Notifications - 响应外部GPU通知

通知处理程序可以在任何线程上执行。 但是,所有UI更新必须在主线程上进行,并且必须明确使应用程序的状态更改成为线程安全的。 为了符合这些线程要求,视图控制器使用@synchronized指令保护对_hotPlugEvent_hotPlugDevice实例变量的访问。 (@synchronized指令是在Objective-C代码中创建互斥锁的便捷方式。)

当发生通知时,该示例在markHotPlugNotificationForDevice:name:方法中设置这些实例变量。

- (void)markHotPlugNotificationForDevice:(nonnull id<MTLDevice>)device
                                    name:(nonnull MTLDeviceNotificationName)name
{
    @synchronized(self)
    {
        if ([name isEqualToString:MTLDeviceWasAddedNotification])
        {
            _hotPlugEvent = AAPLHotPlugEventDeviceAdded;
        }
        else if ([name isEqualToString:MTLDeviceRemovalRequestedNotification])
        {
            _hotPlugEvent = AAPLHotPlugEventDeviceEjected;
        }
        else if ([name isEqualToString:MTLDeviceWasRemovedNotification])
        {
            _hotPlugEvent = AAPLHotPlugEventDevicePulled;
        }

        _hotPlugDevice = device;
    }
}

该示例在主线程上读取这些实例变量,并在handlePossibleHotPlugEvent方法中处理通知。

- (void)handlePossibleHotPlugEvent
{
    AAPLHotPlugEvent hotPlugEvent;
    id<MTLDevice> hotPlugDevice;

    @synchronized(self)
    {
        hotPlugEvent = _hotPlugEvent;
        hotPlugDevice = _hotPlugDevice;
        _hotPlugDevice = nil;
    }

    if(hotPlugDevice)
    {
        switch (hotPlugEvent)
        {
            case AAPLHotPlugEventDeviceAdded:
                [self handleMTLDeviceAddedNotification:hotPlugDevice];
                break;
            case AAPLHotPlugEventDeviceEjected:
            case AAPLHotPlugEventDevicePulled:
                [self handleMTLDeviceRemovalNotification:hotPlugDevice];
                break;
        }
    }
}

当代表外部GPU的设备添加到系统时,handlePossibleHotPlugEvent方法将设备添加到_supportedDevices数组并为设备初始化新的AAPLRenderer。 从系统中删除此类设备时,同样的方法会从_supportedDevices数组中删除该设备并销毁其关联的AAPLRenderer。 如果删除的设备用于渲染,则示例将切换到另一个设备和渲染器。


Update Per-Frame State and Data - 更新每帧状态和数据

MetalKit为示例调用drawInMTKView:方法来渲染每个帧。 在此方法中,示例调用handlePossibleHotPlugEvent方法来处理主线程上的设备添加或删除。 此类操作包括更新与这些设备事件相关的UI以及完成必须在单个线程上以原子方式执行的任何其他状态更改。

然后,该示例调用drawFrameNumber:toView:开始为当前渲染器渲染新帧。 为了确保能够在不同渲染器之间无缝切换的连续渲染,示例存储与渲染器本身分离的任何非渲染状态。 然后,对于每个帧,示例将任何必要的非渲染状态传递给特定的AAPLRenderer实例。 在这种情况下,样本将当前帧编号_frameNumber传递给渲染器,以便它可以计算样本3D模型的位置和旋转。


Deregister from Notifications - 从通知中取消注册

视图消失后,示例会显式取消注册以前的任何显示或设备通知。 否则,系统的通知中心和Metal无法释放示例的视图控制器。

- (void)viewDidDisappear
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:NSApplicationDidChangeScreenParametersNotification
                                                  object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:NSWindowDidChangeScreenNotification
                                                  object:nil];

    MTLRemoveDeviceObserver(_metalDeviceObserver);
}

注意:示例不能将注销过程推迟到视图控制器的dealloc方法。 执行dealloc方法时,系统的通知中心和Metal仍然具有对视图控制器的引用,以防止它被销毁。

后记

本篇主要讲述了图形渲染的器件选择,感兴趣的给个赞或者关注~~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容