Pixi.js: 大量のテキストをレンダリングしてその位置を更新しながら、高いFPSを達成するにはどうすればよいですか?

作成日 2020年09月25日  ·  10コメント  ·  ソース: pixijs/pixi.js

こんにちは、私は現在、新しいプロジェクトのためにPixiJSを調査しており、これまでのところ本当に気に入っています! アプリケーションのパフォーマンスに関していくつかの要件があります。 本当に注意が必要なのは、これにより、表示されるテキストラベルが多くなり、常に更新されることです。 パフォーマンスをさらに向上させる方法を見つけることができませんでした。私の最後の望みは、このコミュニティで助けを見つけることです。

ゴール

1500のテキストラベル
1秒あたり60の位置更新
60 FPS

ピクシープレイグラウンド

私はすでにBitMapTextを使用して最小限の例を作成し、MacBookProで最大28FPSになりました。
https://www.pixiplayground.com/#/edit/rLbAN_xrw7yUU_cg_c8Xv

これを改善するためのアイデアはありますか? よろしくお願いします!

🤔 Question

最も参考になるコメント

おかげで、それはどういうわけかテキストからテクスチャを生成し、それをスプライトで使用するのに役立ちました。 しかし、私は現在その理由を理解しておらず、遊び場でそれを再現することはできません。 😄

ただし、アプリケーションはフレームごとに1500のラベル位置すべてを60FPSで更新するようになりました。 さらに、毎秒テキストコンテンツが更新されます。 これにより、毎秒短いフリーズが発生します。 したがって、次のステップは、たとえばWebワーカーで、これらのテキスト更新を非同期にレンダリングすることです。 私は現在、ImageBitmapとOffscreenCanvasについて読んでいます。 これは他の人にとって興味深いかもしれないので、私は私の進歩を共有します。

全てのコメント10件

いつでも画面に1500が必要ですか? オフスクリーンラベルをカリングすると、パフォーマンスが大幅に向上します。 Pixiは箱から出してカリングを行いませんが、ここで役立つ可能性のあるコミュニティプラグインがいくつかあります(たとえば、@ SukantPalによる@pixi-essentials/cull )。

1500のラベルはすべてユニークですか? 重複が多い場合は、RenderTextureを使用してスプライトに変換し、より多くのメモリを犠牲にして参照を共有できます。

@ChristophWalterあなたが示した例は、 @ bigtimebuddyが述べたように、カリングの恩恵を受ける可能性があります。 それぞれ112文字の1500のラベルは、レンダリングするのに_たくさん_です。 例を使用して関連する最適化を提案できない理由があります。たとえば、テキストがほとんど重複しています。 あなたのプロジェクトは、ユーザーが読めないほど密集した乱雑なテキストを表示することはないと思います。

MESH.BATCHABLE_SIZEを200などのより高い値に設定すると役立つ場合があります。 iMacでCPUが6倍遅くなった後も、45 FPSで電力が供給されるため、これを徹底的にテストしませんでした。

私は例をあまりプロファイルしていません。 ただし、商用プロジェクトに取り組んでいて、これがGPU側のボトルネックであることが判明した場合は、各文字(すべてのAs、次にすべてのB、すべてのCなど)を一緒にレンダリングする順序が狂っていると考えるかもしれません。テクスチャの局所性を改善するため、タイルエンジンを開発する、および/または画面の_変更された部分をレンダリングする(つまり、1500個のラベルからの60回の更新に基づく)差分最適化を実行します。 これは単なるアイデアであり、そのように解釈する必要があります。

ご協力ありがとうございました!

実際には、カリングを使用し、同時に多くのラベルを表示することはほとんどありません。 しかし、ベンチマークの理由から、Web以外のアプリケーションと競争する必要があります。 これらの要件についてはすでに質問しましたが、webglがこれも処理できることを示すことができれば非常に役立ちます。

ラベルは一意になります。 しかし、それほど頻繁には変更されない可能性があります。 この例では、テキストの内容はまったく変更されていません。 したがって、最適化する可能性があるかもしれません。 位置(遊び場)を更新しなくても、FPSは同じままであることに気づきました。 テキストが変更された場合にのみテキストを再レンダリングする方法はありますか?

MESH.BATCHABLE_SIZEを設定しても、私の側では何も変わりませんでした。 私はあなたの他のアイデアを検討するか、iMacの購入をお勧めします😏私はレンダリングにそれほど興味がないので、これらのアイデアは本当に私を助けます!

ああ、あなたが本当にその特定の例を最適化しているなら、最高の方法は@bigtimebuddyが言ったことを1つの変更で行うこと

これには2つの重要な利点があります。

  • これにより、バッチレンダラーのバッファリングフェーズのオーバーヘッドが排除されます。
  • 頂点の数は112倍に削減されます(4 /文字ではなく4 /テキスト)。 これにより、頂点がわずか6Kになります。
  • シーン内の1つのDisplayObjectのみ。

