Name Description Size Coverage
api_resources.rs 12787 95 %
batch.rs 56193 93 %
bezier.rs 6121 -
border.rs 55430 86 %
border_image.rs 12918 100 %
box_shadow.rs 19177 98 %
bump_allocator.rs 15713 92 %
capture.rs 8789 0 %
clip.rs 98514 89 %
command_buffer.rs 23295 88 %
composite.rs Types and definitions related to compositing picture cache tiles and/or OS compositor integration. 73491 85 %
compositor 89 %
debug_colors.rs 14804 -
debug_font_data.rs 117993 -
debug_item.rs 709 0 %
debugger.rs 23189 -
device 77 %
dl_interner.rs Follower storage for items interned by the content process display list builder. See `doc/dl-builder-interning.md` for the whole design. Content mints slot numbers per display list builder, starting at zero, so two pipelines collide in a single store. The scene builder hands each pipeline a dense `DlNamespace` the first time it sees one of that pipeline's display lists, and a `DlHandle` is that namespace plus the content slot. Because the handle names its namespace, one batch can reference items from several pipelines. A `DlStore` is a pure follower: content allocates every slot and the store only replays the deltas it is handed. Nothing here ever picks a slot, and the inner level deliberately has no free list - if a store allocated for itself the two sides would diverge with nothing able to notice. 51442 86 %
ellipse.rs 6061 56 %
filterdata.rs 7160 70 %
frame_allocator.rs 16184 76 %
frame_builder.rs 60737 85 %
freelist.rs A generic backing store for caches. `FreeList` is a simple vector-backed data structure where each entry in the vector contains an Option<T>. It maintains an index-based (rather than pointer-based) free list to efficiently locate the next unused entry. If all entries are occupied, insertion appends a new element to the vector. It also supports both strong and weak handle semantics. There is exactly one (non-Clonable) strong handle per occupied entry, which must be passed by value into `free()` to release an entry. Strong handles can produce an unlimited number of (Clonable) weak handles, which are used to perform lookups which may fail of the entry has been freed. A per-entry epoch ensures that weak handle lookups properly fail even if the entry has been freed and reused. TODO(gw): Add an occupied list head, for fast iteration of the occupied list to implement retain() style functionality. 7675 91 %
glyph_cache.rs 6815 93 %
gpu_types.rs 24636 84 %
hit_test.rs 18621 96 %
image_source.rs This module contains the logic to obtain a primitive's source texture and uv rect. Currently this is a somewhat involved process because the code grew into having ad-hoc ways to store this information depending on how the image data is produced. The goal is for any textured primitive to be able to read from any source (texture cache, render tasks, etc.) without primitive-specific code. 4277 93 %
image_tiling.rs 27436 96 %
intern.rs The interning module provides a generic data structure interning container. It is similar in concept to a traditional string interning container, but it is specialized to the WR thread model. There is an Interner structure, that lives in the scene builder thread, and a DataStore structure that lives in the frame builder thread. Hashing, interning and handle creation is done by the interner structure during scene building. Delta changes for the interner are pushed during a transaction to the frame builder. The frame builder is then able to access the content of the interned handles quickly, via array indexing. Epoch tracking ensures that the garbage collection step which the interner uses to remove items is only invoked on items that the frame builder thread is no longer referencing. Items in the data store are stored in a traditional free-list structure, for content access and memory usage efficiency. The epoch is incremented each time a scene is built. The most recently used scene epoch is stored inside each handle. This is then used for cache invalidation. 19382 80 %
internal_types.rs 37754 65 %
invalidation 90 %
lib.rs ! A GPU based renderer for the web. WebRender turns display lists into GPU draw calls. It is the rendering engine of Firefox, and can also be used standalone. # Api Structure [`create_webrender_instance()`](crate::create_webrender_instance) returns a [`Renderer`] plus a [`RenderApiSender`](crate::render_api::RenderApiSender). The [`Renderer`] owns the GPU connection and draws frames; the sender is how you talk to everything else. [`create_api()`](crate::render_api::RenderApiSender::create_api) gives you a [`RenderApi`](crate::render_api::RenderApi), which manages resources and documents. Work is submitted as a [`Transaction`](crate::render_api::Transaction) — most importantly [`set_display_list()`](crate::render_api::Transaction::set_display_list), which takes a [`BuiltDisplayList`](api::BuiltDisplayList) produced by finalizing a [`DisplayListBuilder`](api::DisplayListBuilder). Display lists nest [stacking contexts][stacking_contexts]. Completion is reported through the [`RenderNotifier`](api::RenderNotifier) you passed at init. # Threads Work is split across threads, and knowing which thread a piece of code runs on explains most of the structure here: - **Scene builder thread** (`scene_builder_thread.rs`, `scene_building.rs`) turns display lists into a [`BuiltScene`](crate::scene::BuiltScene). Runs asynchronously; only re-runs when the display list changes. - **Render backend thread** (`render_backend.rs`, `frame_builder.rs`) turns a scene plus the current scroll/animation state into a `Frame`. Runs every frame. This is where most of the interesting logic lives. - **Render thread** (`renderer/`, `device/`) is the only thread that touches the GPU, and is also the initial entry point into the crate. The `renderer` module documentation describes the render/render-backend split in more detail. Rayon workers and the [`glyph_rasterizer`] are used for parallel work underneath these. # Anatomy of a frame `FrameBuilder::build` in `frame_builder.rs` runs these stages in order. Each has its own module, and this is the sequence to follow when tracing why something renders wrongly: 1. **Picture graph passes** (`picture_graph.rs`) walk the picture tree in dependency order, assign off-screen surfaces (`surface.rs`) and propagate bounding rects. 2. **Visibility** (`visibility.rs`) culls primitives, resolves clip chains (`clip.rs`), snaps rects to the pixel grid, and updates picture-cache tile dependencies (`tile_cache/`, `invalidation/`). 3. **Prepare** (`prepare.rs`) walks each visible primitive by `PrimitiveKind`, builds its `Pattern` (`pattern/`), requests any render tasks it needs, and emits draw commands into command buffers (`command_buffer.rs`). Most primitives go through the quad path (`quad.rs`). 4. **Render task graph** (`render_task_graph.rs`) is finalized: tasks are assigned to passes and to render target allocations (`render_target.rs`). 5. **Batching** (`batch.rs`) replays the command buffers per pass, grouping draws into batches keyed by shader and textures. 6. **Compositing** (`composite.rs`) builds the list of picture-cache tiles to present, either drawn by us or handed to an OS compositor (`compositor/`). The [`Renderer`] then submits the resulting `Frame`: it uploads resources, executes the passes in order, and composites. # Further reading - `gfx/docs/RenderingOverview.md` — how WebRender fits into Gecko, plus the picture / spatial / clip / render-task trees. Note it predates the quad and pattern architecture described above and still describes the retired brush shaders. - `gfx/wr/webrender/doc/coordinate-spaces.md` — the spatial tree, and the local / picture / raster / world / device spaces. - `gfx/wr/webrender/doc/text-rendering.md`, `blob.md`, `CLIPPING_AND_POSITIONING.md`, `swizzling.md` — subsystem deep dives, in varying states of currency. # External dependencies WebRender depends on [FreeType](https://www.freetype.org/) for font rasterization on some platforms. [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context 8992 -
lru_cache.rs This module implements a least recently used cache structure, which is used by the texture cache to manage the lifetime of items inside the texture cache. It has a few special pieces of functionality that the texture cache requires, but should be usable as a general LRU cache type if useful in other areas. The cache is implemented with two types of backing freelists. These allow random access to the underlying data, while being efficient in both memory access and allocation patterns. The "entries" freelist stores the elements being cached (for example, the CacheEntry structure for the texture cache). These elements are stored in arbitrary order, reusing empty slots in the freelist where possible. The "lru_index" freelists store the LRU tracking information. Although the tracking elements are stored in arbitrary order inside a freelist for efficiency, they use next/prev links to represent a doubly-linked list, kept sorted in order of recent use. The next link is also used to store the current freelist within the array when the element is not occupied. The LRU cache allows having multiple LRU "partitions". Every entry is tracked by exactly one partition at any time; all partitions refer to entries in the shared freelist. Entries can move between partitions, if replace_or_insert is called with a new partition index for an existing handle. The partitioning is used by the texture cache so that, for example, allocating more glyph entries does not cause eviction of image entries (which go into a different shared texture). If an existing handle's entry is reallocated with a new size, it might need to move from a shared texture to a standalone texture; in this case the handle will move to a different LRU partition. 23503 90 %
path_tiler.rs A sparse tiling path rasterizer. Paths are flattened and their edges binned into 16x16 pixel tiles on the CPU. Tiles that contain edges are rasterized on the GPU by evaluating the area coverage of each of their edges in the fragment shader (see `cs_path_tile`), while tiles that are fully covered are emitted as solid spans. Tiles that are not covered at all are not emitted. # Overview Evaluating coverage for arbitrary paths is difficult because paths can contain a lot of edges, and edges very far from the sample position can contribute to its coverage. Brute force is not a viable approach, so the name of the game is to break large paths into many small regions that can be evaluated independently in parallel on the GPU. The theory behind this approach is that given a small region of the entire path shape in device space (a tile), coverage for each pixel of the tile can be determined by only looking at the edges that cross that tile if we know the winding number at one reference point of the tile (in our case, the top left corner). We call this reference point the backdrop of the tile. # Algorithm Each tile carries a backdrop: the winding number at its top-left corner. It is computed on the CPU by accumulating the winding of the edges that cross the horizontal tile boundaries, from left to right within each row of tiles. The tiling algorithm contains three stages: binning, sorting and scanning: - (Bin) For each (curve) segment in the input path: - For each line segment in the flattened approximation of the curve segment: - Split the segment into segments that fit into tiles using an adaptation of the DDA algorithm. - Push the split segments into an event buffer. - Whenever a segment crosses a horizontal tile boundary, push a backdrop event into the event buffer. - (Sort) Sort events from top to bottom and from left to right. - (Scan) Scan the event buffer, emitting tile segments and instances. Edges are implicitly grouped by tile via the sorting step. This makes it trivial to generate the edge data. The scanning stage acts as a very coarse rasterizer: it computes winding numbers for the top-left of each tile and can trivially detect when a tile is fully inside or outside of the path. This enables the tiler to not generate geometry where it isn't needed and to produce very cheap geometry where there is coverage but no edges in the tile. Consecutive fully covered tiles (with no edges) are merged into a single "stretched" tile, which greatly reduces the amount of geometry produced. ## Auxiliary edges Although it is sufficient to know only the backdrop winding number and edges overlapping the tile, we want the fragment shader to evaluate the edges by casting horizontal rays (instead of rays between the backdrop and the shaded pixel). To make this work we need a way to express the winding number along the left side of the tile. Conceptually this is equivalent to tracing a vertical ray down from the backdrop along the left side of the tile. If an edge leaves the tile, it has positive contribution to the winding number of all positions below it, whereas if an edge enters the tile, it has a negative contribution to all positions below it. These contributions can be represented by positive/negative vertical edges going from the point where an edge enters/leaves a tile, down to the bottom of the tile along the left side. These edges are called **auxiliary edges**. They are added during the binning phase and treated as regular edges by the rest of the pipeline. # Tradeoffs With large tile sizes, the number of pixels that can require expensive edge evaluation increases and the number of edges per tile increases, putting more pressure on the GPU. Smaller tile sizes naturally run faster on the GPU, but require more work on the CPU since path segments get broken up into more edges which need to be sorted and scanned. I experimentally found the sweet spot to be somewhere between 8x8 and 16x16 tiles, depending on the hardware and the anti-aliasing strategy used by the shader. 45714 0 %
pattern 96 %
picture.rs A picture represents a dynamically rendered image. # Overview Pictures consists of: - A number of primitives that are drawn onto the picture. - A composite operation describing how to composite this picture into its parent. - A configuration describing how to draw the primitives on this picture (e.g. in screen space or local space). The tree of pictures are generated during scene building. Depending on their composite operations pictures can be rendered into intermediate targets or folded into their parent picture. ## Picture caching Pictures can be cached to reduce the amount of rasterization happening per frame. When picture caching is enabled, the scene is cut into a small number of slices, typically: - content slice - UI slice - background UI slice which is hidden by the other two slices most of the time. Each of these slice is made up of fixed-size large tiles of 2048x512 pixels (or 128x128 for the UI slice). Tiles can be either cached rasterized content into a texture or "clear tiles" that contain only a solid color rectangle rendered directly during the composite pass. ## Invalidation Each tile keeps track of the elements that affect it, which can be: - primitives - clips - image keys - opacity bindings - transforms These dependency lists are built each frame and compared to the previous frame to see if the tile changed. The tile's primitive dependency information is organized in a quadtree, each node storing an index buffer of tile primitive dependencies. The union of the invalidated leaves of each quadtree produces a per-tile dirty rect which defines the scissor rect used when replaying the tile's drawing commands and can be used for partial present. ## Display List shape WR will first look for an iframe item in the root stacking context to apply picture caching to. If that's not found, it will apply to the entire root stacking context of the display list. Apart from that, the format of the display list is not important to picture caching. Each time a new scroll root is encountered, a new picture cache slice will be created. If the display list contains more than some arbitrary number of slices (currently 8), the content will all be squashed into a single slice, in order to save GPU memory and compositing performance. ## Compositor Surfaces Sometimes, a primitive would prefer to exist as a native compositor surface. This allows a large and/or regularly changing primitive (such as a video, or webgl canvas) to be updated each frame without invalidating the content of tiles, and can provide a significant performance win and battery saving. Since drawing a primitive as a compositor surface alters the ordering of primitives in a tile, we use 'overlay tiles' to ensure correctness. If a tile has a compositor surface, _and_ that tile has primitives that overlap the compositor surface rect, the tile switches to be drawn in alpha mode. We rely on only promoting compositor surfaces that are opaque primitives. With this assumption, the tile(s) that intersect the compositor surface get a 'cutout' in the rectangle where the compositor surface exists (not the entire tile), allowing that tile to be drawn as an alpha tile after the compositor surface. Tiles are only drawn in overlay mode if there is content that exists on top of the compositor surface. Otherwise, we can draw the tiles in the normal fast path before the compositor surface is drawn. Use of the per-tile valid and dirty rects ensure that we do a minimal amount of per-pixel work here to blend the overlay tile (this is not always optimal right now, but will be improved as a follow up). 135926 83 %
picture_composite_mode.rs 58591 86 %
picture_graph.rs 7652 100 %
picture_textures.rs 12222 95 %
prepare.rs # Prepare pass The second frame building traversal of the picture tree. It runs after the [visibility pass](crate::visibility). Where visibility decides what is drawn, prepare decides how: it turns each visible primitive into the render tasks, GPU data and draw commands needed to render it. Its outputs are: - Render tasks and their dependencies in the render task graph. - GPU buffer data. - [`PrimitiveCommand`]s pushed into the command buffers of the surfaces and tiles the primitive is drawn into. `batch.rs` replays these at the end of frame building to produce the actual draw calls. ## Traversal [`prepare_picture`] is the entry point. It does a recursive traversal of the picture and it's visible items. ## Per-primitive work The bulk of the module is a large match over [`PrimitiveKind`] in `prepare_prim_for_render`, where each kind: - Requests the resources it needs, such as texture cache entries (images, glyphs) or render tasks (rasterized borders, blurred box shadow,cached gradients, etc.). - Writes the GPU data for the primitive. - Push potentially multiple commands to command buffers. 55425 82 %
prim_store 89 %
print_tree.rs 3278 0 %
profiler.rs # Overlay profiler ## Profiler UI string syntax Comma-separated list of of tokens with trailing and leading spaces trimmed. Each tokens can be: - A counter name with an optional prefix. The name corresponds to the displayed name (see the counters vector below. - By default (no prefix) the counter is shown as average + max over half a second. - With a '#' prefix the counter is shown as a graph. - With a '*' prefix the counter is shown as a change indicator. - Some special counters such as GPU time queries have specific visualizations ignoring prefixes. - A preset name to append the preset to the UI (see PROFILER_PRESETS). - An empty token to insert a bit of vertical space. - A '|' token to start a new column. - A '_' token to start a new row. 82546 64 %
quad.rs 96004 92 %
quad_clip.rs A self-contained description of the clips applied to a quad primitive. The types here hold the clipping information already pulled out of the clip store and the clip interner, so that the quad path never has to resolve a handle or an index range. They are the clip-side analogue of `QuadTransformState`, which hides the details of incoming transforms behind a small owned type, and a step towards splitting the low-level 2D rendering code away from the data structures that represent WebRender's scene. `ClipStore::fill_quad_clips` is the bridge that builds a `QuadClipStack` out of a `ClipChainInstance`. It deliberately lives in `clip.rs`, on the side of the split that knows about both. 6959 94 %
rectangle_occlusion.rs A simple occlusion culling algorithm for axis-aligned rectangles. ## Output Occlusion culling results in two lists of rectangles: - The opaque list should be rendered first. None of its rectangles overlap so order doesn't matter within the opaque pass. - The non-opaque list (or alpha list) which should be rendered in back-to-front order after the opaque pass. The output has minimal overdraw (no overdraw at all for opaque items and as little as possible for alpha ones). ## Algorithm overview The occlusion culling algorithm works in front-to-back order, accumulating rectangle in opaque and non-opaque lists. Each time a rectangle is added, it is first tested against existing opaque rectangles and potentially split into visible sub-rectangles, or even discarded completely. The front-to-back order ensures that once a rectangle is added it does not have to be modified again, making the underlying data structure trivial (append-only). ## splitting Partially visible rectangles are split into up to 4 visible sub-rectangles by each intersecting occluder. ```ascii +----------------------+ +----------------------+ | rectangle | | | | | | | | +-----------+ | +--+-----------+-------+ | |occluder | | --> | |\\\\\\\\\\\| | | +-----------+ | +--+-----------+-------+ | | | | +----------------------+ +----------------------+ ``` In the example above the rectangle is split into 4 visible parts with the central occluded part left out. This implementation favors longer horizontal bands instead creating nine-patches to deal with the corners. The advantage is that it produces less rectangles which is good for the performance of the algorithm and for SWGL which likes long horizontal spans, however it would cause artifacts if the resulting rectangles were to be drawn with a non-axis-aligned transformation. ## Performance The cost of the algorithm grows with the number of opaque rectangle as each new rectangle is tested against all previously added opaque rectangles. Note that opaque rectangles can either be added as opaque or non-opaque. This means a trade-off between overdraw and number of rectangles can be explored to adjust performance: Small opaque rectangles, especially towards the front of the scene, could be added as non-opaque to avoid causing many splits while adding only a small amount of overdraw. This implementation is intended to be used with a small number of (opaque) items. A similar implementation could use a spatial acceleration structure for opaque rectangles to perform better with a large amount of occluders. 7526 100 %
render_api.rs 71304 65 %
render_backend.rs The high-level module responsible for managing the pipeline and preparing commands to be issued by the `Renderer`. See the comment at the top of the `renderer` module for a description of how these two pieces interact. 118936 71 %
render_backend_pool.rs Pool of render-backend threads. Each pool member owns three threads — a render backend thread and its companion scene builder and (optional) low-priority scene builder. Windows are assigned to a member round-robin via [`RenderBackendPool::assign`], after which a `WindowRegistration` message is sent on the member's api channel to install per-window state. 9836 99 %
render_target.rs 45005 88 %
render_task.rs 103857 76 %
render_task_cache.rs 14484 92 %
render_task_graph.rs This module contains the render task graph. Code associated with creating specific render tasks is in the render_task module. 54390 67 %
renderdoc.rs On-demand integration with the RenderDoc in-application capture API. A capture is armed via a `DebugCommand::CaptureRenderDoc` (driven from the WebRender debugger / wrshell); the next composited frame is then wrapped in a RenderDoc frame capture and the path of the written `.rdc` is returned. RenderDoc hooks OpenGL at library-load time, so `librenderdoc` must be loaded *before* the GL context is created. In practice this means launching the host process (e.g. Firefox) with `LD_PRELOAD=.../librenderdoc.so`. We resolve the already-loaded library with `RTLD_NOLOAD` and use the in-app API entry point; we deliberately do not load a fresh copy, since a late load cannot install the GL hooks. 10272 -
renderer 73 %
resource_cache.rs 102791 77 %
scene.rs 16535 97 %
scene_builder_thread.rs 57482 88 %
scene_building.rs # Scene building Scene building is the phase during which display lists, a representation built for serialization, are turned into a scene, webrender's internal representation that is suited for rendering frames. This phase is happening asynchronously on the scene builder thread. # General algorithm The important aspects of scene building are: - Building up primitive lists (much of the cost of scene building goes here). - Creating pictures for content that needs to be rendered into a surface, be it so that filters can be applied or for caching purposes. - Maintaining a temporary stack of stacking contexts to keep track of some of the drawing states. - Stitching multiple display lists which reference each other (without cycles) into a single scene (see build_reference_frame). - Interning, which detects when some of the retained state stays the same between display lists. The scene builder linearly traverses the serialized display list which is naturally ordered back-to-front, accumulating primitives in the top-most stacking context's primitive list. At the end of each stacking context (see pop_stacking_context), its primitive list is either handed over to a picture if one is created, or it is concatenated into the parent stacking context's primitive list. The flow of the algorithm is mostly linear except when handling: - shadow stacks (see push_shadow and pop_all_shadows), - backdrop filters (see add_backdrop_filter) 175844 88 %
scene_debug.rs Debug-only inspection and per-primitive modifications of a built scene, driven by the remote debugger (see `debugger.rs` and the wrshell scene tree panel). A client can query the picture / primitive tree, disable primitive instances and highlight one of them; the modifications are applied during frame building, without rebuilding the scene. 12510 92 %
screen_capture.rs Screen capture infrastructure for the Gecko Profiler and Composition Recorder. 19953 75 %
segment.rs 40775 85 %
shutdown_test.rs 13820 -
space.rs Utilities to deal with coordinate spaces. 42976 98 %
spatial_node.rs 51646 94 %
spatial_tree.rs 76401 81 %
surface.rs Contains functionality to help building the render task graph from a series of off-screen surfaces that are created during the prepare pass, and other surface related types and helpers. 39698 97 %
svg_filter.rs 89841 53 %
telemetry.rs 2740 100 %
texture_cache.rs 69885 92 %
texture_pack 79 %
tile_cache 90 %
transform.rs 11130 85 %
util.rs 42646 98 %
visibility.rs # Visibility pass The first of the two frame building traversals of the picture tree, the second being the [prepare pass](crate::prepare). It is driven by `FrameBuilder::build_layer_screen_rects_and_cull_layers`, which calls [`update_prim_visibility`] once per snapshot picture and once per tile cache slice. From there the pass walks down the picture tree, pushing and popping off-screen surfaces as it goes. For each primitive instance it visits, the pass works out whether the primitive is drawn this frame and under which clips, and records the answer in a [`PrimitiveDrawHeader`], pushed into `scratch.primitive.frame.draws` as each drawn primitive is found. Later passes read those headers instead of re-deriving the information. In addition to visibility calculation, this pass performs snapping and builds clip chain instances. ## Surface bookkeeping Alongside the per-primitive state, the traversal accumulates the exact (clipped) local rect of each off-screen surface from the coverage rects of the primitives drawn into it, and propagates culling rects from parent to child surfaces. The prepare pass sizes the surfaces' render tasks from those accumulated rects, so they must be complete before it runs, which is the main reason visibility is a separate pass. 41566 93 %