Skip to content

Instantly share code, notes, and snippets.

@zz85
Last active February 6, 2016 14:40
Show Gist options
  • Save zz85/606666529d38370ccee9 to your computer and use it in GitHub Desktop.
Save zz85/606666529d38370ccee9 to your computer and use it in GitHub Desktop.
Drawing / Painting as Many Rects as Possible (Still WIP, feel free to comments or contribute!)

Moved here

Fastest Rectangles

Some thoughts:

  • How many ways can you draw rectangles in a browser (eg. dom, css, 2d canvas, svg, webgl)
  • How is the fastest (most number of) rectangles can you draw with each approach?

Task:

  • Paint random rectangles for 5 seconds, find out how fast each approach takes.

Rational:

  • For various reasons, you may want to find the most convinent approach to draw rectangles. In Space Radar, I find myself having > 100K rectanges to paint at times, which is why I'm exploring and testing which ways rectangles can be painted.

Tests Cases:

  • DOM, CSS, Canvas, or WebGL (three.js, stackgl or other libraries) may be used
  • Fill them
  • Stroke them
  • Rectangle should be updatable by JS
  • Rectangles are defined by (x, y, w, h)
  • Canvas dimension is 1024x768.
  • Random rects dimensions is width/4, height/4
  • They are in units as fraction of the screen size (0..1)
  • Each draw frame should be within 16.67s (60fps)
  • Goal is to see compare the different approaches

Current Results

These numbers are estimated number of triangles that can be drawn in a single 60fps frame (which has only 16.67ms rendering time). As with all benchmarks, these numbers should be taken with a grain of salt. There are many factors depending on different scenarios so milleage always vary.

  1. Headless - (overheads of Math.random) 76K (boilerplate.html)
  2. Canvas - 2.7K rectangles (canvas.html)
  3. PixiJS - 3.1K Canvas Renderer, 2.3K WebGL Renderer (pixi.html)
  4. PixiJS - Single graphics. 1.8K WebGL, 2.4K Canvas (pixi-single.html)
  5. Three.js (Rect as mesh) (three-simple.html)
  • each mesh as individual geometry (WebGl Renderer)
    • no shared geometry & no shared material - 1K
      • shared geometry - 1.8K
      • shared material - 3.7K (1.1K for canvas renderer)
      • Add rect strokes (fill only, no lines) - 1.7K
      • Almost similar using buffergeometry + line buffers (1.7K)
  1. Single Buffer Geometry (three-buffer.html)

    • 5K
  2. Instance Geometry (three-instance.html)

    • 20K rectangles (Fill + Stroke)

To come

Others

  • Indexed / Interved Buffers?
  • Using point sprites
