Jump to content

Perf drop when using WebGLRenderer and huge amount of Graphics


M3nace
 Share

Recommended Posts

Hello ^_^ ,
 
Actually, I'm trying to develop a parallel coordinates graphe using a WebGL rendering. Like http://bl.ocks.org/syntagmatic/raw/5023284/ but with Pixi.js, since webgl-2D is not maintened anymore. Except I don't have the same amount of data. I have more, a hell more, 100 000 lines minimum :o .
 
I already read some topics here and on Github, where recommandations are to use Sprite instead of Graphics. But the performance are worse . Switching to canvas ? The render is as smooth as a baby butt. The WebGL rendering of 100 000 lines uses 1,50Go of GPU RAM, which seems to be a lil' too much.
 
Here some code and explanations:
 

var rtContainer = new PIXI.DisplayObjectContainer();var stageFG = new PIXI.Stage(null, true);var renderFG = new PIXI.autoDetectRenderer(foreground.width, foreground.height,                                           { view:foreground, transparent:true, antialias:true, clearBeforeRender:false, preserveDrawingBuffer:true });stageFG.addChild(rtContainer);draw();

Here my context. foreground is just a <canvas> element and rtContainer will only contains new graphics to display in the stage. The function draw() will begin to render the stage when data are received.

var render_loop = 0;function draw() {   var requestAnimationId = requestAnimationFrame(draw);   renderFG.render(stageFG);   render_loop += rtContainer.children.length;   rtContainer.removeChildren();                                                                                                                   if (render_loop == data_length) {       cancelAnimationFrame(requestAnimationId);   }}; 

Function draw(), rendering the stage and deleting the graphics which have just been drawed. data_length is the number of data I will receive, which means I draw(), not until I received everything, but only when I rendered everything.

var update_loop = 0;this.update = function(new_data) {    path(new_data, rtContainer, color(new_data.classification));    ++update_loop;};

I receive data asynchronously, with server-side events, triggering the update function.
The new_data is a dictionary. I use the key classification for ordering. The function path() create the line under a graphic object:
 

