0

TextureAtlas必要なときにいつでも動的オブジェクトを生成できるアセット クラスの作成に取り組んでいます。具体的な方法はAssets.generateTextureAtlas()、テクスチャ アトラスを頻繁に再生成する必要があり、53 ミリ秒の平均よりも良い時間を取得したいと考えていたため、可能な限り最適化しようとしています。

53 ミリ秒は現在、約 3 フレームのコストがかかります。これは、テクスチャ アトラス内にパックする必要があるアイテムと、それらを生成する必要がある頻度が増えるにつれて、すぐに加算される可能性があります。したがって、私のコード内のすべての落とし穴に対する答えは素晴らしいでしょう。

クラス コード全体は、こちらの github gist で入手できます。

このRectanglePackerクラスは、(テクスチャ パッカーと同様に) 四角形をできるだけ近くにパックするために単純に使用され、ここで見つけることができます。

参考までに、方法は次のとおりです。

public static function generateTextureAtlas(folder:String):void
{
    if (!_initialised) throw new Error("Assets class not initialised.");

    if (_renderTextureAtlases[folder] != null)
    {
        (_renderTextureAtlases[folder] as TextureAtlas).dispose();
    }

    var i:int;
    var image:Image = new Image(_blankTexture);
    var itemName:String;
    var itemNames:Vector.<String> = Assets.getNames(folder + "/");
    var itemsTexture:RenderTexture;
    var itemTexture:Texture;
    var itemTextures:Vector.<Texture> = Assets.getTextures(folder + "/");
    var noOfRectangles:int;
    var rect:Rectangle;
    var rectanglePacker:RectanglePacker = new RectanglePacker();
    var texture:Texture;

    noOfRectangles = itemTextures.length;

    if (noOfRectangles == 0)
    {
        return;
    }

    for (i = 0; i < noOfRectangles; i++)
    {
        rectanglePacker.insertRectangle(Math.round(itemTextures[i].width), Math.round(itemTextures[i].height), i);
    }

    rectanglePacker.packRectangles();

    if (rectanglePacker.rectangleCount != noOfRectangles)
    {
        throw new Error("Only " + rectanglePacker.rectangleCount + " out of " + noOfRectangles + " rectangles packed for folder: " + folder);
    }

    itemsTexture = new RenderTexture(rectanglePacker.width, rectanglePacker.height);

    itemsTexture.drawBundled(function():void
    {
        for (i = 0; i < noOfRectangles; i++)
        {
            itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
            rect = rectanglePacker.getRectangle(i, rect);

            image.texture = itemTexture;
            image.readjustSize();

            image.x = rect.x + itemTexture.frame.x;
            image.y = rect.y + itemTexture.frame.y;

            itemsTexture.draw(image);
        }
    });

    _renderTextureAtlases[folder] = new TextureAtlas(itemsTexture);

    for (i = 0; i < noOfRectangles; i++)
    {
        itemName = itemNames[rectanglePacker.getRectangleId(i)];
        itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
        rect = rectanglePacker.getRectangle(i);

        (_renderTextureAtlases[folder] as TextureAtlas).addRegion(itemName, rect, itemTexture.frame);
    }
}
4

3 に答える 3

0

他の人が言ったように、パフォーマンスが悪い理由が最適化されていない AS コードである可能性は低いです。プロファイラー (Scout など) からの出力は非常に役立ちます。ただし、目的が新しいテクスチャを追加するだけの場合は、いくつかの最適化を提案できます。

  1. 毎回アトラス全体を再生成する必要があるのはなぜですか (Assets.getTextures()新しいレンダー テクスチャの呼び出しと作成)。既存のアトラスに新しい項目を追加してみませんか? 新しいRenderTexture(したがって、GPU メモリ内の新しいテクスチャ) の作成は、CPU と GPU 間の同期が必要なため、非常にコストのかかる操作です。一方、描画RenderTextureはすべて GPU 内で行われるため、はるかに時間がかかりません。

  2. すべてのアイテムをグリッドに配置する場合RectanglePacker、すべての長方形がグリッドの寸法と一致する同じ寸法を持つことができるため、使用を避けることができます。

編集:

明確にするために、少し前に同様の問題がありました。新しいアイテムを既存のアトラスに定期的に追加する必要がありました。そして、この操作のパフォーマンスは許容範囲内でした (1024x1024 の動的テクスチャを使用した iPad3 で約 8 ミリ秒)。しかし、ダイナミック アトラス アイテムを含むRenderTexture同じSpriteオブジェクトを使用しました。Image新しいアイテムを追加する必要があるときは、必要なテクスチャ (スタンドアロンまたは別の静的アトラスから) で新しいアイテムを作成し、それをSpriteコンテナー内に配置してから、このコンテナーをRenderTexture. アイテムの削除/変更も同様です。

于 2013-01-26T13:25:32.923 に答える
0

プロジェクトをよく読んで、すべてを最適化できるものを見つけるには、確かに時間がかかります。

rectanglePacker.getRectangle(i)内部ループへの複数の呼び出しを削除することから始めます。

例えば ​​:

    itemName = itemNames[rectanglePacker.getRectangleId(i)];
    itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
    rect = rectanglePacker.getRectangle(i);

おそらく、次のようになった可能性があります。

    rect = rectanglePacker.getRectangle(i);
    itemName = itemNames[rect];
    itemTexture = itemTextures[rect];

getRectangleが実際に「長方形を取得」するだけで、何も設定しない場合。

于 2013-01-26T06:15:55.187 に答える
0

手元にあるより大きな問題はこれだと思います。これ以上時間がかからない状況で、なぜランタイム中にこれを行わなければならないのですか? これは拡張性の高い操作です。これをどれだけ最適化しても、AS3 で実行すると、おそらく約 40 ミリ秒かかることになります。

これが、コンパイル時、またはフレームレートが重要ではなく、余裕がある場合の「画面のロード」またはその他の「トランジション」中に、これらの種類の操作を実行する必要がある理由です。

または、C++ または他の言語で別のシステムを作成して、最終的な結果を得る数値処理を実際に処理できるようにします。

また、パフォーマンスのチェックに関しては、はい、関数全体で 53 ミリ秒かかりますが、これらのミリ秒はどこで使用されるのでしょうか? 53ms は何も言わず、犯人を見つけた「オーバーヘッド プロファイリング」にすぎません。その関数内で実際に時間がかかるのは何かについて信頼できる情報を収集するには、それを小さなチャンクに分割する必要があります。

つまり、その関数内には、3 つの for ループ、他のクラスへのいくつかの呼び出し、キャスト、削除、作成があります。1 つのことを行っているわけではありません。その関数は、おそらく 500 行以下のコードと莫大な数の CPU 操作になります。そして、あなたはそれがどこで使われているのか分かりません。その時間の 60%をrectanglePacker.packRectangles();占めるのは .

AS3 の実行時にこれを行う必要がある場合は、これを複数のフレームに分散して実行し、10 フレーム程度でワークロードを均等に分散することをお勧めします。別のスレッドとワーカーの助けを借りてそれを行うこともできます。しかし、これはおそらく別の機会に行われる可能性があるため、これは設計エラーのように思えます。そうでない場合は、これらの種類の操作に適した別の言語で。

これをプロファイリングする最も簡単な方法は、次のようなタイムスタンプをいくつか追加することです。

var timestamps:Array = [];

そしてgetTimer()、コードのさまざまな場所でプッシュし、機能が完了したらそれらを出力します

于 2013-01-26T08:39:28.920 に答える