function drawCacheableRenderLayer (ctx, layer, customDrawFunc) {
  // See if there is a pre-drawn canvas in the pool.
  var backingStore = getBackingStore(layer.backingStoreId);
  var backingStoreScale = layer.scale || window.devicePixelRatio;
  var frameOffsetY = layer.frame.y;
  var frameOffsetX = layer.frame.x;
  var backingContext;

  if (!backingStore) {
    if (_backingStores.length >= Canvas.poolSize) {
      // Re-use the oldest backing store once we reach the pooling limit.
      backingStore = _backingStores[0].canvas;
      Canvas.call(backingStore, layer.frame.width, layer.frame.height, backingStoreScale);

      // Move the re-use canvas to the front of the queue.
      _backingStores[0].id = layer.backingStoreId;
      _backingStores[0].canvas = backingStore;
      _backingStores.push(_backingStores.shift());
    } else {
      // Create a new backing store, we haven't yet reached the pooling limit
      backingStore = new Canvas(layer.frame.width, layer.frame.height, backingStoreScale);
      _backingStores.push({
        id: layer.backingStoreId,
        layer: layer,
        canvas: backingStore
      });
    }

    // Draw into the backing <canvas> at (0, 0) - we will later use the
    // <canvas> to draw the layer as an image at the proper coordinates.
    backingContext = backingStore.getContext('2d');
    layer.translate(-frameOffsetX, -frameOffsetY);

    // Draw default properties, such as background color.
    backingContext.save();
    drawBaseRenderLayer(backingContext, layer);

    // Custom drawing operations
    customDrawFunc && customDrawFunc(backingContext, layer);
    backingContext.restore();

    // Draw child layers, sorted by their z-index.
    if (layer.children) {
      layer.children.slice().sort(sortByZIndexAscending).forEach(function (childLayer) {
        drawRenderLayer(backingContext, childLayer);
      });
    }

    // Restore layer's original frame.
    layer.translate(frameOffsetX, frameOffsetY);
  }

  // We have the pre-rendered canvas ready, draw it into the destination canvas.
  if (layer.clipRect) {
    // Fill the clipping rect in the destination canvas.
    var sx = (layer.clipRect.x - layer.frame.x) * backingStoreScale;
    var sy = (layer.clipRect.y - layer.frame.y) * backingStoreScale;
    var sw = layer.clipRect.width * backingStoreScale;
    var sh = layer.clipRect.height * backingStoreScale;
    var dx = layer.clipRect.x;
    var dy = layer.clipRect.y;
    var dw = layer.clipRect.width;
    var dh = layer.clipRect.height;

    // No-op for zero size rects. iOS / Safari will throw an exception.
    if (sw > 0 && sh > 0) {
      ctx.drawImage(backingStore.getRawCanvas(), sx, sy, sw, sh, dx, dy, dw, dh);
    }
  } else {
    // Fill the entire canvas
    ctx.drawImage(backingStore.getRawCanvas(), layer.frame.x, layer.frame.y, layer.frame.width, layer.frame.height);
  }
}
Draw a bitmap-cacheable layer into a pooled <canvas>. The result will be
drawn into the given context. This will populate the layer backing store
cache with the result.

@param {CanvasRenderingContext2d} ctx
@param {RenderLayer} layer
@param {Function} customDrawFunc
@private