Platform/GFX/Moz2D: Difference between revisions
Line 79: | Line 79: | ||
* Factory::CreateWrappingDataSourceSurface() – Creates a DataSourceSurface to wrap an existing buffer. | * Factory::CreateWrappingDataSourceSurface() – Creates a DataSourceSurface to wrap an existing buffer. | ||
Each backend provides its own implementation of these interfaces however client code always using the backend-independent interfaces. | |||
The implementation of these interfaces typically takes the following form: | |||
<p style="text-align:center"> | |||
[[Image:Source-surface-backend.png|SourceSurfaceD2D implements SourceSurface and DataSourceSurfaceD2D implements DataSourceSurface]] | |||
</p> | |||
=== Draw targets === | === Draw targets === |
Revision as of 06:01, 14 August 2013
The Moz2D graphics API, part of the Azure project, is a cross-platform interface onto the various graphics backends that Gecko uses for rendering such as Direct2D (1.0 and 1.1), Skia, Quartz, and NV Path. Adding a new graphics platform to Gecko is accomplished by adding a backend to Moz2D (see #Implementing a new backend).
Some of the notable features of the API are:
- Mostly stateless—better suited to CSS rendering and eliminates overhead
- Floating-point—better suits platform APIs
- API methods line up with HTML canvas
Current status
The Gecko graphics API preceding Moz2D was Thebes which consists of a C++ wrapper around Cairo, some Gecko-specific utility code, and a text API that uses platform text handling. Currently both the Thebes API and Moz2D API are in use. A Thebes wrapper around Moz2D allows code that still uses the Thebes API to run on top of Moz2D. Over time this code will be migrated to using the Moz2D API directly (see Azure Conversion).
Once the Azure project is completed—that is, all the backends have been implemented and all the calling code converted to use the new Moz2D API—the old Thebes API will be removed.
Side note: For historical reasons, there are some classes with “Thebes” in the name which will be surviving this process as they're not actually part of the graphics API. They may get renamed eventually, to reduce the number of magical words you need to know about, but be warned that just because some code has “Thebes” in the name, it does not mean that the code is on its way out!
See also:
- Progress on Moz2D - July 2013 update
- Azure Conversion
Stateful vs stateless
Many of the performance gains anticipated from Moz2D come about due to its mostly-stateless API.
Stateful APIs
The Thebes API wrapped the Cairo graphics library which uses a stateful context model much like Postscript or Quartz 2D. To draw content, you make individual API calls to set up various bits of state in a context object, followed by another API call to actually perform drawing. For example, to stroke a shape with a dashed line, you would typically set the color, set the line width, set the dashing style, start a new path, emit path segments, and finally draw the stroke—all as separate API calls. In Cairo, even drawing an image requires the caller to set the source surface, emit a rectangle path, and fill—at least 6 API calls.
The overhead of setting up state can be justified when that state is re-used, but for CSS rendering this is typically not the case. Rather, for each item that is drawn fresh CSS values are fetched and re-set. Because the browser does not know what it is drawing, it is unable to group operations and re-use state effectively.
On the other hand, the HTML5 canvas element has a stateful API but because its state-tracking differs from that of Cairo drawing state must be tracked twice: once for canvas and once in Cairo.
A further complication specific to Cairo arises because internally Cairo uses a stateless surface API. On OS X, Cairo uses its Quartz backend for all drawing. However, when using Cairo, despite the fact that both Firefox and Safari ultimately use Quartz as their backend for drawing, Safari is faster than Firefox on some demos. We believe one reason this is the case is because Quartz is stateful. As a result, Cairo needs to convert from its stateful API to its internal, stateless surface API, then back to Quartz’s stateful API.
The Moz2D API, despite being mostly-stateless, was designed to map onto stateful APIs like Quartz in a more efficient manner.
Moz2D: Mostly-stateless
Almost all the operations on an Moz2D DrawTarget (see #Draw targets below) take relevant state as parameters and do actual drawing. The only state carried by a DrawTarget is the destination surface itself plus a current transform and a current clip stack. We let the transform and clip state remain in the DrawTarget because those are the only pieces of state not constantly reset by CSS rendering. Our CSS rendering needs to always render under some given transform and clip and we don't want all our rendering code to have to pass those around all the time. Because of this, the Moz2D API is called mostly-stateless.
Learning Moz2D: Introducing the API
Following is an overview of the main pieces of the API. For the details including the most recent changes, please refer to [1].
The two main components of the Moz2D API are:
- Source surfaces, and
- Draw targets.
Source surfaces
Source surfaces represent a read-only graphics buffer. There are actually two categories of source surfaces as follows:
Both SourceSurface and DataSourceSurface are abstract interfaces that are implemented by the various backends.
A SourceSurface is an opaque representation of a buffer, a handle. That means you can’t access its pixel data directly.
So what can you do?
- Use it in various drawing operations on a DrawTarget. For example,
- Draw it, or just part of it (DrawSurface)
- Use it as a mask when drawing a pattern (MaskSurface)
- Use it as a pattern will filling a rectangle (FillRect after wrapping the SourceSurface in a SourcePattern)
- You can also get a DataSourceSurface from it which does let you access its data.
A DataSourceSurface is a subclass of a SourceSurface that provides direct access to pixel data.
It is possible to get the DataSourceSurface corresponding to a SourceSurface using the SourceSurface::GetDataSurface
method.
Where do SourceSurfaces come from?
- DrawTarget::Snapshot() – SourceSurface corresponding to the current contents of a draw target.
- DrawTarget::CreateSourceSurfaceFromData() – SourceSurface for an existing memory buffer (suitable for using with the draw target it is called on).
- DrawTarget::CreateSourceSurfaceFromNativeSurface() – SourceSurface for an existing buffer of some… ?
- DrawTarget::OptimizeSourceSurface() – Converts an existing SourceSurface into one that can be readily used with the given DrawTarget.
- SourceSurface::GetDataSurface() – Gets a DataSourceSurface corresponding to an existing SourceSurface.
- Factory::CreateDataSourceSurface() – Creates an new memory buffer to be used as a DataSourceSurface.
- Factory::CreateWrappingDataSourceSurface() – Creates a DataSourceSurface to wrap an existing buffer.
Each backend provides its own implementation of these interfaces however client code always using the backend-independent interfaces.
The implementation of these interfaces typically takes the following form:
Draw targets
Developing Moz2D
Building Moz2D
Testing Moz2D: Player2D
Implementing a new backend
Further reading
The following resources describe the motivation for and goals of this API: