SpriteKit:在预加载SKTextureAtlas时性能受到影响

dam*_*hec 8 multithreading ios sprite-kit sktextureatlas

我在预加载时遇到了性能损失SKTextureAtlas:

let textureAtlas = SKTextureAtlas(named: atlasName)
textureAtlas.preload(completionHandler: {
    ...
})
Run Code Online (Sandbox Code Playgroud)

通过性能命中,我的意思是FPS在短时间内降至~50.

我测试了它Time ProfilerInstruments和验证,这项工作确实正在上一个工人线程中完成,就像在声明文件.

图像波纹显示了Time Profiler由预加载图集引起的尖峰捕获.正如您所看到的,大多数峰值是由2个工作线程引起的,据我所知,这些线程似乎都在加载图像数据.但是,这不应该导致主线程IMHO的性能损失.

在此输入图像描述

注1:.spriteatlas我预加载不是那么大:4个资产大约.1000x1000尺寸.

注2:我正在测试iPhone 6,iOS 10,Xcode 8.

注3:同时没有其他实质性工作; CPU一直徘徊在~30%左右.GPU也是如此.

注4:在需要来自该图集的任何纹理之前,请求图集预加载,因此它应该有足够的时间来预加载.

任何帮助/方向非常感谢!


UPDATE

发生预加载的完整代码块:

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)
}
Run Code Online (Sandbox Code Playgroud)

更新2

我创建了一个空项目,只有atlas预加载块.性能下降仍然存在.我尝试过多个地图集,即使是只有一个资产的地图集.

在这个空的新项目中,我也尝试了@Sez建议(参见下文),但在这种情况下,甚至没有调用完成块,这似乎是类中的另一个错误SKTextureAtlas.(?!)

let atlas = SKTextureAtlas(dictionary: ["texture1": UIImage(named: "texture1")!])

atlas.preload(completionHandler: { [weak self] in
    print("COMPLETION BLOCK NOT CALLED")
    self?.cachedAtlas = atlas
})
Run Code Online (Sandbox Code Playgroud)

更新3

我尝试删除.spriteatlas.atlas使用相同的纹理创建,并且(几乎)没有性能命中.但是,.atlas不支持切片,这就是我想首先使用的原因.spriteatlas.

Sez*_*Sez 5

存在困扰SKTextureAtlas(Apple Dev论坛帖子)的问题.

尝试使用SKTextureAtlas:withDictionary:提供另一个StackOverflow帖子中描述[String:UIImage]方法的字典创建纹理图集,看看是否有帮助.

更新:如果你想在后台线程中发生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)
    }
}
Run Code Online (Sandbox Code Playgroud)

关于应用程序切片,只要您将资产放在资产目录中,就应该对它们进行切片.将构建上传到iTunesConnect后,您可以看到反映此内容的App Store大小的报告.

iTunesConnect应用商店大小

我有一个视频,我记录了一个实时流,我显示了这个导入过程.对不起它有点令人痛苦的长(2小时+),但这里的链接正好进入精灵表的导入.

https://youtu.be/Ic9Wnux8vd8?t=1h17m

按照我在这里展示的方式进行操作,你会得到一个精灵图册,带有切片图像.如果你想要的话,我的脚本(我在前面的剪辑中演示过)就在我的GitHub上.