The Firefox release candidate for mobile is now out, and things are looking a lot zippier since 1.1. Improving panning and zooming was a huge focus during development. We’ve eliminated major delays between user input and rendering the results on the screen, and we’ve significantly improved the animation smoothness.
So, how did we do it? Through a major effort across the Firefox platform team and the front-end team, we split the rendering and processing done by web content into a separate process from the one that handles Firefox’s user interface. Firefox developers internally refer to this split as electrolysis, and you may be familiar with the concept from other browsers like Internet Explorer and Chrome. Furthermore, the content process can asynchronously render more content than is visible so that our main process can quickly pan and zoom by transforming the rendered content appropriately. In the meanwhile, the content process is informed of the new visible region and computes the new rendering quietly in the background.
For this post, I’d like to cover the platform foundation that made this possible for mobile Firefox.
One of the largest projects completed for Firefox 4 was the ability to quickly render common kinds of transitions and animations by splitting up rendering into separate parts and compositing them together quickly using.hardware acceleration. The layers system is responsible for compositing.
The layers are stored in a tree. A container layer holds child layers, and the leaf nodes are populated by layers that render content, such as thebes layers or color layers. Each node has properties like a transformation, a clip rectangle, and an opacity, which affect the rendering of it and its children. You can see what a node looks like in Layers.h. Applying transforms, clipping, and opacities are the kinds of operations that are straightforward operations for DirectX and OpenGL pipelines, so we can generate these accelerated layers on demand for fast web page animations.
For our desktop platforms, layers are already being composited using hardware acceleration, but we have disabled it for mobile platforms for now. We hope to significantly reduce mobile Firefox’s CPU load in a near-term release through hardware acceleration.
Layers are not only useful for fast animations. They also demonstrate a semantically clean break between expensive rendering and simple rendering for web pages. The expensive rendering is done in the content process, and the simple layers compositing is done in the main process. These layers generated in the content process and forwarded to the parent process are called shadow layers. In the main process, we manipulate these shadow layers using translation and scale transformations to achieve the effect of panning and zooming. When the layers are updated from the content process, we carefully compensate for any changes in the layers so that the procedure is seamless to the user.
Our first implementation of shadow layers used sockets to transport pixels back and forth, but we quickly found this was too slow. The main process would spend too much time shoving pixels to and fro, emitting obvious stalls during panning. We now use shared double buffered memory between the processes. The main process manipulates how the read-only front buffer is rendered, while the content process refreshes invalidated content in the back buffer.
The final component allows Firefox to render more than what is visible on the screen. Otherwise, there would be no content to render for hundred of milliseconds when the user pans. We render extraneously by setting a displayport, which overrides the visible region and paints the specified rectangle we give it. The web page is none the wiser.
Using these APIs provided by our platform, mobile Firefox is able to give an experience that is responsive and fast. The experience isn’t perfect yet, but we have a solid foundation to iterate on top of.
The majority of this great work was done by Chris Jones with the assistance of Robert O’Callahan and Timothy Nikkel. Without them, this project would not have happened. Thanks guys!