预加载SKTextureAtlas
时遇到性能下降:
let textureAtlas = SKTextureAtlas(named: atlasName)
textureAtlas.preload(completionHandler: {
...
})
所谓的性能下降,是指FPS在短时间内下降到约50。
我用
Time Profiler
中的Instruments
测试了它,并验证了这项工作确实是在工作线程上完成的,如documentation中所述。下面的图像显示了由预加载图集引起的尖峰的
Time Profiler
捕获。如您所见,据我所知,大多数峰值是由2个工作线程引起的,它们似乎都在加载图像数据。但是,这不应导致主线程IMHO的性能下降。注1 :我正在预加载的
.spriteatlas
没那么大:4个 Assets 的大约1000x1000
的大小。注2 :我正在使用iPhone 6,iOS 10,Xcode 8进行测试。
注3 :没有同时进行其他大量工作; CPU一直徘徊在30%左右。 GPU也是如此。
注4 :在需要来自该 map 集的任何纹理之前,请求 map 集预加载,因此应该有足够的时间进行预加载。
任何帮助/方向,不胜感激!
更新
发生预加载的完整代码块:
let updateGroup = DispatchGroup()
for assetDefinition in assetContainmentDefinitions {
let assetName = assetDefinition.assetName
// Check if asset is not needed anymore and clear the cache with it
if progress >= assetDefinition.range.to {
if cachedAssets[assetName] != nil {
cachedAssets[assetName] = nil
}
}
// Check if asset is needed and if it's not already loading then preload and cache it
else if progress >= assetDefinition.range.from {
if currentlyLoadingAssets.contains(assetName) == false &&
cachedAssets[assetName] == nil {
currentlyLoadingAssets.append(assetName)
// Enter dispatch group
updateGroup.enter()
switch assetDefinition.assetType {
case .textureAtlas:
let textureAtlas = SKTextureAtlas(named: assetName)
textureAtlas.preload(completionHandler: {
DispatchQueue.main.async { [weak self] in
self?.cachedAssets[assetName] = textureAtlas
self?.currentlyLoadingAssets.remove(object: assetName)
// Leave dispatch group after preload is done
updateGroup.leave()
}
})
case .texture:
let texture = SKTexture(imageNamed: assetName)
texture.preload(completionHandler: {
DispatchQueue.main.async { [weak self] in
self?.cachedAssets[assetName] = texture
self?.currentlyLoadingAssets.remove(object: assetName)
// Leave dispatch group after preload is done
updateGroup.leave()
}
})
}
}
}
}
// Call completion after the dispatch group is fully completed
if let completion = completion {
updateGroup.notify(queue: DispatchQueue.main, execute: completion)
}
更新2
我创建了一个空项目,只剩下了Atlas预加载块。性能下降仍然会发生。我尝试过使用多个 map 集,甚至只使用一项 Assets 的 map 集。
在这个空的新项目中,我也尝试了@Sez的建议(请参见下面的内容),但是在那种情况下,甚至没有调用完成块,这似乎是
SKTextureAtlas
类中的另一个错误。 (?!)let atlas = SKTextureAtlas(dictionary: ["texture1": UIImage(named: "texture1")!])
atlas.preload(completionHandler: { [weak self] in
print("COMPLETION BLOCK NOT CALLED")
self?.cachedAtlas = atlas
})
更新3
我尝试删除
.spriteatlas
并使用相同的纹理创建.atlas
,并且(几乎)没有性能降低。但是,.atlas
不支持切片,这就是为什么我要首先使用.spriteatlas
的原因。 最佳答案
有issues plaguing SKTextureAtlas(Apple Dev论坛帖子)。
尝试使用提供了SKTextureAtlas:withDictionary:
字典,method described on another StackOverflow post的[String:UIImage]
创建纹理图集,看看是否有帮助。
更新:如果您希望preload of the SKTexture在后台线程中发生,我会专门编写它,而不是依赖preload
便捷功能。
let atlas: SKTextureAtlas
let myImages: [String: UIImage] = getImages()
let globalQueue = DispatchQueue.global()
globalQueue.async {
let atlas = SKTextureAtlas(withDictionary:myImages)
for (k,v) in myImages {
let tex = atlas.textureNamed(k)
print(tex.size()) // force load
}
DispatchQueue.main.async {
completionHandler(atlas)
}
}
关于app slicing,只要您将 Assets 放置在 Assets 目录中,就应该对其进行切片。将构建上传到iTunesConnect之后,您可以看到反射(reflect)出此情况的App Store大小的报告。
我有一个录制的直播视频,其中显示了此导入过程。很抱歉,它的时间很长(2hrs +),但是这里的链接恰好发生在导入到Sprite表中的那一刻。
https://youtu.be/Ic9Wnux8vd8?t=1h17m
按照我在这里显示的方式进行操作,您会得到带有切片图像的Sprite map 集。如果需要,我的脚本(我在剪辑的前面已经演示过)是on my GitHub。
关于ios - SpriteKit : performance hit while preloading SKTextureAtlas,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40834615/