Olá, atualmente estou investigando o PixiJS para um novo projeto e gosto muito dele até agora! Existem alguns requisitos relacionados ao desempenho do aplicativo. A parte realmente complicada é que isso resultará em muitos rótulos de texto exibidos que são atualizados constantemente. Não consegui encontrar uma maneira de melhorar ainda mais o desempenho e minha última esperança é encontrar alguma ajuda nesta comunidade.
1500 rótulos de texto
60 atualizações de posição por segundo
60 FPS
Já criei um exemplo mínimo usando BitMapText resultando em ~ 28 FPS no MacBook Pro.
https://www.pixiplayground.com/#/edit/rLbAN_xrw7yUU_cg_c8Xv
Você tem alguma ideia para melhorar isso? Muito obrigado antecipadamente!
Tem _ certeza_ de que precisa de 1500 na tela _todas_ vezes? Eliminar os rótulos fora da tela ajudará muito no desempenho. Pixi não faz nenhuma seleção fora da caixa, mas existem alguns plug-ins de comunidade (por exemplo, @pixi-essentials/cull
por @SukantPal) que podem ajudá-lo aqui.
Todos os 1500 rótulos são únicos? Se houver muitas duplicatas, você pode convertê-lo em um Sprite usando RenderTexture e compartilhar referências ao custo de mais memória.
@ChristophWalter O exemplo que você mostrou poderia se beneficiar potencialmente do abate como @bigtimebuddy mencionou. 1500 rótulos cada um com 112 caracteres é _muito_ para renderizar. Existem razões pelas quais seu exemplo não pode ser usado para sugerir uma otimização relevante, por exemplo, o texto é quase sempre sobreposto. Não acho que seu projeto exibirá um texto confuso que é tão denso que não pode ser lido pelo usuário.
Definir MESH.BATCHABLE_SIZE para um valor mais alto, como 200, pode ajudar. Eu não testei completamente isso porque ele ainda funciona a 45 FPS após uma redução de 6x da CPU no meu iMac.
Não fiz muito o perfil do exemplo. No entanto, se você está trabalhando em um projeto comercial e isso acaba sendo um gargalo do lado da GPU, você pode considerar um desordem que processa cada letra (todas As, depois todas as Bs, todas as Cs, etc.) juntas para melhorar a localidade da textura, desenvolvendo um mecanismo de bloco e / ou fazendo uma otimização diferente que renderiza a parte da tela que foi _alterada_ (ou seja, com base nas 60 atualizações de 1500 rótulos). Estas são apenas ideias e devem ser entendidas como tal.
Obrigado pela ajuda!
Na realidade, usaremos a seleção e será muito improvável que exibamos tantos rótulos ao mesmo tempo. Mas, por motivos de benchmark, precisamos competir com aplicativos que não sejam da web. Já questionamos esses requisitos, mas ajudaria muito se pudéssemos mostrar que o webgl pode lidar com isso também.
Os rótulos serão únicos. Mas eles podem não mudar com tanta frequência. O exemplo não muda o conteúdo do texto. Portanto, pode haver algum potencial para otimizar. Percebi que o FPS permanece o mesmo mesmo que eu não atualize as posições ( playground ). Existe uma maneira de renderizar novamente os textos apenas quando eles mudam?
Definir MESH.BATCHABLE_SIZE não mudou nada do meu lado. Vou dar uma olhada em suas outras ideias ou recomendar a compra de iMacs 😏 Não gosto muito de renderizar, então essas ideias realmente me ajudam!
Ah, se você realmente está otimizando aquele exemplo específico, então o método supremo seria fazer o que @bigtimebuddy disse, com uma mudança - usar uma malha e atualizar essa malha diretamente (ao invés de usar 1500 sprites).
Isso oferece dois benefícios principais:
Então, basicamente, o processo seria semelhante a:
Este deve ser um processo direto. Por favor, diga-nos como foi!
Ei, tentei implementar sua sugestão. Converter o BitmapText em uma textura parecia funcionar. Mas atualmente estou tentando exibir a textura em uma malha. Tentei usar um PIXI.Mesh e PIXI.SimpleMesh. Preciso criar meu próprio sombreador ou posso usar 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_: Corrigido. Os vértices precisam representar coordenadas de pixel. _Próximo passo_: Use mais de uma textura na malha.
const vertices = [
0, 0,
500, 0,
500, 500,
0, 500
];
-
_Editar 2_: Isso só funcionará ao usar uma textura para todos os rótulos, certo? Eu posso ter me expressado errado. Os rótulos serão únicos, mas o conteúdo não mudará em cada quadro.
Percebi que o FPS permanece o mesmo mesmo que eu não atualize as posições ( playground ). Existe uma maneira de renderizar novamente os textos apenas quando eles mudam?
Um PIXI.Mesh ajudará nisso? Você conhece algum recurso onde eu possa ler sobre o processo de renderização do PixiJS?
@ChristophWalter Você mesmo pode criar um renderizador (em vez de usar um aplicativo), configurar um loop de ticker que chama Renderer.render
_only_ quando sua cena muda.
Você também pode passar autoStart: false
para as opções do aplicativo e chamar app.render()
quando algo mudar. Mesmo diff.
Obrigado, de alguma forma funcionou gerando uma textura a partir do texto e usando-a em um sprite. Mas atualmente não entendo o porquê e não consigo reproduzi-lo em um playground. 😄
No entanto, o aplicativo agora atualiza todas as 1500 posições de rótulo a cada quadro com 60FPS. Além disso, a cada segundo o conteúdo do texto é atualizado. Isso leva a um breve congelamento a cada segundo. Portanto, a próxima etapa será tornar essas atualizações de texto assíncronas, por exemplo, em um web worker. Atualmente, estou lendo sobre ImageBitmap e OffscreenCanvas. Isso pode ser interessante para outras pessoas, então compartilharei meu progresso.
Breve atualização, pois parei de investigar há algumas semanas
Eu renderizei o texto em um web worker que funcionou bem direto:
// 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));
}
Infelizmente, não há nenhuma melhoria para meu caso de uso. Os frames ainda caem durante a renderização dos textos. Talvez devido ao trabalho necessário para transferir os bitmaps de imagem do trabalhador.
Para nosso caso de uso específico, podemos recomendar a redução do comprimento do texto de cada etiqueta para atender aos requisitos de desempenho.
Comentários muito úteis
Obrigado, de alguma forma funcionou gerando uma textura a partir do texto e usando-a em um sprite. Mas atualmente não entendo o porquê e não consigo reproduzi-lo em um playground. 😄
No entanto, o aplicativo agora atualiza todas as 1500 posições de rótulo a cada quadro com 60FPS. Além disso, a cada segundo o conteúdo do texto é atualizado. Isso leva a um breve congelamento a cada segundo. Portanto, a próxima etapa será tornar essas atualizações de texto assíncronas, por exemplo, em um web worker. Atualmente, estou lendo sobre ImageBitmap e OffscreenCanvas. Isso pode ser interessante para outras pessoas, então compartilharei meu progresso.