<html>
<body>
<script src="boilerplate.js"></script>
<script>
// Experiment 0. Boilerplate, headless rendering
var BATCH = 100000;
function draw() {
rects += BATCH;
var haha = 0;
for (var i = 0; i < BATCH; i++) {
var x = Math.random() * WIDTH;
var y = Math.random() * HEIGHT;
var w = Math.random() * WIDTH / 4;
var h = Math.random() * HEIGHT / 4;
haha += x + y - w - h; // just so JS doesn't skip this unused block smartly
}
}
setTimeout(start, 500);
</script>
</body>
</html>
/* Globals */
var WIDTH = 1024;
var HEIGHT = 768;
var timeStart;
var total = 0;
var rafs = 0;
var rects = 0;
/* Implement your draw function !! */
function run() {
rafs++
var then = performance.now();
draw();
var now = performance.now();
var lapsed = now - timeStart;
total += now - then;
if (lapsed < 5000) {
requestAnimationFrame(run);
} else {
console.timeEnd('run');
console.log('Number of Rafs', rafs);
console.log('Time per Rafs', (total / rafs).toFixed(2), 'ms');
console.log('Total Rects', rects);
console.log('Rects / second', (rects / total * 1000).toFixed(2));
console.log('Rects / 16.67ms', (rects * 1000 / total / 60).toFixed(2));
console.log('Done!!');
}
}
function start() {
console.log('Test start!');
timeStart = performance.now();
console.time('run');
run();
}
<html>
<body>
<script src="boilerplate.js"></script>
<script>
// Experiment 1. Using Canvas 2D
var canvas = document.createElement('canvas');
canvas.width = 1024; // window.innerWidth;
canvas.height = 768; // window.innerHeight;
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';
for (var i = 0; i < 2500; i++) {
ctx.beginPath()
var x = Math.random() * canvas.width;
var y = Math.random() * canvas.height;
var w = Math.random() * canvas.width / 4;
var h = Math.random() * canvas.height / 4;
ctx.rect(x, y, w, h);
ctx.stroke();
ctx.fill();
rects++;
}
}
setTimeout(start, 500);
</script>
</body>
</html>
<html>
<body>
<script src="../node_modules/pixi.js/bin/pixi.js"></script>
<script src="boilerplate.js"></script>
<script>
// Experiment 2b. Using pixi library with WebGL + single sprite
var renderer = new PIXI.CanvasRenderer(WIDTH, HEIGHT);
// var renderer = new PIXI.WebGLRenderer(WIDTH, HEIGHT);
document.body.appendChild(renderer.view);
var stage = new PIXI.Container();
var BATCH = 2000;
var sprites = [];
var graphics = new PIXI.Graphics();
stage.addChild(graphics);
function draw() {
rects += BATCH;
graphics.clear();
graphics.beginFill(0xffeeff);
graphics.lineStyle(1, 0x000000, 1);
for (var i = 0; i < BATCH; i++) {
var x = Math.random() * WIDTH;
var y = Math.random() * HEIGHT;
var w = Math.random() * WIDTH / 4;
var h = Math.random() * HEIGHT / 4;
graphics.drawRect(x, y, w, h);
// This is slower
// graphics.moveTo(x, y);
// graphics.lineTo(x + w, y);
// graphics.lineTo(x + w, y + h);
// graphics.lineTo(x, y + h);
// graphics.lineTo(x, y);
// graphics.endFill();
}
renderer.render(stage);
}
setTimeout(start, 500);
</script>
</body>
</html>
<html>
<body>
<script src="../node_modules/pixi.js/bin/pixi.js"></script>
<script src="boilerplate.js"></script>
<script>
// Experiment 2. Using pixi library
var renderer = new PIXI.CanvasRenderer(WIDTH, HEIGHT);
// var renderer = new PIXI.WebGLRenderer(WIDTH, HEIGHT);
document.body.appendChild(renderer.view);
var stage = new PIXI.Container();
var BATCH = 2700;
var sprites = [];
for (var i = 0; i < BATCH; i++) {
var x = Math.random() * WIDTH;
var y = Math.random() * HEIGHT;
var w = Math.random() * WIDTH / 4;
var h = Math.random() * HEIGHT / 4;
// Not sure how to use this...
// var graphics = new PIXI.Rectangle(x, y, w, h)
/**/
var graphics = new PIXI.Graphics();
graphics.beginFill(0xffeeff);
graphics.lineStyle(1, 0x000000, 1);
// draw a rectangle
graphics.drawRect(x, y, w, h);
// This is slower
// graphics.moveTo(x, y);
// graphics.lineTo(x + w, y);
// graphics.lineTo(x + w, y + h);
// graphics.lineTo(x, y + h);
// graphics.lineTo(x, y);
// graphics.endFill();
stage.addChild(graphics);
sprites.push(graphics);
}
function draw() {
rects += BATCH;
for (var i = 0; i < BATCH; i++) {
var x = Math.random() * WIDTH;
var y = Math.random() * HEIGHT;
var w = Math.random() * WIDTH / 4;
var h = Math.random() * HEIGHT / 4;
// sprites[i].drawRect(x, y, w, h);
// sprites[i].scale.x = Math.random();
// sprites[i].scale.y = Math.random();
var sprite = sprites[i];
sprite.position.x = x;
sprite.position.y = y;
sprite.width = w;
sprite.height = h;
}
renderer.render(stage);
}
setTimeout(start, 500);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - camera - orthographic</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="/gits/three.js/build/three.min.js"></script>
<script src="boilerplate.js"></script>
<script>
var camera, scene, renderer;
var RECTS = 5000;
var positions, lineBuffer;
function setRect(x, y, w, h, index, buffer, lines) {
// a.setXYZ(0, -0.5, 0.5, 0); // a
// a.setXYZ(1, 0.5, 0.5, 0); // b
// a.setXYZ(2, 0.5, -0.5, 0); // d
// a.setXYZ(3, -0.5, -0.5, 0); // c
var i = index * 6;
var ax = -0.5 + x;
var ay = -0.5 + y;
var bx = 0.5 + x + w;
var by = -0.5 + y;
var dx = 0.5 + x + w;
var dy = 0.5 + y + h;
var cx = -0.5 + x;
var cy = 0.5 + y + h;
var z = 0 + index * 0.01;
buffer.setXYZ(i + 0, ax, ay, z); // a
buffer.setXYZ(i + 1, bx, by, z); // b
buffer.setXYZ(i + 2, dx, dy, z); // d
buffer.setXYZ(i + 3, dx, dy, z); // d
buffer.setXYZ(i + 4, cx, cy, z); // c
buffer.setXYZ(i + 5, ax, ay, z); // a
i = index * 8;
z = 0.001 + index * 0.01;
lines.setXYZ(i + 0, ax, ay, z); // a
lines.setXYZ(i + 1, bx, by, z); // b
lines.setXYZ(i + 2, bx, by, z); // b
lines.setXYZ(i + 3, dx, dy, z); // d
lines.setXYZ(i + 4, dx, dy, z); // d
lines.setXYZ(i + 5, cx, cy, z); // c
lines.setXYZ(i + 6, cx, cy, z); // c
lines.setXYZ(i + 7, ax, ay, z); // a
}
function init() {
camera = new THREE.OrthographicCamera( WIDTH / - 2, WIDTH / 2, HEIGHT / 2, HEIGHT / - 2, - 500, 1000 );
// camera.position.x = 0;
// camera.position.y = 100;
camera.position.z = 100;
scene = new THREE.Scene();
lineBuffer = new THREE.BufferAttribute( new Float32Array( RECTS * 8 * 3 ), 3 );
positions = new THREE.BufferAttribute( new Float32Array( RECTS * 6 * 3 ), 3 )
var rectLineGeometry = new THREE.BufferGeometry();
rectLineGeometry.addAttribute( 'position', lineBuffer, 3 );
// var colors = new Float32Array( 5 * 3 ).fill(0);
// rectLineGeometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) );
// rectLineGeometry.computeBoundingSphere();
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', positions);
var lineMaterial = new THREE.
LineBasicMaterial
( {
// vertexColors: THREE.VertexColors,
color: 0x000000,
// opacity: 0.2
} );
var lines = new THREE.LineSegments( rectLineGeometry, lineMaterial );
scene.add(lines);
var material;
var color = new THREE.Color(1, 1, 1);
material = new THREE.MeshBasicMaterial({
color: color,
side: THREE.DoubleSide,
wireframe: !true
})
var rectMesh = new THREE.Mesh(geometry, material);
for ( var i = 0; i < RECTS; i ++ ) {
var x, y, w, h;
x = Math.random() * WIDTH - WIDTH / 2;
y = Math.random() * HEIGHT - HEIGHT / 2;
w = Math.random() * WIDTH / 4;
h = Math.random() * HEIGHT / 4;
setRect(x, y, w, h, i, positions, lineBuffer);
}
scene.add( rectMesh );
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
// renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( WIDTH, HEIGHT );
document.body.appendChild( renderer.domElement );
//
// window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.left = WIDTH / - 2;
camera.right = WIDTH / 2;
camera.top = HEIGHT / 2;
camera.bottom = HEIGHT / - 2;
camera.updateProjectionMatrix();
renderer.setSize( WIDTH, HEIGHT );
}
//
function draw() {
camera.lookAt( scene.position );
for ( var i = 0; i < RECTS; i ++ ) {
var x, y, w, h;
x = Math.random() * WIDTH - WIDTH / 2;
y = Math.random() * HEIGHT - HEIGHT / 2;
w = Math.random() * WIDTH / 4;
h = Math.random() * HEIGHT / 4;
setRect(x, y, w, h, i, positions, lineBuffer);
}
positions.needsUpdate = true;
lineBuffer.needsUpdate = true;
renderer.render( scene, camera );
rects += RECTS;
}
init();
setTimeout(start, 500);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - instanced particles - billboards - colors</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
color: #ffffff;
font-family: Monospace;
font-size: 13px;
text-align: center;
font-weight: bold;
background-color: #000000;
margin: 0px;
overflow: hidden;
}
#info {
position: absolute;
top: 0px;
width: 100%;
padding: 5px;
}
a {
color: #ffffff;
}
#oldie a {
color: #da0;
}
#notSupported {
width: 50%;
margin: auto;
border: 2px red solid;
margin-top: 20px;
padding: 10px;
}
</style>
</head>
<body>
<div id="info">
<div id="notSupported" style="display:none">Sorry your graphics card + browser does not support hardware instancing</div>
</div>
<script src="boilerplate.js"></script>
<script src="/gits/three.js/build/three.min.js"></script>
<script src="/gits/three.js/examples/js/Detector.js"></script>
<script id="vshader" type="x-shader/x-vertex">
precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec2 uv;
attribute vec3 normal;
attribute vec3 translate;
attribute vec2 scale;
attribute vec3 color;
varying vec3 vColor;
void main() {
vec3 p = position;
// p.xy *= scale.xy;
p.x *= scale.x;
p.y *= scale.y;
vec4 mvPosition = modelViewMatrix * vec4( p + translate, 1.0 );
// mvPosition.xyz += p;
// * scale
vColor = color;
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script id="fshader" type="x-shader/x-fragment">
precision highp float;
varying vec3 vColor;
void main() {
// vec4 diffuseColor = texture2D( map, vUv );
// vec3 vColor = vec3(0.4, 0.5, 0.4);
// vec4 diffuseColor = vec4( 0.8, .8, .4, 1.);
gl_FragColor = vec4( vColor, 1. );
// if ( diffuseColor.w < 0.5 ) discard;
}
</script>
<script>
var container;
var camera, scene, renderer;
var geometry, material, mesh;
var particleCount = 20000;
var translateArray = new Float32Array( particleCount * 3 );
var scaleArray = new Float32Array( particleCount * 2 );
var colorsArray = new Float32Array( particleCount * 3 );
function init() {
if ( !Detector.webgl ) {
Detector.addGetWebGLMessage();
return false;
}
renderer = new THREE.WebGLRenderer();
if ( renderer.extensions.get( 'ANGLE_instanced_arrays' ) === false ) {
document.getElementById( "notSupported" ).style.display = "";
return false;
}
container = document.createElement( 'div' );
document.body.appendChild( container );
var color = new THREE.Color( 0xffffff );
camera = new THREE.OrthographicCamera( WIDTH / - 2, WIDTH / 2, HEIGHT / 2, HEIGHT / - 2, - 500, 1000 );
camera.position.z = 100;
scene = new THREE.Scene();
geometry = new THREE.InstancedBufferGeometry();
geometry.copy( new THREE.PlaneBufferGeometry( 1, 1, 1, 1 ) );
lineGeometry = new THREE.InstancedBufferGeometry();
lineBuffer = new THREE.BufferAttribute( new Float32Array( 8 * 3 ), 3 );
function setRect(lines) {
var ax = -0.5;
var ay = -0.5;
var bx = 0.5;
var by = -0.5;
var dx = 0.5;
var dy = 0.5;
var cx = -0.5;
var cy = 0.5;
var z = 0;
lines.setXYZ(0, ax, ay, z); // a
lines.setXYZ(1, bx, by, z); // b
lines.setXYZ(2, bx, by, z); // b
lines.setXYZ(3, dx, dy, z); // d
lines.setXYZ(4, dx, dy, z); // d
lines.setXYZ(5, cx, cy, z); // c
lines.setXYZ(6, cx, cy, z); // c
lines.setXYZ(7, ax, ay, z); // a
}
setRect(lineBuffer);
lineGeometry.addAttribute( 'position', lineBuffer );
translateArray = new Float32Array( particleCount * 3 );
scaleArray = new Float32Array( particleCount * 2 );
colorsArray = new Float32Array( particleCount * 3 );
var x, y, w, h;
for ( var i = 0, i3 = 0, l = particleCount; i < l; i ++, i3 += 3 ) {
x = Math.random() * WIDTH - WIDTH / 2;
y = Math.random() * HEIGHT - HEIGHT / 2;
translateArray[ i3 + 0 ] = x;
translateArray[ i3 + 1 ] = y;
translateArray[ i3 + 2 ] = i * 0.1;
}
for ( var i = 0, i2 = 0, l = particleCount; i < l; i ++, i2 += 2 ) {
w = Math.random() * WIDTH / 4;
h = Math.random() * HEIGHT / 4;
scaleArray[ i2 + 0 ] = w;
scaleArray[ i2 + 1 ] = h;
}
for ( var i = 0, i3 = 0, l = particleCount; i < l; i ++, i3 += 3 ) {
// color.setHSL( Math.random(), 1 - i / l * 0.3, 0.5 );
colorsArray[ i3 + 0 ] = color.r;
colorsArray[ i3 + 1 ] = color.g;
colorsArray[ i3 + 2 ] = color.b;
}
geometry.addAttribute( "translate", new THREE.InstancedBufferAttribute( translateArray, 3, 1 ).setDynamic( true ) );
geometry.addAttribute( "scale", new THREE.InstancedBufferAttribute( scaleArray, 2, 1 ).setDynamic( true ) );
geometry.addAttribute( "color", new THREE.InstancedBufferAttribute( colorsArray, 3, 1 ).setDynamic( true ) );
// Line Geometry
lineColors = new Float32Array( particleCount * 3 ).fill(0);
lineGeometry.addAttribute( "translate", new THREE.InstancedBufferAttribute( translateArray, 3, 1 ).setDynamic( true ) );
lineGeometry.addAttribute( "scale", new THREE.InstancedBufferAttribute( scaleArray, 2, 1 ).setDynamic( true ) );
lineGeometry.addAttribute( "color", new THREE.InstancedBufferAttribute( lineColors, 3, 1 ).setDynamic( true ) );
material = new THREE.RawShaderMaterial( {
uniforms: {
// map: { type: "t", value: THREE.ImageUtils.loadTexture( "textures/sprites/circle.png" ) }
},
vertexShader: document.getElementById( 'vshader' ).textContent,
fragmentShader: document.getElementById( 'fshader' ).textContent,
depthTest: true,
depthWrite: true
} );
mesh = new THREE.Mesh( geometry, material );
lineMesh = new THREE.LineSegments( lineGeometry, material );
lineMesh.position.z = 0.0001;
mesh.scale.multiplyScalar(1);
scene.add( mesh );
scene.add( lineMesh )
// renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( WIDTH, HEIGHT );
container.appendChild( renderer.domElement );
//
// window.addEventListener( 'resize', onWindowResize, false );
return true;
}
function onWindowResize( event ) {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function draw() {
var time = performance.now() * 0.0005;
camera.lookAt( scene.position );
var translates = geometry.getAttribute( 'translate' );
var scales = geometry.getAttribute( 'scale' );
var colors = geometry.getAttribute( 'color' );
for ( var i = 0, i2 = 0, i3 = 0, l = particleCount;
i < l;
i ++, i2 += 2, i3 += 3 ) {
var x = Math.random() * WIDTH - WIDTH / 2;
var y = Math.random() * HEIGHT - HEIGHT / 2;
var w = Math.random() * WIDTH / 4;
var h = Math.random() * HEIGHT / 4;
translateArray[ i3 + 0 ] = x;
translateArray[ i3 + 1 ] = y;
translateArray[ i3 + 2 ] = -i * 0.01;
scaleArray[ i2 + 0 ] = w;
scaleArray[ i2 + 1 ] = h;
}
renderer.setClearColor( 0xf0f0f0 );
translates.needsUpdate = true;
scales.needsUpdate = true;
// colors.needsUpdate = true;
lineGeometry.getAttribute( 'translate' ).needsUpdate = true;
lineGeometry.getAttribute( 'scale' ).needsUpdate = true;
// renderer.clear();
renderer.render( scene, camera );
rects += particleCount;
}
init();
setTimeout(start, 500);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - camera - orthographic</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="/gits/three.js/build/three.min.js"></script>
<script src="/gits/three.js/examples/js/renderers/Projector.js"></script>
<script src="/gits/three.js/examples/js/renderers/CanvasRenderer.js"></script>
<script src="boilerplate.js"></script>
<script>
var camera, scene, renderer;
var RECTS = 5000;
function init() {
camera = new THREE.OrthographicCamera( WIDTH / - 2, WIDTH / 2, HEIGHT / 2, HEIGHT / - 2, - 500, 1000 );
camera.position.x = 0;
// camera.position.y = 100;
camera.position.z = 100;
scene = new THREE.Scene();
// Grid
var size = 500, step = 50;
var rectLineGeometry = new THREE.Geometry();
var geometry = new THREE.PlaneGeometry(1, 1, 1, 1);
rectLineGeometry.vertices[0] = geometry.vertices[0];
rectLineGeometry.vertices[1] = geometry.vertices[1];
rectLineGeometry.vertices[2] = geometry.vertices[3];
rectLineGeometry.vertices[3] = geometry.vertices[2];
rectLineGeometry.vertices[4] = geometry.vertices[0];
var lineMaterial = new THREE.LineBasicMaterial( { color: 0x000000, opacity: 0.2 } );
var material = new THREE.MeshBasicMaterial( { color: 0x330033, shading: THREE.FlatShading, overdraw: 0.5 } );
var geometry = new THREE.PlaneGeometry(1, 1, 1, 1);
var color = new THREE.Color(1, 1, 1);
material = new THREE.MeshBasicMaterial({
color: color,
side: THREE.DoubleSide,
wireframe: !true
})
function RectangleMesh() {
THREE.Mesh.call(this, geometry, material);
var line = new THREE.Line( rectLineGeometry, lineMaterial );
line.position.z = 0.0001;
this.add( line );
}
RectangleMesh.prototype = Object.create(THREE.Mesh.prototype);
RectangleMesh.prototype.setRect = function(x, y, w, h) {
this.position.x = x + w * .5;
this.position.y = y + h * .5;
this.scale.x = w;
this.scale.y = h;
}
for ( var i = 0; i < RECTS; i ++ ) {
var rect = new RectangleMesh( material );
rect.position.z = 0.01 * i;
scene.add( rect );
}
// renderer = new THREE.CanvasRenderer();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
// renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( WIDTH, HEIGHT );
document.body.appendChild( renderer.domElement );
//
// window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.left = WIDTH / - 2;
camera.right = WIDTH / 2;
camera.top = HEIGHT / 2;
camera.bottom = HEIGHT / - 2;
camera.updateProjectionMatrix();
renderer.setSize( WIDTH, HEIGHT );
}
//
function draw() {
for ( var i = 0; i < RECTS; i ++ ) {
var rect = scene.children[i];
var x, y, w, h;
x = Math.random() * WIDTH - WIDTH / 2;
y = Math.random() * HEIGHT - HEIGHT / 2;
w = Math.random() * WIDTH / 4;
h = Math.random() * HEIGHT / 4;
rect.setRect(x, y, w, h);
}
camera.lookAt( scene.position );
renderer.render( scene, camera );
rects += RECTS;
}
init();
setTimeout(start, 500);
</script>
</body>
</html>
@zz85
Copy link
Author

zz85 commented Jan 17, 2016

@vorg I'm current on a 2013 MacBook Air with specs running it on Chrome.

image

The "test suite" kinda works like this. It runs using a sequence of requestAnimationFrames until 5 seconds is up. It then takes the total number of rectangles drawn (specified with rects += BATCH) over the rendering time the RAFs has happened. I typically tune the number so the batch number would run within 16.67ms, (which is also estimated in one of the console.logs). I've also made a calculate mistakes getting the optimal frames in a RAF, so I'm running the tests and updating the numbers again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment