CSC/ECE 517 Spring 2018- Project M1802: 2D Canvas Rendering (Part 2): Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 97: Line 97:


==Initial Steps==
==Initial Steps==
The initial steps were the first OSS project.
===Adding an Id to Canvas Communications===
===Adding an Id to Canvas Communications===
As shown below, we then associated a CanvasId with each variant of a canvas message; these messages are passed around various parts of the Servo implementation such as the 2D canvas struct, view layout, and various scripts. This required us to make other changed to allow a CanvasId to be sent with each message, as shown below.
As shown below, we then associated a CanvasId with each variant of a canvas message; these messages are passed around various parts of the Servo implementation such as the 2D canvas struct, view layout, and various scripts. This required us to make other changed to allow a CanvasId to be sent with each message, as shown below.
Line 132: Line 135:


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


==Subsequent Steps==
==Subsequent Steps==

Revision as of 01:15, 24 April 2018

“M1802: Simplify the 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 Theater 3000, Servo is an experimental web browser layout engine developed by Mozilla. It is currently 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. Here is a demo on how Servo performs against Firefox.

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. However, the <canvas> element has no drawing abilities of its own since it only acts as a container for graphics. A script to actually draw the graphics. By using scripts, the HTML <canvas> can:

  • Be animated
  • Be interactive
  • Draw text
  • Be used in games

An example of several canvases from servo's tests.

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 by Mozilla (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 our task was to add a few parts to code that already existed.

Implementation

General Information

Our repo is here.

Building

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


Testing

After successfully building servo, we proceeded to test the amount of time spent under “drawImage” as shown below. The function "drawImage" copies the contents of one canvas in a view to another canvas in the same view. During the "Initial Steps," this test was created to test the initial performance of Servo with regards to canvas rendering.

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.");

This was/will be used to measure the performance when drawing a single canvas.

Running Servo

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

Initial Steps

The initial steps were the first OSS project.

Adding an Id to Canvas Communications

As shown below, we then associated a CanvasId with each variant of a canvas message; these messages are passed around various parts of the Servo implementation such as the 2D canvas struct, view layout, and various scripts. This required us to make other changed to allow a CanvasId to be sent with each message, as shown below.

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 necessary changed to the constellation 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);


Performance

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

Subsequent Steps

Drawing Canvases with one CanvasPaintThread

As it stands, a CanvasPaintThread is made for every Canvas, which is very slow when there are many Canvases. To fix this, a single CanvasPaintThread will be made that does all the work for all the canvases. The data currently stored in CanvasPaintThread unique to each Canvas will be moved to a new data structure known as CanvasData.

Currently, communication channels are opened up between each CanvasPaintThread and what uses it and those channels send the CanvasIds established above alongside their messages. After completing the Subsequent Steps, these channels will instead connect the single CanvasPaintThread and those same users. The users will be unable to tell the difference between the current implementation and the new implementation. The CanvasPaintThread will redirect those existing channels into painting using the appropriate CanvasData structure.

The majority of changes will be entirely contained within components/canvas/canvas_paint_thread.rs. The changes to other files were largely done in the initial steps. The changes made to CanvasPaintThread should end up being invisible to its clients.

There will be slight changes to Constellation so that it doesn't try to create multiple CanvasPaintThreads, but those changes are minor and well described in the spec.

Conclusion

So far we have associated each variant of a canvas message with a particular canvas id. Even though there is not much improvement is the performance of http://slither.io/, we believe that an improvement can be noticed after going through the Subsequent Steps. Here is a link to our pull request. On 4/2/2018, our contribution was merged into Servo's master branch!

The Subsequent Steps should be able to be completed similarly. The instructions are similarly clear, and the team understands how to work on Servo much more clearly, so the difficulty of initially getting set up will not need to be done again.

References

  1. https://en.wikipedia.org/wiki/Servo_(layout_engine)
  2. https://www.rust-lang.org/en-US/
  3. https://github.com/servo/servo
  4. https://github.com/servo/servo/wiki/Canvas-rendering-project
  5. https://www.w3schools.com/graphics/canvas_intro.asp
  6. https://github.com/servo/servo/issues/10381
  7. https://github.com/servo/servo/issues/13879