PixiJS rendering optimization

Recently, I have done online CAD visualization and editing, and I have selected the front-end visual rendering technology. For 2D CAD, canvas is generally enough, but every time the canvas is translated, zoomed, and the data is updated, all graphics need to be recalculated and rendered. Data, if there is too much data, it will be very stuck. If you use 3D webgl, without any third-party open source packaging technology, you will encounter many problems to realize 2D through webgl. Based on this, I found PixiJS, an engine that supports canvas and webgl rendering. The key is that it is very fast (I saw an online program test on github before, and there are more than a dozen rendering engine tests. I compared it and it is indeed the strongest. I forgot where this site is)

General Optimization Strategies

1. Using BatchGeomery

BatchGeomery is a technology to optimize rendering performance. It can combine multiple Geomery objects into one rendering batch, thereby reducing the number of rendering calls. It is an instantiation technology that requires the same graphics and properties of the object. Other Graphics Objects are optimized based on this benchmark object.

const app = new PIXI.Application();

document.body.appendChild(app.view);

const geometry = new PIXI.Geometry()
    .addAttribute('aVPos', [-100, 0, 100, 0, 0, -150]);

geometry.instanced = true;
geometry.instanceCount = 5;

const positionSize = 2;
const colorSize = 3;
const buffer = new PIXI.Buffer(new Float32Array(geometry.instanceCount * (positionSize + colorSize)));

geometry.addAttribute('aIPos', buffer, positionSize, false, PIXI.TYPES.FLOAT, 4 * (positionSize + colorSize), 0, true);
geometry.addAttribute('aICol', buffer, colorSize, false, PIXI.TYPES.FLOAT, 4 * (positionSize + colorSize), 4 * positionSize, true);

for (let i = 0; i < geometry.instanceCount; i++) {
    
    
    const instanceOffset = i * (positionSize + colorSize);

    buffer.data[instanceOffset + 0] = i * 80;
    buffer.data[instanceOffset + 2] = Math.random();
    buffer.data[instanceOffset + 3] = Math.random();
    buffer.data[instanceOffset + 4] = Math.random();
}

const shader = PIXI.Shader.from(`
    precision mediump float;
    attribute vec2 aVPos;
    attribute vec2 aIPos;
    attribute vec3 aICol;

    uniform mat3 translationMatrix;
    uniform mat3 projectionMatrix;

    varying vec3 vCol;

    void main() {
        vCol = aICol;

        gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVPos + aIPos, 1.0)).xy, 0.0, 1.0);
    }`,

`precision mediump float;

    varying vec3 vCol;

    void main() {
        gl_FragColor = vec4(vCol, 1.0);
    }

`);

const triangles = new PIXI.Mesh(geometry, shader);

triangles.position.set(400, 300);

app.stage.addChild(triangles);

app.ticker.add((delta) => {
    
    
    triangles.rotation += 0.01;
});

2. Use WebGL renderer: WebGL renderer can use GPU to accelerate rendering, thereby improving rendering performance

PixiJS uses WebGL rendering by default, but can be forced to use Canvas rendering in the following way

// 创建一个使用Canvas渲染的Pixi应用程序
const app = new PIXI.Application({
    
    
    width: 800,
    height: 600,
    forceCanvas: true // 强制使用Canvas渲染
});

If you want to switch the renderer at runtime you can use the following code

// 获取当前渲染器
const renderer = app.renderer;

// 切换渲染器
if (renderer instanceof PIXI.WebGLRenderer) {
    
    
    app.renderer = new PIXI.CanvasRenderer();
} else if (renderer instanceof PIXI.CanvasRenderer) {
    
    
    app.renderer = new PIXI.WebGLRenderer();
}

3. Reduce the number of rendering objects

Reducing the number of render objects improves rendering performance by reducing the number of render calls. This can be done by merging objects, using atlases, etc.

For CAD data, if a Grpahics object is used for each vector, not only the memory consumption is relatively large, but also the performance drops severely. If the data of the entire CAD drawing is not modified, all the graphics can be drawn into a Graphics object, and the general method is as follows:

var g1=new PIXI.Graphics();
//绘制第一个对象;
g1.beginFill(0xFF0000);
g1.drawCircle(10,10,20);
gl.endFill();
//绘制第二个对象;
g1.beginFill(0x00FF);
g1.drawRect(0,0,10,10);
gl.endFill();
//绘制后面的对象
...
//添加到舞台;
app.stage.addChild(g1);

The following is the rendering of nearly 10M original CAD data (about 1 million objects) using one object and one Graphics object. The memory consumption is 11G, the frame rate is basically 0, and it is in a semi-suspended state
1681627662787-2b42eca6-f6ec-47fa-ba35-83eb7bba125e.png
. In a Graphics, it can be found that the frame rate can reach nearly 40 frames, and the memory consumption is 4500M (but this method is not suitable for operating a single object, and there is no problem for pure browsing)! [1681627285950-e8e26fbc-
018d- 4363-9373-106507d0fadc.png])

4. Shared geometric objects

Instead of a single shape object, we create multiple shapes that all share the same geometry. The only optional constructor for a graph is a reference to another graph's geometry instance. This method updates

var g1=new PIXI.Graphics();
g1.beginFill(0xFF0000);
g1.drawRect(0,0,100,100);
g1.endFill();
app.stage.addChild(g1);

var g2=new PIXI.Graphics(g1.geometry); //如果和第一个对象只是位置不同,可以这样使用.
g2.x=g2.x+10;
g2.y=g2.y+10;
app.stage.addChild(g2);

5. Using bitmap text

Bitmap text can also greatly reduce the memory usage of the renderer. For specific usage, please refer to " PixiJs Text Blurring "

6. Render Textures

This method does not use shared graphics geometry, but instead uses a RenderTexture to render a single graphics object. Then use it like any texture, and share it between sprites. Essentially, it's the same effect, but without the graphics overhead and better anti-aliasing. If you need to scale the circle, just render the render texture with a larger initial size (in the case of extreme zoom, the display will still be blurry)
image.png

7. Set to Cullable

When dealing with thousands of elements, it is very important to only display the basic elements on the screen. When there is less data in the visible area, the performance can be greatly improved (probably due to the problem of pixijs outsourcing calculations, the efficiency improvement is not very high. obvious)
image.png

8. Avoid using too many filters: Filters can enhance rendering effects, but too many filters will affect rendering performance.

9. Use low-resolution textures: Using low-resolution textures can reduce the burden on the GPU, thereby improving rendering performance.

10. Avoid using too much transparency: Transparency will increase the GPU burden, and too much transparency will affect rendering performance.

Guess you like

Origin blog.csdn.net/qq_33377547/article/details/130184896