Pixi.js: Bagaimana cara mencapai FPS tinggi sambil merender banyak teks dan memperbarui posisinya?

Dibuat pada 25 Sep 2020  ·  10Komentar  ·  Sumber: pixijs/pixi.js

Hai, saya sedang menyelidiki PixiJS untuk proyek baru dan sejauh ini saya sangat menyukainya! Ada beberapa persyaratan terkait kinerja aplikasi. Bagian yang benar-benar rumit adalah ini akan menghasilkan banyak label teks yang ditampilkan yang terus diperbarui. Saya tidak dapat menemukan cara untuk lebih meningkatkan kinerja dan harapan terakhir saya adalah menemukan bantuan di komunitas ini.

Tujuan

1500 label teks
60 pembaruan posisi per detik
60 FPS

Pixi Playground

Saya sudah membuat contoh minimal menggunakan BitMapText yang menghasilkan ~ 28 FPS di MacBook Pro.
https://www.pixiplayground.com/#/edit/rLbAN_xrw7yUU_cg_c8Xv

Apakah Anda punya ide untuk memperbaiki ini? Terima kasih banyak sebelumnya!

🤔 Question

Komentar yang paling membantu

Terima kasih, entah bagaimana cara ini berhasil menghasilkan tekstur dari teks dan menggunakannya pada sprite. Tapi saat ini saya tidak mengerti mengapa dan saya tidak bisa mereproduksinya di taman bermain. 😄

Namun aplikasi sekarang memperbarui semua 1500 posisi label setiap frame dengan 60FPS. Selanjutnya setiap detik konten teks diperbarui. Hal ini menyebabkan pembekuan singkat setiap detik. Jadi langkah selanjutnya adalah membuat pembaruan teks tersebut asynchronous, misalnya di web worker. Saat ini saya membaca tentang ImageBitmap dan OffscreenCanvas. Ini mungkin menarik bagi orang lain, jadi saya akan membagikan kemajuan saya.

Semua 10 komentar

Apakah Anda _sure_ Anda membutuhkan 1500 di layar pada _all_ kali? Memilah label di luar layar akan banyak membantu kinerja. Pixi tidak melakukan pemilahan apa pun di luar kotak, tetapi ada beberapa plugin komunitas (mis., @pixi-essentials/cull oleh @SukantPal) yang dapat membantu Anda di sini.

Apakah semua 1500 label itu unik? Jika ada banyak duplikat, Anda dapat mengubahnya menjadi Sprite menggunakan RenderTexture dan berbagi referensi dengan lebih banyak memori yang mahal.

@ChristophWalter Contoh yang telah Anda tunjukkan berpotensi mendapatkan keuntungan dari pemusnahan seperti yang disebutkan @bigtimebuddy . 1500 label masing-masing dengan 112 karakter adalah _a lot_ untuk dirender. Ada alasan mengapa contoh Anda tidak dapat digunakan untuk menyarankan pengoptimalan yang relevan, misalnya teks sebagian besar tumpang tindih. Saya rasa proyek Anda tidak akan menampilkan teks campur aduk yang begitu padat sehingga tidak dapat dibaca oleh pengguna.

Menyetel MESH.BATCHABLE_SIZE ke nilai yang lebih tinggi seperti 200 mungkin membantu. Saya tidak mengujinya secara menyeluruh karena masih menyala melalui 45 FPS setelah CPU melambat 6x di iMac saya.

Saya belum membuat profil contoh terlalu banyak. Namun, jika Anda sedang mengerjakan proyek komersial & ini ternyata menjadi hambatan sisi GPU, Anda dapat mempertimbangkan kerusakan yang membuat setiap huruf (semua As, lalu semua B, semua C, dll.) Bersama-sama untuk meningkatkan lokalitas tekstur, mengembangkan mesin ubin dan / atau melakukan pengoptimalan diff-rect yang menampilkan bagian layar yang telah _changed_ (yaitu berdasarkan 60 pembaruan dari 1500 label). Ini hanya ide dan harus dianggap seperti itu.

Terima kasih untuk bantuannya!

Pada kenyataannya kami akan menggunakan pemusnahan dan sangat tidak mungkin untuk menampilkan banyak label pada saat yang bersamaan. Tapi untuk alasan benchmark kita perlu bersaing dengan aplikasi non-web. Kami sudah mempertanyakan persyaratan ini, tetapi akan sangat membantu jika kami dapat menunjukkan bahwa webgl juga dapat menangani ini.

Labelnya akan unik. Tapi mereka mungkin tidak sering berubah. Contoh tersebut tidak mengubah konten teks sama sekali. Jadi mungkin ada beberapa potensi untuk dioptimalkan. Saya perhatikan bahwa FPS tetap sama meskipun saya tidak memperbarui posisi ( taman bermain ). Apakah ada cara untuk merender ulang teks hanya jika berubah?

Menyetel MESH.BATCHABLE_SIZE tidak mengubah apa pun di sisi saya. Saya akan melihat ide-ide Anda yang lain atau merekomendasikan untuk membeli iMac not Saya tidak terlalu suka rendering, jadi ide-ide ini sangat membantu saya!

Ah, jika Anda benar-benar mengoptimalkan contoh spesifik itu maka metode tertinggi adalah melakukan apa yang dikatakan @bigtimebuddy , dengan satu perubahan - gunakan Mesh dan perbarui mesh itu secara langsung (daripada menggunakan 1500 sprite).

Ini memberikan dua manfaat utama:

  • Ini akan menghilangkan overhead fase buffering penyaji batch.
  • Jumlah simpul akan dipotong 112x (4 / teks bukan 4 / karakter). Itu menurunkan simpul menjadi hanya 6K.
  • Hanya satu DisplayObject di tempat kejadian.

Jadi pada dasarnya, prosesnya akan terlihat seperti:

  1. Render BitmapText menjadi RenderTexture
  2. Buat Mesh dengan 1500 * 4 simpul.
  3. Menganimasikan simpul secara langsung. Anda harus berhati-hati karena ada empat simpul per contoh (yaitu persegi panjang). Jadi Anda akan menghitung posisi untuk simpul pertama. Kemudian tiga lainnya adalah (x + lebar, tinggi), (x + lebar, y + tinggi), (x, y + tinggi).

Ini harus menjadi proses yang mudah. Tolong beritahu kami bagaimana kelanjutannya!

Hei, saya mencoba menerapkan saran Anda. Mengubah BitmapText menjadi tekstur tampaknya berhasil. Tapi saya saat ini terjebak mencoba menampilkan tekstur dalam jaring. Saya mencoba menggunakan PIXI.Mesh dan PIXI.SimpleMesh. Apakah saya perlu membuat shader sendiri atau dapatkah saya menggunakan 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

-
_Edit_: Memperbaiki itu. Simpul perlu merepresentasikan koordinat piksel. _Langkah berikutnya_: Gunakan lebih dari satu tekstur di mesh.

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

-
_Edit 2_: Ini hanya akan berfungsi saat menggunakan satu tekstur untuk semua label, bukan? Saya mungkin telah salah menyatakan diri. Labelnya akan unik, tapi isinya tidak akan berubah di setiap frame.

Saya perhatikan bahwa FPS tetap sama meskipun saya tidak memperbarui posisi ( taman bermain ). Apakah ada cara untuk merender ulang teks hanya jika berubah?

Akankah PIXI.Mesh membantu dengan ini? Apakah Anda mengetahui sumber daya di mana saya dapat membaca tentang proses rendering PixiJS?

@ChristophWalter Anda dapat membuat sendiri penyaji (alih-alih menggunakan Aplikasi), menyiapkan loop ticker yang memanggil Renderer.render _only_ saat adegan Anda berubah.

Anda juga dapat memberikan autoStart: false ke opsi Aplikasi dan kemudian memanggil app.render() ketika ada perubahan. Perbedaan yang sama.

Terima kasih, entah bagaimana cara ini berhasil menghasilkan tekstur dari teks dan menggunakannya pada sprite. Tapi saat ini saya tidak mengerti mengapa dan saya tidak bisa mereproduksinya di taman bermain. 😄

Namun aplikasi sekarang memperbarui semua 1500 posisi label setiap frame dengan 60FPS. Selanjutnya setiap detik konten teks diperbarui. Hal ini menyebabkan pembekuan singkat setiap detik. Jadi langkah selanjutnya adalah membuat pembaruan teks tersebut asynchronous, misalnya di web worker. Saat ini saya membaca tentang ImageBitmap dan OffscreenCanvas. Ini mungkin menarik bagi orang lain, jadi saya akan membagikan kemajuan saya.

Pembaruan singkat karena saya berhenti menyelidiki beberapa minggu yang lalu
Saya membuat teks di pekerja web yang sebenarnya bekerja cukup lurus ke depan:

// 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));
}

Sayangnya tidak ada perbaikan untuk kasus penggunaan saya. Bingkai masih jatuh saat merender teks. Mungkin karena pekerjaan yang diperlukan untuk mentransfer bitmap gambar dari pekerja.

Untuk kasus penggunaan khusus kami, kami dapat menyarankan pengurangan panjang teks setiap label untuk mencapai persyaratan kinerja.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat