Render vs prerender with sugar

Benchmark created by Eris on


Description

This is a response of sorts to Simon Sarris: Increasing Performance by Caching Paths on HTML5 Canvas.

PaintCache code was written in CoffeeScript and performance could probably be improved by rewriting and hand-optimising it. It would also look considerably less ugly than the compiler output, I'm sure.

Preparation HTML

<canvas id="display" width="100" height="100">
  Shit, son. This test case needs canvas, but your browser
  <em>
    can't
  </em>
  vas.
</canvas>
<script>
  var PaintCache;
  var __hasProp = Object.prototype.hasOwnProperty,
      __extends = function(child, parent) {
      for (var key in parent) {
        if (__hasProp.call(parent, key)) child[key] = parent[key];
      }
  
      function ctor() {
        this.constructor = child;
      }
      ctor.prototype = parent.prototype;
      child.prototype = new ctor;
      child.__super__ = parent.prototype;
      return child;
      };
  PaintCache = (function() {
    function PaintCache(context) {
      this.context = context;
      this.layerCache = {};
    }
    PaintCache.prototype.paintLayer = function(name, options, fn) {
      var defaults, layer, layerCanvas;
      if (arguments.length === 2) {
        fn = options;
        options = {};
      }
      defaults = {
        x: 0,
        y: 0,
        width: this.context.canvas.width,
        height: this.context.canvas.height
      };
      options = __extends(defaults, options != null ? options : {});
      if (name in this.layerCache) {
        layer = this.layerCache[name];
      } else {
        layerCanvas = document.createElement('canvas');
        layerCanvas.width = options.width;
        layerCanvas.height = options.height;
        this.layerCache[name] = layer = {
          valid: false,
          canvas: layerCanvas,
          context: layerCanvas.getContext('2d')
        };
      }
      if (!layer.valid) {
        layer.context.clearRect(options.x, options.y, options.width, options.height);
        fn(layer.context);
      }
      this.context.drawImage(layer.canvas, options.x, options.y);
      return layer.valid = true;
    };
    PaintCache.prototype.invalidateLayer = function(name) {
      if (name in this.layerCache) {
        return this.layerCache[name].valid = false;
      }
    };
    return PaintCache;
  })();
</script>

Setup

var context = document.getElementById('display').getContext('2d');
    var cache = new PaintCache(context);
    var prerendered;
    
    (function() {
      var context;
    
      prerendered = document.createElement('canvas');
      prerendered.width = 100;
      prerendered.height = 100;
    
      context = prerendered.getContext('2d');
      context.beginPath();
      context.strokeStyle = 'red';
      context.lineWidth = 4;
      context.moveTo(10, 10);
      context.lineTo(10, 30);
      context.lineTo(30, 30);
      context.lineTo(40, 70);
      context.quadraticCurveTo(72, 43, 22, 12);
      context.quadraticCurveTo(12, 43, 12, 102);
      context.stroke();
    })()

Test runner

Ready to run.

Testing in
TestOps/sec
regular ordinary canvas drawing
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.beginPath();
context.strokeStyle = 'red';
context.lineWidth = 4;
context.moveTo(10, 10);
context.lineTo(10, 30);
context.lineTo(30, 30);
context.lineTo(40, 70);
context.quadraticCurveTo(72, 43, 22, 12);
context.quadraticCurveTo(12, 43, 12, 102);
context.stroke();
ready
cached drawing WITHOUT sugar
context.drawImage(prerendered, 0, 0);
ready
cached drawing WITH sugar
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
cache.paintLayer('back', function(layer) {
  layer.beginPath();
  layer.strokeStyle = 'red';
  layer.lineWidth = 4;
  layer.moveTo(10, 10);
  layer.lineTo(10, 30);
  layer.lineTo(30, 30);
  layer.lineTo(40, 70);
  layer.quadraticCurveTo(72, 43, 22, 12);
  layer.quadraticCurveTo(12, 43, 12, 102);
  layer.stroke();
});
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.