Skip to content
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

Closed
ChristophWalter opened this issue Sep 25, 2020 · 10 comments
Labels
🤔 Question User question, similar to Help Wanted or Needs Help. These can be addressed whenever someone has tim

Comments

@ChristophWalter
Copy link

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!

@bigtimebuddy
Copy link
Member

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., @pixi-essentials/cull by @SukantPal) that might help you here.

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.

@ShukantPal ShukantPal added the 🤔 Question User question, similar to Help Wanted or Needs Help. These can be addressed whenever someone has tim label Sep 25, 2020
@ShukantPal
Copy link
Member

@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.

@ChristophWalter
Copy link
Author

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!

@ShukantPal
Copy link
Member

ShukantPal commented Sep 26, 2020 via email

@ChristophWalter
Copy link
Author

ChristophWalter commented Sep 28, 2020

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

--
Edit: Fixed it. The vertices need to represent pixel coordinates. Next step: Use more than one texture in the mesh.

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

--
Edit 2: This will only work when using one texture for all labels, right? I might have expressed myself wrong. The labels will be unique, but the contents will not change in every frame.

@ShukantPal ShukantPal reopened this Sep 28, 2020
@ChristophWalter
Copy link
Author

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?

Will a PIXI.Mesh help with this? Do you know any resource where I can read about the rendering process of PixiJS?

@ShukantPal
Copy link
Member

@ChristophWalter You can create a renderer yourself (instead of using an Application), setup a ticker loop which calls Renderer.render only when your scene changes.

@bigtimebuddy
Copy link
Member

You can also pass autoStart: false to the Application options and then call app.render() when something changes. Same diff.

@ChristophWalter
Copy link
Author

ChristophWalter commented Sep 29, 2020

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.

@ChristophWalter
Copy link
Author

Short update as I stopped investigating some weeks ago
I rendered the text in a web worker which actually worked quite straight forward:

// 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🤔 Question User question, similar to Help Wanted or Needs Help. These can be addressed whenever someone has tim
Projects
None yet
Development

No branches or pull requests

3 participants