function path(d, ctx, color, lineWidth) {    var graphics = new PIXI.Graphics();    var lineWidth = lineWidth || 1;        graphics.lineStyle(lineWidth, color.replace('#', '0x'), opacity);    var x = xscale(dimensions[0]) - 15,        y = yscale[dimensions[0]](d[dimensions[0]]); // left edge    graphics.moveTo(x, y);    dimensions.map(function(p) {        x = xscale(p),        y = yscale[p](d[p]);        graphics.lineTo(x, y);    });    graphics.lineTo(x + 15, y); // right edge    ctx.addChild(graphics);    delete graphics;};

Pretty much the same function of the example. is the data, ctx the rtContainer. My graphics is a line, with multiple points, and different colors. So each lines are differents. And voila.

 

Little benchmark :

Canvas rendering 120 000 lines : ~20sec, very smooth. Memory is happy.

WebGL rendering 120 000 lines : ~20sec, not so smooth, depends a lot of the number of lines. Huge amount of memory used.

I tried to use generateTexture() in the path() and use a SpriteBatch, but my comp crashed around 1000 lines... Not a good idea obviously.

 

My questions are :

- :huh:  Do I use Pixi.js correctly ?

- :huh:  Is there a way to improve performance of WebGL ? Any idea ?

- :huh:  Is it normal to use this quantity of memory when using WebGL ?

 

If you need more code or more explanations, I can provide it  :) (although, I'm not english, I apologise for any tears, vomiting, eye-gouging caused by my prose)

Link to comment
Share on other sites

Really hard to say. I need to fix my memory use too. It is just a jigsaw game but a lot of phones explode... uses over 500mb of texture memory I guess...

 

One idea is to use filters or shaders instead of textures for different colors. It might seem wasteful but it is the way to go in opengl/webgl land.

Link to comment
Share on other sites

Maybe this link can help you: http://www.html5gamedevs.com/topic/776-pixijs-now-draws-primitives-webgl-and-canvas/

 

Pixi computes polygons from lines to have nice corners.

 

Draw lines in webgl (openGL ES) in windows are not trivial.

 

In my post I have similar problems: http://www.html5gamedevs.com/topic/10124-mmo-strategy-game-drawing-graph-map/

Link to comment
Share on other sites

Small feedback.

The render is speeded up, but the memory allocation is still the same.

PIXI.WebGLGraphics.buildLine = function(graphicsData, webGLData){                                                                                                                                                                                    // TODO OPTIMISE!    var i = 0;    var points = graphicsData.points;        if (points.length === 0) return;    // if the line width is an odd number add 0.5 to align to a whole pixel    if (graphicsData.lineWidth % 2)    {        for (i = 0; i < points.length; i++) {            points[i] += 0.5;        }    }    var verts = webGLData.points;    var indices = webGLData.indices;    var length = points.length / 2;    var indexCount = points.length;    var indexStart = verts.length / 6;    // sort color    var color = PIXI.hex2rgb(graphicsData.lineColor);    var alpha = graphicsData.lineAlpha;    var r = color[0] * alpha;    var g = color[1] * alpha;    var b = color[2] * alpha;    var p1x, p1y, p2x, p2y;    for (i = 1; i < length - 1; i++) {        p1x = points[(i - 1) * 2];        p1y = points[(i - 1) * 2 + 1];        p2x = points[i * 2];        p2y = points[i * 2 + 1];        verts.push(p1x , p1y);        verts.push(r, g, b, alpha);        verts.push(p2x, p2y);        verts.push(r, g, b, alpha);    }    indices.push(indexStart);    for (i = 0; i < indexCount - 4; i++) {        indices.push(indexStart++);    }    indices.push(indexStart - 1);};
PIXI.WebGLGraphics.renderGraphics = function(graphics, renderSession){    ...    else    {        webGLData = webGL.data[i];        renderSession.shaderManager.setShader( shader );//activatePrimitiveShader();        shader = renderSession.shaderManager.primitiveShader;        gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true));        gl.uniform2f(shader.projectionVector, projection.x, -projection.y);        gl.uniform2f(shader.offsetVector, -offset.x, -offset.y);        gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint));        gl.uniform1f(shader.alpha, graphics.worldAlpha);        gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer);        gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0);        gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4);        // set the index buffer!        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer);        gl.drawElements(gl.LINE_STRIP,  webGLData.indices.length, gl.UNSIGNED_SHORT, 0 );    }    ...};
Link to comment
Share on other sites

Umm, I tried your code and with one Graphics object with 2.000 points only got 15 FPS.

 

Javascript only takes 0.1ms to draw the object, but seem that the graphic card can't draw 2000 vertexs. May need a special shader?.

 

¿What are you performance?

 

pd: With my intel 4600 -> 1 graphic object with 2.000 lines -> 15FPS. With my nvidia 650 -> 1 graphic object with 20.000 lines 55FPS

Link to comment
Share on other sites

I have wrote a little library that extend the pixi's Graphics object.

If you make '1px' line, it uses GL_LINES.

If you make '>1px' line, it uses the pixi algorithm.

https://github.com/DavidBM/pixi-nativeGLline

I tried to use GL_LINES instead of GL_LINE_STRIP, because GL_LINE_STRIP can't paint separate lines. The problem is that now you need to add 3 points per line, when the correct would be 2 points. I don't know why. Anyway, I'll keep updating it and improving it.

Link to comment
Share on other sites

Woohoo \o/

Solved the memory rape. My bad, create a new Graphics object in the function path() was NOT a good idea.

 

New code :

function draw() {   var requestAnimationId = requestAnimationFrame(draw);   renderFG.render(stageFG);   render_loop += rtContainer.children.length;   if (render_loop == data_length) {       cancelAnimationFrame(requestAnimationId);   }};
var graphics = new PIXI.Graphics();function path(d, ctx, color, lineWidth) {    var lineWidth = lineWidth || 1;        graphics.lineStyle(lineWidth, color.replace('#', '0x'), opacity);    var x = xscale(dimensions[0]) - 15,        y = yscale[dimensions[0]](d[dimensions[0]]); // left edge    graphics.moveTo(x, y);    dimensions.map(function(p) {        x = xscale(p),        y = yscale[p](d[p]);        graphics.lineTo(x, y);    });    graphics.lineTo(x + 15, y); // right edge    ctx.addChild(graphics);};

Don't need to clear the graphics, dunno why... And if i clear it, my whole scene goes blank.

With your library, benchmark:

120k line in 16s, as smooth as canvas, GPU memory doesn't flinch.

 

Thanks a lot for your help David  :D

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...