New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to achieve high FPS while rendering lots of text and updating its position? #6909
Comments
Are you sure you need 1500 on screen at all times? Culling offscreen labels will help a lot with performance. Pixi doesn't do any culling out of the box, but there are a few community plugins (e.g., Are all 1500 labels unique? If there are many duplicates, you can convert it into a Sprite using a RenderTexture and share references at the expensive of more memory. |
@ChristophWalter The example you've shown could potentially benefit from culling as @bigtimebuddy mentioned. 1500 labels each with 112 characters is a lot to render. There are reasons why your example cannot be used to suggest relevant optimization, for example the text is mostly overlapping. I don't think your project will be displaying jumbled text that is so dense it can't be read by the user. Setting MESH.BATCHABLE_SIZE to a higher value like 200 might help. I didn't throughly test this because it still powers through 45 FPS after a 6x CPU slowdown on my iMac. I haven't profiled the example too much. However, if you're working on a commercial project & this turns out to be GPU-side bottleneck, you might consider an out-of-order that renders each letter (all As, then all Bs, all Cs, etc.) together to improve texture locality, developing a tile-engine and/or doing a diff-rect optimization that renders the portion of the screen that has changed (i.e. based on the 60 updates out of 1500 labels). This are just ideas and should be taken as such. |
Thank you for your help! In reality we will use culling and it will be very unlikely to display that many labels at the same time. But for benchmark reasons we need to compete against non-web applications. We already questioned these requirements, but it would help a lot if we can show that webgl can handle this as well. The labels will be unique. But they might not change that often. The example doesn't change the text content at all. So there might be some potential to optimize. I noticed that the FPS stay the same even if I do not update the positions (playground). Is there a way to rerender the texts only when they change? Setting MESH.BATCHABLE_SIZE did not change anything on my side. I will take a look at your other ideas or recommend to buy iMacs 😏 I am not that much into rendering, so these ideas really help me! |
Ah, if you really are optimization that specific example then the supreme method would be to do what @bigtimebuddy said, with one change - use a Mesh and update that mesh directly (instead of using 1500 sprites).
This provides two key benefits:
+ This will eliminate the overhead of the batch renderer’s buffering phase.
+ The number of vertices will be cut by 112x (4/text instead of 4/character). That brings down the vertices to just 6K.
+ Only one DisplayObject in the scene.
So basically, the process would look like:
1. Render the BitmapText into a RenderTexture
2. Create a Mesh with 1500*4 vertices.
3. Animate the vertices directly. You must be careful because there are four vertices per instance (i.e. rectangles). So you would calculate the position for the first vertex. Then the other three would be (x + width, height), (x + width, y + height), (x, y + height).
This should be a straightforward process. Please tell us how it goes!
|
Hey, I tried to implement your suggestion. Converting the BitmapText to a texture seemed to work. But I am currently stuck trying to display the texture in a mesh. I tried using a PIXI.Mesh and PIXI.SimpleMesh. Do I need to create my own shader or can I use 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
]; -- |
Will a PIXI.Mesh help with this? Do you know any resource where I can read about the rendering process of PixiJS? |
@ChristophWalter You can create a renderer yourself (instead of using an Application), setup a ticker loop which calls |
You can also pass |
Thanks, it somehow worked generating a texture from the text and using it on a sprite. But I currently do not understand why and I can't reproduce it in a playground. 😄 However the application now updates all 1500 label positions every frame with 60FPS. Furthermore each second the text content gets updated. This leads to a short freeze every second. So the next step will be to render those text updates asynchronous, e.g. in a web worker. I am currently reading about ImageBitmap and OffscreenCanvas. This might be interesting to others, so I will share my progress. |
Short update as I stopped investigating some weeks ago // 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));
} Unfortunately there is no improvement for my use case. The frames still drop while rendering the texts. Maybe due to the work required for transferring the image bitmaps from the worker. For our specific use case we ware able to recommend reducing the text length of each label to achieve the performance requirements. |
Hi, I am currently investigating PixiJS for a new project and I really like it so far! There are some requirements regarding the performance of the application. The really tricky part is that this will result in lots of displayed text labels which are constantly updated. I could not find a way to further improve the performance and my last hope is to finde some help in this community.
Goal
1500 text labels
60 position updates per second
60 FPS
Pixi Playground
I already created a minimal example using BitMapText resulting in ~28 FPS on MacBook Pro.
https://www.pixiplayground.com/#/edit/rLbAN_xrw7yUU_cg_c8Xv
Do you have any idea to improve this? Thanks a lot in advance!
The text was updated successfully, but these errors were encountered: