Press "Enter" to skip to content

JavaScript Frustum View Culling Performance

At work the other day a buddy (+Chris Lentini)¬†wrote a simple little JavaScript frustum culling program, checking 16384 spheres. On an aside, check out his very cool River Trail post based on this same starting code. Also I’d like to point out that this code was not meant to be elegant, or for high performance, just to get a basic idea for something he was working. Even with that, what peaked my interest though was the difference in timing between Chrome v13 and Firefox 6.0.2. My first guess was that the function calling was killing it and taking most of the time. After grabbing the code I decided to make a few test to check to see if it was the dot product function, garbage collection, or a combination of both.

First let’s look at the timing for the original code and timing results (all times are in ms):

[code language=”javascript”]
function Dot(pt0, pt1) {
return (pt0[0] * pt1[0]) + (pt0[1] * pt1[1]) + (pt0[2] * pt1[2]) + (pt0[3] + pt1[3]);
}

var numVis = 0;
for (var j=0; j numVis = 0;
for (var i=0; i if (Dot(frustum[0], spheres[i]) < 0.0)
continue;
if (Dot(frustum[1], spheres[i]) < 0.0)
continue;
if (Dot(frustum[2], spheres[i]) < 0.0)
continue;
if (Dot(frustum[3], spheres[i]) < 0.0)
continue;
if (Dot(frustum[4], spheres[i]) < 0.0)
continue;
if (Dot(frustum[5], spheres[i]) < 0.0)
continue;
numVis++;
}
}
[/code]

Now again, this was meant to be a simple test and is not production code, but it’s amazing that Chrome in this case is more than twice as slow as Firefox. If it is the function calling, let’s start by changing it up a bit and have it do the sign check for us.

[code language=”javascript”]
function sphereCheck(pt) {
for (var k = 0; k < 6; k++) {
var d = (frustum[k])[0] * pt[0] + (frustum[k])[1] * pt[1] + (frustum[k])[2] * pt[2] + (frustum[k])[3] + pt[3];
if(d < 0.0) {
return false;
}
}
return true;
}
[/code]

Ok, so rather than having multiple function calls, we’re down to just one, and we’re only taking in the sphere we’re checking against. On top of that, our return value is nice and simple. So, did it help? Let’s look at the data.

Well ¬†that certainly doesn’t look good for Firefox. You can’t tell in this graph, but Chrome improved to an average of 118 ms, a nice improvement, but again, what happened to Firefox? As a quick test, let’s move the the for loop in the sphereCheck into the main loop and remove that function call entirely to see if we gain anything from no function calls.

[code language=”javascript”]
var numVis = 0;
for (var j=0; j numVis = 0;
for (var i=0; i < arraysize; i++) {
var check = true;
for (var k = 0; k < 6; k++) {
if(check) {
var d = (frustum[k])[0] * (spheres[i])[0] + (frustum[k])[1] * (spheres[i])[1] + (frustum[k])[2] * (spheres[i])[2] + (frustum[k])[3] + (spheres[i])[3];
if(d > 0.0) {
numVis++;
check = false;
}
}
}
}
}
[/code]

Chrome improved once again now averaging to around 100ms, but Firefox is still worse than the original test code. It’s interesting to see the differences between the two browser, especially with respect to the second test. I’m going to spend a little more time with that test to find out why Firefox behaves so poorly and why Chrome improved so much. The next step for this will be to play with River Trail and Web Workers to see what other performance gains I can get and hopefully get both browsers running near the same speed. If you have another way of doing this, see a mistake, or would like to discuss the difference between the two browsers leave a comment or send me a message on G+. As usual, code is available on GitHub. Definite thanks to Chris for his help, and for giving me a starting point to this post.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *