Platform/GFX/Quantum Render
Contents
What is it
The goal of the Quantum Render project is to take the WebRender compositor in Servo and embed it in Firefox. It will replace Gecko's existing compositor, interfacing with Gecko's main-thread layout code. As WebRender is written in Rust and uses a very different design approach, we expect to get stability and performance benefits from this switch.
Planning
WebRender roadmap and more detailed quarterly plans: WebRender Planning
(You'll also find links to various tracking metabugs here)
Vital stats
Canonical code repository: https://hg.mozilla.org/mozilla-central
Bugzilla Component: Core :: Graphics: WebRender
Triage info: [[1]]
Mailing list: dev-tech-gfx@lists.mozilla.org
IRC channel: #gfx
Project owner: Maire Reavy
Where is it on: Platform/GFX/WebRender_Where
Getting the code
The work for the Quantum Render project is in mozilla-central and uses the normal gecko development workflow.
Build instructions
Building is the same as building a regular mozilla-central build. However, WebRender may be disabled by default at runtime if you are running on hardware that we have not whitelisted. You can enable it one of three ways:
- Manually set the gfx.webrender.enabled or gfx.webrender.all pref to true. You can do this from about:config, and it requires restarting the browser to take effect.
- Run firefox with MOZ_WEBRENDER=1 as an environment variable. This will attempt to enable WebRender at runtime.
Note: WebRender may still be disabled by other runtime conditions. Common conditions include a disabled GPU process (on Windows) or disabled hardware acceleration. You can check the WebRender status by going to about:support and looking at the WebRender line in the graphics section. On Linux hardware acceleration is disabled by default, so set layers.acceleration.force-enabled to true in about:config (restart required), or run with MOZ_ACCELERATED=1 in the environment to ensure HWA is enabled and doesn't block WebRender.
Contribution workflow
You can hack on WebRender either via a checkout of mozilla-central or via a checkout of the webrender repository. Using mozilla-central allows you to easily build/test Firefox and trigger try pushes, especially when you have interdependent changes across Firefox and WebRender. You can also build standalone WebRender from within mozilla-central, via i.e.
cd gfx/wr cargo build --features=capture,replay,pathfinder
If you use mozilla-central (recommended), you should use the normal Gecko workflow and submit patches for review via Phabricator. If you really want, you can also submit PRs against the servo/webrender repository on Github, and one of the core contributors will review and merge your patch into mozilla-central. The mozilla-central copy of webrender is considered canonical, and there is a periodic one-way sync from mozilla-central to Github.
Testing third-party rust library changes
Sometimes when hacking on Quantum Render, you'll need to make a change to one of the upstream dependencies of the webrender library (say for example euclid). However, you may need to test out your changes in the QR build. The way to do this is not obvious, because the QR build uses the vendored copy of euclid (in third_party/rust/euclid) which you can't directly modify without violating the checksum checks. Instead, what you need to do is this:
In $MOZILLA_ROOT/Cargo.toml, there should be a section at the bottom called [patch.crates-io]. Add an entry like so:
[patch.crates-io] ... euclid = { path = "third_party/rust/euclid" }
Then run:
./mach vendor rust
Hacking
There are lots of places to help hack on Quantum Render. See the sections below for some info on where to get started.
On WebRender
A good overview of WebRender and what it is can be found in this blog post. WebRender is developed as a standalone library in the WebRender github repo. There is a wiki with several informative documents on the architecture of WebRender. In particular, Debugging WebRender document can be useful to get familiar with the tools and tricks regularly employed by developers. There is also a tutorial-style issue 3070 describing a step-by-step process of investigating and fixing a particular rendering issue.
Look through the issues list to find things to work on. Some WebRender bugs that are good for new contributors are tagged on GitHub:
- WebRender's “easy” bugs do not require specific experience with graphics rendering or WebRender.
- WebRender's “less-easy” bugs assume familiarity with Rust, graphics, and WebRender.
On Gecko integration
If you are familiar with the way Gecko normally works, the Quantum Render changes should be relatively straightforward. Instead of using a ClientLayerManager, we create a WebRenderLayerManager instance. However, this "layer manager" doesn't actually manage layers, because WebRender doesn't use layers. Instead, it walks the Gecko display list and calls the BuildWebRenderCommands function on each display item to generate WebRender display items. This display list is sent over the PWebRenderBridge IPDL channel, which is conceptually similar to PLayerTransaction in Gecko. The commands are received in WebRenderBridgeParent which interprets the messages, and talks to the core WebRender library using the API in webrender_ffi.h (via various wrapper abstractions in gfx/webrender_bindings/). The API is implemented in the webrender_bindings crate, which is written in Rust.
Look through open unassigned bugs (with no open dependencies) in the WebRender bugzilla component to find things to work on.
On reftests
Many of the existing gecko reftests (over 13000 of them) are passing with Quantum Render. However, there are still around 80 failing reftests. These are listed in this Google sheet. You're welcome to work on getting these passing - please follow the procedure below:
- Identify a single reftest you wish to work on. Try to pick one that sounds unrelated to other reftests that people are working on, because often one patch will fix multiple related reftests.
- File a bug for that reftest, and mark it as blocking bug 1322815 (webrender-reftests).
- Work on the fix. The best way to do this is to remove the fails-if or skip-if annotation in your local checkout and run the reftest using mach reftest --enable-webrender. Debug and fix as needed.
- You should do at least one try push after writing your fix to verify it works in automation, as well as to identify any other tests that are fixed by your patch. Make sure to update the annotations for all newly-passing tests as part of your final commit. Try not to introduce regressions, although breaking a small number of tests to make a larger number pass might be acceptable.
- Land your fix after getting review as appropriate.
In general, tests that are marked skip-if(webrender) are the worst, because they cause the reftest run to crash or hang and prevent other reftests from running. Slightly better than this are the random-if(webrender) tests - these can intermittently fail or pass, and so mean that there is a race condition somewhere. Slighly better than this are the fail-if(webrender) tests - these fail, but at least they do so consistently. Best of all are the ones with no webrender annotation at all, which means they behave the same as a Gecko build would.
Testing
Locally
If you want to run QR tests locally, you can use mach to run them as you would for Gecko normally. Just be sure to pass --enable-webrender to the mach command when running to enable WebRender. For example, to run the sanity reftests which is a good smoketest that you didn't horribly break everything, you can do this:
./mach reftest --enable-webrender layout/reftests/reftest-sanity/
You can also run the WR standalone tests (without the Gecko stuff) locally. The page at https://developer.mozilla.org/en-US/docs/Mozilla/QA/WebRender describes how to do this.
Try pushes
You can (and are encouraged to) push changes to tryserver to test them out before landing them.
The simplest way to render webrender-related tests is to use the "webrender" try preset:
./mach try --preset webrender
This preset runs most tests relevant to webrender with the exception of some tests running on Pixel 2 devices (for now). It is also possible to push to try using try or try fuzzy syntax explicitly:
For linux64, the tests will run using a regular linux64 build. However, you need to specify linux64-qr as the test platform to have QR enabled during testing. The same goes for windows10-64/windows10-64-qr and macosx64/macosx64-qr. So, for example, to run all the available desktop tests on QuantumRender-enabled builds, you could use this trychooser syntax:
try: -b do -p linux64,win64,macosx64 -u all[linux64-qr,windows10-64-qr,macosx64-qr] -t all[linux64-qr,windows10-64-qr,macosx64-qr]
Note that the query above does not run wrench tests.
There is also a webrender-perf try preset which only runs some of the talos tests relevant to webrender:
./mach try --preset webrender-perf
Running Android tests is a little tricker, because we only run Gecko reftests on Pixel 2 devices for Android, and we don't have a lot of those so there's often a backlog of jobs for them. In order to minimize the chance of people accidentally queuing up jobs on these device that they don't actually need, jobs that run on these devices are not selectable by default. Instead you have to run `./mach try fuzzy --full` which lists all the "hidden" jobs, and explicitly select them. From the command line, you can select the Android QuantumRender jobs like so:
./mach try fuzzy --full -q "'"'-qr android !fis' # !fis excludes fission variants which also show up with --full but are perma-fail
You can also run WebRender's standalone tests on tryserver (linting and Linux/Windows tests only for now, macOS is tracked in bug 1516568). As trychooser syntax is deprecated, the way to run these tests is to select them in ./mach try fuzzy with the query string ^webrender-. You can do this on the command line like so:
./mach try fuzzy -q '^webrender-'
To run all of the desktop QuantumRender tests as well as the WebRender standalone tests, you can use the following command:
./mach try fuzzy -q "'-qr" -q '^webrender-'
or without talos/raptor:
./mach try fuzzy -q "'"'-qr !talos !raptor' -q '^webrender-'
If you have a particular set of tests you run frequently that cannot be easily represented by trychooser or fuzzy syntax, the best way to do it is this:
- First, do a push using ./mach try fuzzy where you select the set of jobs you care about
- The head commit of that try push will have a try_tasks_config.json file which lists the jobs. Copy that file into a local commit
- Whenever you want to do a new push with that same set of jobs, cherry-pick or apply that commit as your head commit and just hg push or git push to try (no need for ./mach try here).
Automation
The integration branches (inbound and autoland) run a subset of QuantumRender tests on every push. The mozilla-central branch runs all that, plus a few extra tests that are not yet marked as tier-1.
Android
First note that WR on Android is still in the early stages of development and testing, so expect bugs.
On Android, WR can only be enabled on GeckoView-based products. In particular it *cannot* be enabled on Firefox for Android (aka Fennec), since it is not GeckoView-based. Some GeckoView products are the GeckoView Example app (aka GVE, built from mozilla-central, similar to Fennec), the Reference Browser (built via TaskCluster/Github integration), and Firefox Focus (which currently uses a pinned version of Gecko from the beta channel, so not really helpful for WR development/testing).
If you just want to run with WR enabled, the reference browser gives you a better experience - download the latest APK for your architecture, set gfx.webrender.all to true in about:config, and restart the browser. The rest of these instructions apply to the GeckoView Example App, which is the most useful for development purposes.
Building
To build GVE, follow the instructions for setting up your mozconfig from the Firefox for Android build instructions, and then run
./mach build && ./mach package && ./mach android install-geckoview_example
to build and install. You will need to enable WR by setting gfx.webrender.all to true either via about:config or in a prefs file that is used by GeckoView. If you change any Rust/C++ code, you need to re-run ./mach build binaries && ./mach package && ./mach android install-geckoview_example to get the updates on-device.
Debugging with Android Studio
The Android Studio instructions on the build instructions page work for the GeckoView Example App, as long as you choose the "geckoview_example" run configuration instead of the "app" run configuration. Debugging of Java and C++ code works in both the parent process and the content process, if you add this to your ~/.lldbinit file:
settings append target.exec-search-paths /path/to/objdir/toolkit/library settings append target.exec-search-paths /path/to/objdir/mozglue/build
You need this because there's no way in Android Studio currently to tell the content-process instance of lldb about the search paths (the UI pane only applies to the parent-process instance of lldb). Also note that when doing native code debugging with lldb, you'll hit a "segfault" at this line on startup; this is expected and can be ignored. bug 1506009 tracks Rust code debugging, which has not yet been tested extensively.
Note that to debug a content process, you have to attach to it explicitly. After starting the app in a debug configuration from Android Studio, click on the "Attach debugger to Android process" icon (tall rectangle with a bug on it) in the toolbar and select the process you want to attach to. You can be attached to multiple processes at the same time.
Debugging with the WebRender Debugger
WebRender has a built-in debug server that can be interacted with a browser-based frontend. This debugger can let you toggle debug flags in WebRender, as well as extract some of WR's internal data structures "live". The browser-based frontend can be opened by pointing your browser to the gfx/wr/debugger/index.html file, and navigating the different panes will give you an idea of what kinds of things it can do.
The debug server in WebRender is not built or enabled by default. To build it, you need to add
ac_add_options --enable-webrender-debugger
to your mozconfig file. And to enable it, you need to set the gfx.webrender.start-debug-server pref to true. All WR renderer instances created after this pref is enabled will start a separate instance of the debug server. On desktop, this means new browser windows opened after the pref is enabled will each have a debug server, although in practice only the first will be able to acquire the port and the rest will fail. On mobile (for example in the GVE) the pref needs to be set during startup for the WR renderer instance to start the debug server.
Once you have the debug server enabled, you need to ensure the frontend can talk to it. If you are running the frontend and WR on the same machine this should just work. For debugging WR on Android, you will generally load the frontend on a desktop, and will need to use adb forward tcp:3583 tcp:3583 to port-forward the connection to the device being debugged. Then click on the "Connect" button in the frontend and you should be good to go.
Bisecting
mozregression now also supports gve as a product, so it can be used to bisect regressions in WR on Android. An example bisection command:
./mach mozregression -n gve --good 2018-11-10 --pref gfx.webrender.all:true
Captures
It is possible to get WR captures from GeckoView-based products that have WR enabled. To trigger the capture, you need to run the product (e.g. GVE) with the devtools.remote.usb.enabled pref enabled, and use Web Developer > WebIDE in Firefox desktop to connect to the phone. In the "Main Process" browser console, run this command:
window.windowUtils.wrCapture();
This will tell WR to generate a capture, which will be saved to the filesystem. The path (which is something like /storage/emulated/0/Android/data/org.mozilla.geckoview_example/files/wr-capture, but may vary by device and Android version) will be printed to logcat, so you can view it by running adb logcat. To pull the capture off the device onto your local system, run adb pull /path/to/wr-capture, where the path is what was printed to logcat. This should download the entire folder, which you can then load in wrench as usual.
Further reading
There is more information at the following locations: