CSC/ECE 517 Spring 2018- Project M1802: 2D Canvas Rendering

From Expertiza_Wiki
Revision as of 19:48, 26 March 2018 by Pmocher (talk | contribs)
Jump to navigation Jump to search

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 laid 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


Design Pattern

Design patterns were not applicable since out task was to add a few parts to code that already existed.


Implementation

After setting up the environment required to develop for Servo, we built and compiled Servo as per the instructions on Servo’s Github repo. We used Mozilla’s mach tools to build Servo with Cargo, which is the rust package manager.

git clone https://github.com/servo/servo
cd servo
./mach build --dev
./mach run tests/html/about-mozilla.html

After successfully building servo, we proceeded to test the amount of time spent under “drawImage” as shown below.

var t0 = performance.now();
drawImage(25, 25);
var t1 = performance.now();
document.getElementById('Test Result').innerHTML = "DrawImage took " + (t1 - t0) + " milliseconds.";
console.log("DrawImage took " + (t1 - t0) + " milliseconds.");

We then associated a CanvasId with each variant of a canvas message, as shown below. This required us to make other changed to allow a CanvasId to be sent with each message.

Before After
pub enum CanvasMsg {
   Canvas2d(Canvas2dMsg),
   FromLayout(FromLayoutMsg),
   FromScript(FromScriptMsg),
   Recreate(Size2D<i32>),
   Close(),

}

pub enum CanvasMsg {
   Canvas2d(Canvas2dMsg, CanvasId),
   FromLayout(FromLayoutMsg, CanvasId),
   FromScript(FromScriptMsg, CanvasId),
   Recreate(Size2D<i32>, CanvasId),
   Close(CanvasId),

}

Then, we made the necceasy changed to the contellation and the canvas paint thread to be able to initialize, increment, and store the CanvasId. TO finish up with the initial steps, we programmatically checked to make sure that each id that was processed by CanvasPaintThread was the same id that was provided when the thread was started (CanvasPaintThread::start).

assert!(canvas_id == painter.canvas_id);

After each successful build through our development process, we ran Servo as per the instructions on Servo’s Github repo.

./servo [url] [arguments] # if you run with nightly build
./mach run [url] [arguments] # if you run with mach

# For example
./mach run https://www.google.com

Almost every time, http://slither.io/ ended up crashing. Other times, it performed very slowly. However, much more is left to de done (The Subsequent Steps) as of 2/26/2018.

Conclusion

References