Firefox 37的Canvas渲染性能是否有任何解决方案?

时间:2015-05-08 22:14:01

标签: performance firefox html5-canvas

我故意在问题中提供版本号,因为这是一个在某些时候会过时的问题。

这是一个简单的动画,可以在Chrome和Safari中非常流畅地运行,但在Firefox中会非常生涩:

function lerp(a,b,λ) {
    return a + λ*(b-a);
}

function random(a,b) {
    return lerp( a, b, Math.random() );
}

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 

$(document).ready( function() 
{  
    var canvas = document.getElementById("myCanvas");
    var ctx = canvas.getContext("2d");

    var balls = new Array(12);

    for( var i=0; i<balls.length; i++ ) 
    {
        while(true) 
        {
            var density = Math.sqrt( random(1,100) );
            var r = random(5, 30);

            var x = random( r+1, canvas.width-1  -(r+1) ),
                y = random( r+1, canvas.height-1 -(r+1) );

            var overlap = false;

            for( var j=0; j<i; j++ )
            {
                var _x = balls[j].x - x,
                    _y = balls[j].y - y,
                    d2 = _x*_x + _y*_y,
                    _r = balls[j].r + r;
                
                if( d2 < _r*_r )
                    overlap = true;
            }

            if( overlap )
                continue;
            
            balls[i] = {
                color   : d3.hsl( lerp(0,240,density/10), random(.3,.7), random(.3,.7) ).toString(), 
                x       : x,
                y       : y,
                vx      : random(0, 0.2),
                vy      : random(0, 0.2), 
                r       : r,

                advance: function(t) {
                    this.x += t * this.vx;
                    this.y += t * this.vy;
                }
            };
            
            break;
        }
    };

    window.requestAnimationFrame(vsync);
    
    var t_last;

    function vsync(t) 
    {
        if( t_last )
            render( t - t_last );
        
        t_last = t;
        window.requestAnimationFrame(vsync);
    }

    function render(t_frame)
    {
        canvas.width  = window.innerWidth;
        canvas.height = window.innerHeight;

        ctx.fillStyle="gray";
        ctx.fillRect(0,0, canvas.width, canvas.height);

        function advance_all(t) {
            balls.forEach( function(b) {
                b.advance(t);
            });
        }

        var t_remaining = t_frame;

        while(true) {
            var hit = get_next_collision( balls, canvas.width, canvas.height );
            
            if( t_remaining < hit.t )
                break;
            
            advance_all( hit.t );
            
            t_remaining -= hit.t;

            collide_wall( balls[hit.i], hit.wall );
            balls[hit.i].advance(.001);
        };

        advance_all( t_remaining );

        // draw balls
        balls.forEach( function(ball) {
            ctx.beginPath();
            ctx.arc(ball.x, ball.y, ball.r, 0, Math.PI*2, true);
            ctx.closePath();
            ctx.fillStyle = ball.color;
            ctx.fill();
        });
    }
});

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 

function get_next_collision(balls,W,H) 
{
    var winner;

    // ball-wall
    balls.forEach( function(ball,i) 
    {
        var t = [];
        
        t['L'] =     (ball.r - ball.x) / ball.vx; // s.x + t v.x = r
        t['T'] =     (ball.r - ball.y) / ball.vy;  
        t['R'] = (W-1-ball.r - ball.x) / ball.vx; // s.x + t v.x = (W-1)-r
        t['B'] = (H-1-ball.r - ball.y) / ball.vy; 

        // get index of smallest positive t
        var LR =  t['L'] >= 0  ?  'L' : 'R',
            TB =  t['T'] >= 0  ?  'T' : 'B',
            wall = t[LR] < t[TB] ? LR : TB; 
        
        if( ! winner || ( t[wall] <= winner.t ) )
            winner = { 
                t   : t[wall],
                i   : i, 
                wall: wall 
                };
    });
        
    return winner;
}


function collide_wall( A, wall ) 
{
    if( wall == 'L'  ||  wall == 'R' )
        A.vx *= -1;
    else
        A.vy *= -1;
}
html, body {
              width:  100%;
              height: 100%;
              margin: 0px;
            }
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
        <script src="http://cdnjs.cloudflare.com/ajax/libs/mathjs/1.5.1/math.min.js"></script>
        <script src="http://d3js.org/d3.v3.min.js"></script>

        <canvas id="myCanvas">
            <!-- Insert fallback content here -->
        </canvas>

为什么Firefox的表现明显比竞争对手差?

如果我把球的数量减少到500,Chrome仍然很流畅,Firefox会非常不稳定。

如果我把球的数量减少到1,那么Firefox仍然会消耗它。

我做的另一个实验是每帧应用固定的速度增量,这样动画的平滑度就能准确地反映渲染回调的均匀度。这表明Firefox到处都是。另一方面,Chrome很流畅。

如果有足够的兴趣,我也可以提供一个片段,也许整理第一个,以便它提供一个滑块来修改球数。

据我所知,(1)Firefox肯定没有给我们一个真正的VSYNC回调,我怀疑它只是使用一个计时器,(2)甚至通过手动计算每帧的经过时间进行校正,它会刺激动画,也许暗示它有时候回调会及时触发VSYNC并且有时会错过船只。(3)与Chrome相比,还有一个额外的合成命中率不成比例。

有什么事要做吗?

编辑:我在四年前发现了这个问题! Poor performance of html5 canvas under firefox - 请不要将此问题标记为副本,除非确定该问题的答案仍然是四年后唯一相关的答案。我故意在问题中包含版本号,因为问题与当前版本有关。

0 个答案:

没有答案