したがって、基本的に、プロセスは次のようになります。

  1. BitmapTextをRenderTextureにレンダリングします
  2. 1500 * 4の頂点を持つメッシュを作成します。
  3. 頂点を直接アニメートします。 インスタンスごとに4つの頂点(つまり長方形)があるため、注意する必要があります。 したがって、最初の頂点の位置を計算します。 次に、他の3つは(x +幅、高さ)、(x +幅、y +高さ)、(x、y +高さ)になります。

これは簡単なプロセスである必要があります。 どうなるか教えてください!

ねえ、私はあなたの提案を実行しようとしました。 BitmapTextをテクスチャに変換することはうまくいったようです。 しかし、私は現在、テクスチャをメッシュで表示しようとして立ち往生しています。 PIXI.MeshとPIXI.SimpleMeshを使ってみました。 独自のシェーダーを作成する必要がありますか、それともPIXI.MeshMaterialを使用できますか?

const bitmapFontText = new PIXI.BitmapText(
    'Lorem ipsum dolor\nsit amet consetetur\nsadipscing elitr sed', 
    { font: '55px Desyrel', align: 'left' }
);

const texture =  PIXI.RenderTexture.create({ width: 800, height: 600 });
app.renderer.render(bitmapFontText, texture);

const vertices = [
    -0.5, -0.5,
    0.5, -0.5,
    0.5, 0.5,
    -0.5, 0.5
];
const uvs = [
    0, 0,
    1, 0,
    1, 1,
    0, 1,
];
const indices = [0, 1, 2, 0, 2, 3];
const geometry = new PIXI.MeshGeometry(vertices, uvs, indices);
const shader = new PIXI.MeshMaterial(texture);

const mesh = new PIXI.Mesh(geometry, shader);
app.stage.addChild(mesh);

https://www.pixiplayground.com/#/edit/7RHqFti0tdSzw -6iOtylk

-
_編集_:修正しました。 頂点はピクセル座標を表す必要があります。 _次のステップ_:メッシュで複数のテクスチャを使用します。

const vertices = [
    0, 0,
    500, 0,
    500, 500,
    0, 500
];

-
_編集2_:これは、すべてのラベルに1つのテクスチャを使用する場合にのみ機能します。 私は自分自身を間違って表現したかもしれません。 ラベルは一意になりますが、内容はフレームごとに変わるわけではありません。

位置(遊び場)を更新しなくても、FPSは同じままであることに気づきました。 テキストが変更された場合にのみテキストを再レンダリングする方法はありますか?

PIXI.Meshはこれを支援しますか? PixiJSのレンダリングプロセスについて読むことができるリソースを知っていますか?

@ChristophWalter (アプリケーションを使用する代わりに)自分でレンダラーを作成し、シーンが変更されたときにRenderer.render _ only_を呼び出すティッカーループを設定できます。

autoStart: falseをアプリケーションオプションに渡し、何かが変更されたときにapp.render()呼び出すこともできます。 同じ差分。

おかげで、それはどういうわけかテキストからテクスチャを生成し、それをスプライトで使用するのに役立ちました。 しかし、私は現在その理由を理解しておらず、遊び場でそれを再現することはできません。 😄

ただし、アプリケーションはフレームごとに1500のラベル位置すべてを60FPSで更新するようになりました。 さらに、毎秒テキストコンテンツが更新されます。 これにより、毎秒短いフリーズが発生します。 したがって、次のステップは、たとえばWebワーカーで、これらのテキスト更新を非同期にレンダリングすることです。 私は現在、ImageBitmapとOffscreenCanvasについて読んでいます。 これは他の人にとって興味深いかもしれないので、私は私の進歩を共有します。

数週間前に調査をやめたので、短い更新
私は実際に非常に簡単に機能するWebワーカーでテキストをレンダリングしました。

// render-text-worker.js
onmessage = function(event) {
    const textLines = event.data.text.split(/\n/);
    const index = event.data.index;
    const offscreen = new OffscreenCanvas(150,45);
    const ctx = offscreen.getContext("2d");
    ctx.font = "15px monospace";
    textLines.forEach((text, index) => {
        ctx.fillText(text, 0, 15 + index * 15)
    })
    const imageBitmap = offscreen.transferToImageBitmap();
    postMessage({imageBitmap, index});
};
// index.js
const worker = new Worker("render-text-worker.js");
const callbacks ={}
function getLabelTexture(index, text, callback) {
  callbacks[index] = callback
  worker.postMessage({ index, text });
}
worker.onmessage = function({ data }) {
  const callback = callbacks[data.index]
  callback(PIXI.Texture.from(data.imageBitmap));
}

残念ながら、私のユースケースには改善がありません。 テキストのレンダリング中もフレームはドロップします。 おそらく、ワーカーから画像ビットマップを転送するために必要な作業が原因です。

特定のユースケースでは、パフォーマンス要件を達成するために、各ラベルのテキスト長を短くすることをお勧めします。

このページは役に立ちましたか?
0 / 5 - 0 評価