phetsims / scenery

Scenery is an HTML5 scene graph.
MIT License
53 stars 12 forks source link

Render simulation components in WebGL *without* scenery #324

Open samreid opened 9 years ago

samreid commented 9 years ago

At today's developer meeting, @jonathanolson suggested rendering an entire simulation using WebGL but without using scenery. This will allow us to investigate optimizing WebGL rendering, understanding the tradeoffs of different approaches and finding out what the maximal achievable performance to inform our scenery implementation.

samreid commented 9 years ago

@jonathanolson & I discussed this:

JO: it would be good to have rounded rectangles to stand-in for control panels, text in the control panel, icons, moving objects (no interaction), changing text. This will let us focus on getting good performance, keeping things look sharp. See if we can do a single draw call or just a few draw calls?

SR: This will help us get a good sense of what performance we can achieve, and we can include free parameters of (a) how many particles (b) how many static elements (c) how many different kinds of elements, etc.

JO: To do everything in one draw call, we would have to use one giant texture. May be impractical if changing every frame. Could use the z-buffer for a large number of levels.

JO: We should also investigate clipping--use the stencil buffer. Clipping could complicate everything we do. Same with alpha compositing. Maybe in the example, the transparency of one object could change? Or something animating (something changing inside it) is partially opaque, which would require render to texture.

SR: Should we compare against pixi?

JO: May be a good idea, but we should definitely create our own hand-coded version. Ideally, we would have our sample test that renders in (a) our hand-written webgl (b) in scenery and optionally (c) with pixi.

jonathanolson commented 9 years ago

Desired example features (togglable on/off):

samreid commented 9 years ago

I started writing an application skeleton to test the ideas above. I'm currently rendering small solid-fill moving triangles using a single drawArrays call every frame, and rates according to Mr. Doob's Stats (just time for the rendering). The canvas is not properly handling the devicePixelRatio on retina devices. I'll report some initial results from fa23d9e59d033c7d90809edb2a18f781e72c3d7c

# triangles FPS on iPad3/iOS8 FPS on Nexus 7
3000 60 60
10000 57 45
20000 30 28

It is important to note that there is a "warmup" time before this steady-state frame rate is achieved. For example, on the iPad3/iOS8, it takes about 6 seconds of stuttering, irregular (20-40fps) low performance before the smooth performance is achieved. It may be critical in our simulations to "warm up" the js engine + webgl pipeline before trying to animate anything visual in the sim.

Another critical thing to note is the canvas 2d performance, when just clearing the screen and drawing a moving rectangle (at steady state):

device FPS
iPad3/iOS8 34
Nexus 7 60

This simplified canvas2d test has very laggy/jerky behavior on the iPad3 and suggests that canvas2d is probably inappropriate to use on iPad3/iOS8 as a rendering strategy for our applications.

samreid commented 9 years ago

One other important note: when toggling the @mrdoob stats banner to read out milliseconds, the entire animation becomes much more jerky and sluggish. When reading out frames per second it doesn't seem to be slowing anything down. Here's a modified end function that will report draw times and ranges every second:

    end: function() {

      var time = Date.now();

      ms = time - startTime;
      msMin = Math.min( msMin, ms );
      msMax = Math.max( msMax, ms );

      frames++;

      if ( time > prevTime + 1000 ) {
        msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')';
        msMin = ms;
        msMax = ms;

        fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) );
        fpsMin = Math.min( fpsMin, fps );
        fpsMax = Math.max( fpsMax, fps );

        fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')';
        updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) );

        prevTime = time;
        frames = 0;

      }

      return time;

    }
samreid commented 9 years ago

Something else to note, in c48510dc19e1abc03a758061d86d57abe3ce8556 even though I am oscillating all of the triangles equally, there is some "tearing" or "lag" which I cannot explain.

samreid commented 9 years ago

Note that my work above uses disjoint triangles. But Marcelo Cantos recommends using triangle strip for this and discusses how to use triangle strip to show disjoint triangles: http://stackoverflow.com/questions/3677592/what-is-the-benefit-of-using-triangle-strip-and-triangle-fan-in-rendering

Triangle strips are extremely common, since they can represent surfaces more efficiently than triangle lists. Also, you can stitch together triangle strips that are visually separate so that entire disjoint geometries can be rendered as a single strip. You can do this by duplicating the last vertex of the first strip and the first vertex of the second strip, thus creating two degenerate triangles that bridge the gap but don't render.

samreid commented 9 years ago

Something odd: if I change TRIANGLES to TRIANGLE_STRIP with no other changes, the frame rate remains 60fps on iPad3 but drops to about 13fps on my macbook air and 11fps on Android. These is all for 15,000 vertices and one draw call per frame. Note: this is a lot of thin, tall triangles.

samreid commented 9 years ago

Our latest work on this can be viewed at http://localhost/scenery/tests/webgl/test-webgl-renderer.html

jonathanolson commented 9 years ago

If it uses the same vertex buffer, wouldn't the strip draw many more triangles?

samreid commented 9 years ago

If it uses the same vertex buffer, wouldn't the strip draw many more triangles?

Yes, but it is a question of how many edges are shared in our geometry. In our latest vision of webgl support where we are rendering all shapes in a single draw call, the number of shared edges would be very low.

jonathanolson commented 9 years ago

Ok, so to clarify, going from 6 vertices per rectangle to 4 vertices (and using TRIANGLE_STRIP) takes a performance hit?

If so, even though future shape rendering may have many edges duplicated, it sounds like TRIANGLES would be preferred).

samreid commented 9 years ago

Ok, so to clarify, going from 6 vertices per rectangle to 4 vertices (and using TRIANGLE_STRIP) takes a performance hit?

No, I am using the same number of vertices and rendering many more triangles when switching from TRIANGLES to TRIANGLE_STRIP. This was an isolated test with random geometry (hence the many tall, thin triangles, which I heard are harder for WebGL to optimize).

samreid commented 9 years ago

@jonathanolson has taken the lead on developing WebGL support for scenery, so I'm not sure this standalone module is still necessary. Up to @jonathanolson I think.