CSC/ECE 517 Spring 2018- Project M1802: 2D Canvas Rendering
As robust and fast Servo is, Servo’s implementation of its HTML 2D/3D “<canvas>” has several inefficiencies that make some websites perform slowly or run out of memory when performing complex canvas operations. The goal of this project was to make these websites perform better when loaded in Servo.
Introduction
This section briefly describes the main parts of this project: Servo, Rust, and HTML Canvas.
Servo
Named after a robot from the show Mystery Science Threater 3000, Servo is an experimental web browser layout engine developed by Mozilla. It is currenly being developed for 64-bit OS X, 64-bit Linux, 64-bit Windows, and Android. While working with Samsung, Mozilla seeks to create a highly parallel environment where the many things in a web browser like rendering, image decoding, and HTML parsing are handled by isolated tasks.
Rust
Since existing languages like C++ did not provide a direct way to achieve a parallel environment, Mozilla developed and uses a programming language called Rust to further develop Servo.
HTML Canvas
A <canvas> in HTML is a container for graphics that is used to draw graphics “on the fly” by using Javascript. The HTML <canvas> can:
- Be animated
- Be interactive
- Draw text
- Be used in games
Project Description
The goal of this project was to remove the inefficiencies of Servo’s implemetation of the 2D/3D canvas. As reported with issue #10381, a gaming website called http://slither.io/ was performing extremely slowly as of Apr 3, 2016. As of Oct 21, 2016, it was reported with issue #13879 that http://slither.io/ was, in fact, crashing when loaded onto Servo. The suggestion to these issues was to, first, create a test case that measured the amount of time that Servo spends under “drawImage” while doing operations related to a canvas. Another suggestion was to change Servo’s implementation from using one thread per canvas to one thread for all canvases. The steps layed out for this project (both the initial setup steps and optimizing the canvas implementation) are shown below.
Initial Steps:
- create a testcase that contains two canvases and uses the drawImage API to draw the contents of one canvas onto the other. Programmatically measure the time this operation takes.
- To prepare for the big switch from 1 threads per canvas to 1 thread for all canvases, add a struct CanvasId(u64) type to components/canvas_traits/canvas.rs and add a CanvasId member to each variant of the CanvasMsg enum.
- add a CanvasId member to Constellation in components/constellation/constellation.rs which is initialized to 0 and increased by 1 each time handle_create_canvas_paint_thread is called.
- make the response_sender argument of handle_create_canvas_paint_thread also include the new CanvasId value, and pass it as an argument to CanvasPaintThread::start. Store the id when it is received for use in all canvas messages.
- For each CanvasMsg that is processed by CanvasPaintThread, verify that the id received matches the id that was provided to CanvasPaintThread::start
Subsequent Steps:
- extract the innards of CanvasPaintThread into a CanvasData structure,
- make CanvasPaintThread store a hashtable of CanvasId->CanvasData
- as part of Constellation::start, create a canvas paint thread and store the channel to communicate with it as a member of Constellation. Remove the initial canvas id from the API of Constellation::start.
- when handle_create_canvas_paint_thread is invoked, communicate with the canvas thread and have it create a new entry in the hashtable.
- when the canvas thread receives a message, perform the operation on the appropriate canvas according to the provided id
- optimize the DrawImageInOther operation by drawing on the destination canvas directly, rather than relying on sending a message to another canvas thread. Remove the now-unnecessary IpcSender from the DrawImageInOther enum variant. Verify that the earlier test case demonstrates a performance improvement.
- report on how slither.io performs in Servo after all these changes