Sprite Kit 的主要 iOS9 性能问题
Major iOS9 performance issues with Sprite Kit
我在使用 iOS9 时遇到了巨大的性能问题,我不知道该怎么办。我读过很多帖子 - 例如 here, and here,但他们建议的解决方案无济于事或作用不大。
我的游戏从旧 iPad 2 (iOS 8.4) 上的 60fps 到新 iPad mini (iOS 9) 上的 < 15fps。
我正在努力找出罪魁祸首。我很确定其中之一是 SKCropNodes。我通常在场景中渲染多个 SKCropNodes (6 - 18)。这在 iOS8 中从来都不是问题,但它似乎 iOS9,虽然它在裁剪方面做得更好,但这样做也会消耗性能。
如果我将裁剪节点渲染为正常的 SKSpriteNodes,我可能会在旧设备上获得 5fps,而在较新的设备上最多可获得 30fps iPhone6. 除了使用裁剪节点我别无选择,但它可以'这不是全部问题。
我认为可能使用了错误的纹理图集 - 即分辨率更大的纹理图集之一。但是,强制我的设备使用非常小的地图集没有任何区别。
我正在使用 Texture Packer 生成我的地图集,其中包含针对不同设备的缩放变体。我注意到 XCAssets 现在有一个添加 Sprite Atlas 的选项(我似乎找不到任何关于此的文档)。这不适合我的游戏,因为我使用了 100 个精灵。我已经尝试将我的地图集添加到 XCAssets,但由于某种原因它不会使用缩放变体。尽管如此,在低分辨率纹理下,它仍然运行得很糟糕。
我试过设置
skView.ignoresSiblingOrder = YES;
并给了我所有节点的 zPosition 值,但仍然没有效果。我还为每个图像名称添加了 .png 扩展名(最初是一个问题,意味着它们不会呈现。)
我的场景中有一些 SKEffectNodes,但是删除和添加它们似乎没有效果。
我不明白同样的硬件和同样的代码怎么会产生如此截然不同的结果。显然,Apple 已经改变了一些与渲染有关的东西,这产生了不利影响。他们似乎也无意解决这些问题。我知道这个问题上的错误已经存在了几个月——早在 iOS9 发布之前。
我已经为这个游戏工作了 2 年,并且在 iOS9 之前才刚刚发布。它现在正遭受糟糕的性能和经常崩溃的困扰。
有没有人知道 Apple 到底做了什么来降低性能?如果我知道这一点,我至少可以尝试解决它......谢谢。
更新
以下是同一场景的一些数字,其中包含游戏一次生成的绝对最大节点数。
iOS 8,iPad 2,~200 个节点,~100 个绘图,58.7 - 60 fps
iOS 9,iPhone6,~280 个节点,~216 个绘制,大约 20 fps
我假设节点数量的差异是由于屏幕尺寸不同造成的。如果我在 iPhone 6 上更改场景以达到等效值,FPS 仍然在 24 左右。
更新 2
使用 Xcode 的模板 Sprite Kit 项目,并将宇宙飞船更改为包含宇宙飞船的 SKCropNode,在 iOS 8 上,我可以添加 100 艘飞船,没有帧率问题。在 iOS 9 上,同一个项目,我可以在帧率降至 < 30 之前添加大约 25。
iOS iPad2 上的 8 个:
iOS 9 iPhone 5:
在纹理图集的使用方面,正如我的评论,我不能保证任何东西都会从同一个图集中绘制出来。我的游戏包含自定义角色,以及来自一系列地图集的资产(每个地图集包含约 100 种纹理)。屏幕上一次最多可以显示 9 个字符。我知道这在平局方面不是最有效的,但我从来没有遇到过问题,直到 iOS9...
更新 3
我已经向 Apple 提交了一个错误,包括我的示例程序。我还用完了我的技术支持请求之一。到目前为止,苹果没有任何消息。
很多抽奖......你试过这个吗?
skView.ignoresSiblingOrder = YES;
你在使用 SKLightNode 吗?如果是这样,请尝试从您的代码中删除所有轻节点。我这样做了,我的游戏 运行 回到了 60fps 就像在 iOS8 中一样。
来源:SKLightNode performance issues
在您提供的示例中,我可以看到一种提高绘制计数并验证裁剪节点不是问题的方法。我不知道这在您的实际游戏中有多实用,但这极大地限制了您的绘制调用。
#import "GameScene.h"
@interface GameScene ()
@property(nonatomic, strong)SKTexture *spaceshipTexture;
@end
@implementation GameScene
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
myLabel.text = @"Hello, World!";
myLabel.fontSize = 45;
myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:myLabel];
//Create one texture to be used over an over
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];
sprite.xScale = 0.5;
sprite.yScale = 0.5;
SKCropNode *cropNode = [[SKCropNode alloc] init];
SKSpriteNode *mask = [SKSpriteNode spriteNodeWithColor:[UIColor blackColor] size:CGSizeMake(100, 100)];
mask.position = sprite.position;
[cropNode setMaskNode:mask];
[cropNode addChild:sprite];
[self addChild:cropNode];
//temp add to scene to create the texture than remove
//keep pointer to texture so it can be reused
self.spaceshipTexture = [self.view textureFromNode:cropNode];
[cropNode removeFromParent];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
//re use texture
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:self.spaceshipTexture];
sprite.position = location;
[self addChild:sprite];
}
}
@end
缺点是当屏幕上的节点达到 120 多个时,问题仍然存在。
另请注意,我使用默认模板创建了一个新的 SpriteKit 应用程序并删除了 SKAction。不幸的是,当您达到 120 多个节点时,即使是默认的 Apple 模板也会遭受巨大的 fps 下降。所以我不知道是否会有答案,你最好的行动方案是提交错误报告,因为我相信它应该(并且曾经能够)在屏幕上处理那么多而不会降低 fps。
另外值得注意的是,默认项目中出现了这个错误,这可能与...
2015-10-20 10:46:50.640 Test[2218:658384] <CAMetalLayer: 0x15fe987c0>: calling -display has no effect.
更新
我 运行 对此进行了更多研究...
https://forums.developer.apple.com/thread/14487
看起来其他人正在为此提交错误...
有两个主要问题。
一个是 Sprite Kit 的性能从 iOS 8 急剧下降到 iOS 9,原因有很多,其中一些你已经链接到,但还有其他原因。似乎节点的渲染、排序和 storing/dealing 的许多方面都损坏了,或者是它们之前在 CPU/GPU.
上的负载增加了一倍或三倍
然而,还有另一个问题进一步加剧了解决任何可能的性能问题的努力。这是一种普遍存在且看似任意的帧速率上限机制,当它以 40fps 运行时最容易被注意到。但它也可以在其他频率下运行。
多年来,当人们手动使用 CADisplayLink 逐帧创建游戏循环或其他基于时序的机制时(很少)注意到这种上限。
在 iOS9 中,这种看似自动的封顶已成为基于 Sprite Kit、SceneKit、Metal 和 OpenGL ES 的应用程序非常不需要的"feature"。
在 SceneKit 的情况下,这是最能说明问题的,因为无论渲染选择如何 - Metal 或 OpenGL - 都会发生封顶,而且似乎在所有设备上,包括新的 6S 手机和 iPad Air 2,即使是非常简单的默认模板项目。
"Renderer" 是设备屏幕上 SceneKit 详细统计信息中的一个行项目。这是封顶过程最明显的迹象。当游戏以 60fps 稳定运行时,它不存在。
当上限为 40fps 时,无论在屏幕上和逻辑上执行其他活动所需的时间有多少,此组件都会吸收游戏循环中保持稳定的 40fps 上限所需的所有剩余时间。它根据其他活动所需的时间而变化,总是迫使底层 OS 的明显目标将帧速率保持在 40fps。
此问题连同有关 iOS 9 Sprite Kit 性能的问题意味着目前可能无法解决您的所有问题。很难确定您是在达到这些(看似)任意的 fps 上限之一还是在造成实际问题。
顺便说一句,这些上限不限于 40fps。我注意到它们的帧率为 30fps、24fps、20fps、15fps、12fps 和 8fps。
当然,Apple 从未承认或承认 OS 中的这种上限机制,也没有评论 when/how/why 它对游戏和渲染过程的影响如此之大。
我的理论,如 post ( ) 中所述,它是 iOS 的一部分,旨在促进即将到来的可变帧率技术的使用iPad Pro,可能在其他设备中。
120Hz 成为未来设备的基本速率是有道理的,特别是考虑到 iOS、新的 Apple TV 和屏幕的 240Hz 采样 touches/pen iPad Pro...以及市场上相当数量的 120Hz 电视。
即使没有可变帧率技术(比如……你的电视),120Hz 显示率意味着 24fps 电影可以以稳定的 5:5:5 帧显示模式播放——这大大增加了 joy/immersion 在观看电影时,几乎所有电影都是以真正的 24fps 拍摄并真正利用其优势来实现模糊和运动效果。
120Hz 与可变帧率技术或 5:5:5 帧显示相比,与目前在所有具有最大帧率的设备上使用的下拉方法相比,Apple 在电影压缩和解压缩方面也将节省大量精力60fps.
所有猜测,但我猜想在游戏引擎技术中使用这些帧速率上限也有助于使游戏使用更少的功率,并为(将来)开发人员提供锁定帧速率的选项可变帧率设备世界中的游戏。非常不幸的是(如果是这样的话)他们做得很糟糕,或者解决了 OS 中的上限问题和 Sprite Kit 的性质,导致你盲目地争取获得良好、高、一致的帧速率。
Apple 的沉默和对这两组问题引起的问题的漠不关心的态度(很可能)强烈表明他们对 "their game development community" 的看法。
在一个不必要的秘密和沉默寡言(几乎是好战的)组织的封闭源代码框架内处理游戏制作所固有的各种尖端和性能关键开发问题,这是最大的单一问题。
大多数 iOS9 问题似乎在最新的 iOS9.2 beta 和 Xcode 7.2 beta 中得到了解决。
感谢 Apple 终于 解决了这个问题。太糟糕了,我花了 2 个月的时间来解决问题。
我一直在 Apple 的论坛上发帖、提交错误并与支持技术人员沟通。遗憾的是,Apple 从未明确说明他们知道哪些问题,以及 Sprite Kit 团队正在解决什么问题。
不过,至少 Sprite Kit 的大部分问题现在似乎都已解决,不再需要转行 :]
我在使用 iOS9 时遇到了巨大的性能问题,我不知道该怎么办。我读过很多帖子 - 例如 here, and here,但他们建议的解决方案无济于事或作用不大。
我的游戏从旧 iPad 2 (iOS 8.4) 上的 60fps 到新 iPad mini (iOS 9) 上的 < 15fps。
我正在努力找出罪魁祸首。我很确定其中之一是 SKCropNodes。我通常在场景中渲染多个 SKCropNodes (6 - 18)。这在 iOS8 中从来都不是问题,但它似乎 iOS9,虽然它在裁剪方面做得更好,但这样做也会消耗性能。
如果我将裁剪节点渲染为正常的 SKSpriteNodes,我可能会在旧设备上获得 5fps,而在较新的设备上最多可获得 30fps iPhone6. 除了使用裁剪节点我别无选择,但它可以'这不是全部问题。
我认为可能使用了错误的纹理图集 - 即分辨率更大的纹理图集之一。但是,强制我的设备使用非常小的地图集没有任何区别。
我正在使用 Texture Packer 生成我的地图集,其中包含针对不同设备的缩放变体。我注意到 XCAssets 现在有一个添加 Sprite Atlas 的选项(我似乎找不到任何关于此的文档)。这不适合我的游戏,因为我使用了 100 个精灵。我已经尝试将我的地图集添加到 XCAssets,但由于某种原因它不会使用缩放变体。尽管如此,在低分辨率纹理下,它仍然运行得很糟糕。
我试过设置
skView.ignoresSiblingOrder = YES;
并给了我所有节点的 zPosition 值,但仍然没有效果。我还为每个图像名称添加了 .png 扩展名(最初是一个问题,意味着它们不会呈现。)
我的场景中有一些 SKEffectNodes,但是删除和添加它们似乎没有效果。
我不明白同样的硬件和同样的代码怎么会产生如此截然不同的结果。显然,Apple 已经改变了一些与渲染有关的东西,这产生了不利影响。他们似乎也无意解决这些问题。我知道这个问题上的错误已经存在了几个月——早在 iOS9 发布之前。
我已经为这个游戏工作了 2 年,并且在 iOS9 之前才刚刚发布。它现在正遭受糟糕的性能和经常崩溃的困扰。
有没有人知道 Apple 到底做了什么来降低性能?如果我知道这一点,我至少可以尝试解决它......谢谢。
更新
以下是同一场景的一些数字,其中包含游戏一次生成的绝对最大节点数。
iOS 8,iPad 2,~200 个节点,~100 个绘图,58.7 - 60 fps
iOS 9,iPhone6,~280 个节点,~216 个绘制,大约 20 fps
我假设节点数量的差异是由于屏幕尺寸不同造成的。如果我在 iPhone 6 上更改场景以达到等效值,FPS 仍然在 24 左右。
更新 2
使用 Xcode 的模板 Sprite Kit 项目,并将宇宙飞船更改为包含宇宙飞船的 SKCropNode,在 iOS 8 上,我可以添加 100 艘飞船,没有帧率问题。在 iOS 9 上,同一个项目,我可以在帧率降至 < 30 之前添加大约 25。
iOS iPad2 上的 8 个:
iOS 9 iPhone 5:
在纹理图集的使用方面,正如我的评论,我不能保证任何东西都会从同一个图集中绘制出来。我的游戏包含自定义角色,以及来自一系列地图集的资产(每个地图集包含约 100 种纹理)。屏幕上一次最多可以显示 9 个字符。我知道这在平局方面不是最有效的,但我从来没有遇到过问题,直到 iOS9...
更新 3
我已经向 Apple 提交了一个错误,包括我的示例程序。我还用完了我的技术支持请求之一。到目前为止,苹果没有任何消息。
很多抽奖......你试过这个吗?
skView.ignoresSiblingOrder = YES;
你在使用 SKLightNode 吗?如果是这样,请尝试从您的代码中删除所有轻节点。我这样做了,我的游戏 运行 回到了 60fps 就像在 iOS8 中一样。
来源:SKLightNode performance issues
在您提供的示例中,我可以看到一种提高绘制计数并验证裁剪节点不是问题的方法。我不知道这在您的实际游戏中有多实用,但这极大地限制了您的绘制调用。
#import "GameScene.h"
@interface GameScene ()
@property(nonatomic, strong)SKTexture *spaceshipTexture;
@end
@implementation GameScene
-(void)didMoveToView:(SKView *)view {
/* Setup your scene here */
SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
myLabel.text = @"Hello, World!";
myLabel.fontSize = 45;
myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:myLabel];
//Create one texture to be used over an over
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];
sprite.xScale = 0.5;
sprite.yScale = 0.5;
SKCropNode *cropNode = [[SKCropNode alloc] init];
SKSpriteNode *mask = [SKSpriteNode spriteNodeWithColor:[UIColor blackColor] size:CGSizeMake(100, 100)];
mask.position = sprite.position;
[cropNode setMaskNode:mask];
[cropNode addChild:sprite];
[self addChild:cropNode];
//temp add to scene to create the texture than remove
//keep pointer to texture so it can be reused
self.spaceshipTexture = [self.view textureFromNode:cropNode];
[cropNode removeFromParent];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
//re use texture
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:self.spaceshipTexture];
sprite.position = location;
[self addChild:sprite];
}
}
@end
缺点是当屏幕上的节点达到 120 多个时,问题仍然存在。
另请注意,我使用默认模板创建了一个新的 SpriteKit 应用程序并删除了 SKAction。不幸的是,当您达到 120 多个节点时,即使是默认的 Apple 模板也会遭受巨大的 fps 下降。所以我不知道是否会有答案,你最好的行动方案是提交错误报告,因为我相信它应该(并且曾经能够)在屏幕上处理那么多而不会降低 fps。
另外值得注意的是,默认项目中出现了这个错误,这可能与...
2015-10-20 10:46:50.640 Test[2218:658384] <CAMetalLayer: 0x15fe987c0>: calling -display has no effect.
更新 我 运行 对此进行了更多研究...
https://forums.developer.apple.com/thread/14487
看起来其他人正在为此提交错误...
有两个主要问题。
一个是 Sprite Kit 的性能从 iOS 8 急剧下降到 iOS 9,原因有很多,其中一些你已经链接到,但还有其他原因。似乎节点的渲染、排序和 storing/dealing 的许多方面都损坏了,或者是它们之前在 CPU/GPU.
上的负载增加了一倍或三倍然而,还有另一个问题进一步加剧了解决任何可能的性能问题的努力。这是一种普遍存在且看似任意的帧速率上限机制,当它以 40fps 运行时最容易被注意到。但它也可以在其他频率下运行。
多年来,当人们手动使用 CADisplayLink 逐帧创建游戏循环或其他基于时序的机制时(很少)注意到这种上限。
在 iOS9 中,这种看似自动的封顶已成为基于 Sprite Kit、SceneKit、Metal 和 OpenGL ES 的应用程序非常不需要的"feature"。
在 SceneKit 的情况下,这是最能说明问题的,因为无论渲染选择如何 - Metal 或 OpenGL - 都会发生封顶,而且似乎在所有设备上,包括新的 6S 手机和 iPad Air 2,即使是非常简单的默认模板项目。
"Renderer" 是设备屏幕上 SceneKit 详细统计信息中的一个行项目。这是封顶过程最明显的迹象。当游戏以 60fps 稳定运行时,它不存在。
当上限为 40fps 时,无论在屏幕上和逻辑上执行其他活动所需的时间有多少,此组件都会吸收游戏循环中保持稳定的 40fps 上限所需的所有剩余时间。它根据其他活动所需的时间而变化,总是迫使底层 OS 的明显目标将帧速率保持在 40fps。
此问题连同有关 iOS 9 Sprite Kit 性能的问题意味着目前可能无法解决您的所有问题。很难确定您是在达到这些(看似)任意的 fps 上限之一还是在造成实际问题。
顺便说一句,这些上限不限于 40fps。我注意到它们的帧率为 30fps、24fps、20fps、15fps、12fps 和 8fps。
当然,Apple 从未承认或承认 OS 中的这种上限机制,也没有评论 when/how/why 它对游戏和渲染过程的影响如此之大。
我的理论,如 post (
120Hz 成为未来设备的基本速率是有道理的,特别是考虑到 iOS、新的 Apple TV 和屏幕的 240Hz 采样 touches/pen iPad Pro...以及市场上相当数量的 120Hz 电视。
即使没有可变帧率技术(比如……你的电视),120Hz 显示率意味着 24fps 电影可以以稳定的 5:5:5 帧显示模式播放——这大大增加了 joy/immersion 在观看电影时,几乎所有电影都是以真正的 24fps 拍摄并真正利用其优势来实现模糊和运动效果。
120Hz 与可变帧率技术或 5:5:5 帧显示相比,与目前在所有具有最大帧率的设备上使用的下拉方法相比,Apple 在电影压缩和解压缩方面也将节省大量精力60fps.
所有猜测,但我猜想在游戏引擎技术中使用这些帧速率上限也有助于使游戏使用更少的功率,并为(将来)开发人员提供锁定帧速率的选项可变帧率设备世界中的游戏。非常不幸的是(如果是这样的话)他们做得很糟糕,或者解决了 OS 中的上限问题和 Sprite Kit 的性质,导致你盲目地争取获得良好、高、一致的帧速率。
Apple 的沉默和对这两组问题引起的问题的漠不关心的态度(很可能)强烈表明他们对 "their game development community" 的看法。
在一个不必要的秘密和沉默寡言(几乎是好战的)组织的封闭源代码框架内处理游戏制作所固有的各种尖端和性能关键开发问题,这是最大的单一问题。
大多数 iOS9 问题似乎在最新的 iOS9.2 beta 和 Xcode 7.2 beta 中得到了解决。
感谢 Apple 终于 解决了这个问题。太糟糕了,我花了 2 个月的时间来解决问题。
我一直在 Apple 的论坛上发帖、提交错误并与支持技术人员沟通。遗憾的是,Apple 从未明确说明他们知道哪些问题,以及 Sprite Kit 团队正在解决什么问题。
不过,至少 Sprite Kit 的大部分问题现在似乎都已解决,不再需要转行 :]