Reputation: 14563
I'm working on a graphics frontend that renders server-side and pushes screen updates to a browser by sending compressed images to the client (think VNC). I've decided the overhead of encoding PNGs is too high, so currently I'm sending raw blobs of 8-bit RGB pixel values through a websocket (with compression enabled). This is actually very fast and I'm seeing great compression gains (75K -> 2.7k for example).
On the client however, I have to take the raw pixels and then draw them onto a canvas. This is my current best code performance wise:
// receive message from server
self.ws.onmessage = function (evt) {
// get image offset
var dv = new DataView(evt.data);
var dx = dv.getInt16(0);
var dy = dv.getInt16(2);
var ww = dv.getInt16(4);
var hh = dv.getInt16(6);
var offset = 8;
// get context to canvas and create image
var ctx = self.canvas.getContext("2d");
var img = ctx.createImageData(ww, hh);
// unpack image data
var start = performance.now();
var dst = 0, src = offset;
for (var ii=0; ii < ww*hh; ii++) {
img.data[dst++] = dv.getUint8(src++);
img.data[dst++] = dv.getUint8(src++);
img.data[dst++] = dv.getUint8(src++);
img.data[dst++] = 255;
}
// draw image
ctx.putImageData(img, dx, dy, 0, 0, ww, hh);
var end = performance.now();
console.log("draw took " + (end-start) + " milliseconds");
The aforementioned 75K image (which is 1000x500 pixels) takes ~53ms to render in this way though, which is a long time. What's the fastest way to do this drawing operation? I can output RGBA pixels instead of that makes life easier.
Edit: Seems like this might be more of a Firefox issue, Chrome runs this same decode loop in ~2.5ms on average.
Upvotes: 6
Views: 1440
Reputation: 14563
Switching to full RGBA output (which doesn't add much overhead thanks to the compression), and using this code to directly wrap the websocket buffer is significantly faster:
// receive message from server
self.ws.onmessage = function (evt) {
// get image offset
var dv = new DataView(evt.data);
var dx = dv.getInt16(0);
var dy = dv.getInt16(2);
var ww = dv.getInt16(4);
var hh = dv.getInt16(6);
// get context to canvas and create image
var ctx = self.canvas.getContext("2d");
// draw image data
var start = performance.now();
ctx.putImageData(
new ImageData(new Uint8ClampedArray(evt.data, 8), ww, hh),
dx, dy,
0, 0,
ww, hh
);
var end = performance.now();
console.log("draw took " + (end-start) + " milliseconds");
}
Now the large image size takes ~1ms to render in Firefox and 350us in Chrome. Going to call that good enough.
Upvotes: 2