1 of 80

LIFE OF A

Steve Kobes

skobes@chromium.org

Nov 2020

slides: bit.ly/lifeofapixel

with special thanks to past presenters

dknox@, chrishtr@, pdr@

"The unexamined pixel is not worth rendering."

— Socrates

2 of 80

LIFE OF A

88.0.4307.0

(Nov 2020)

3 of 80

LIFE OF A

content

pixels

rendering

4 of 80

content

NOT "content"

content::WebContents

sandboxed renderer process

Blink

cc

5 of 80

content

(Hyper-Text Markup Language)

  • HTML
  • CSS
  • JS
  • images

<p> hello </p>

(Cascading Style Sheets)

p { color: red }

(JavaScript)

p.innerHTML = "goodbye";

(other kinds of rendered content: <video>, <canvas>, WebAssembly, WebGL, WebVR, PDF, ...)

<img src="kitten.gif">

6 of 80

content

https://www.nytimes.com

7 of 80

Operating System

pixels

Device Driver

  • textures
  • shaders
  • vertex buffers

#include <GL/gl.h>

( )

( )

( ?)

8 of 80

goals

  • render content into pixels

  • build data structures to enable updating the rendering efficiently

initial render

update

update

update

time

  • JavaScript
  • user input
  • asynchronous loading
  • animations
  • scrolling
  • zooming

9 of 80

stages

<html>...</html>

content

stage

stage

stage

?

?

pixels

lifecycle

intermediate data structures

10 of 80

parsing

HTMLDocumentParser

builds

  • HTML tags can nest�
  • an object model reflects the containment relationships

Document

HTML

BODY

DIV

P

P

Text

Text

(these tags are optional)

(always the root)

HTMLTreeBuilder

<div>

<p> hello </p>

<p> world </p>

</div>

11 of 80

DOM

Document

HTML

BODY

DIV

P

P

Text

Text

This is the

Document

Object

Model.

The DOM

is a tree!

  • parents
  • children
  • siblings

12 of 80

V8

DOM

Document

HTML

BODY

DIV

P

P

Text

Text

The DOM is both

  • Chrome's internal representation� AND
  • the API exposed to JavaScript

var div = document.body.firstChild;

var p2 = div.childNodes[1];

p2.appendChild(document.createElement("span"));

p2.foo = {arbitrary: "state"};

[bindings]

[ JS ]

13 of 80

DOM

<my-widget>

<b>

<wrapper>

<slot>

A custom element has a shadow tree.

<my-widget>

<b>inside</b>

</my-widget>

customElements.define('my-widget',

class extends HTMLElement { constructor() { super();

this.attachShadow({mode: 'open'}).innerHTML =

"<wrapper><slot /></wrapper>"; }});

ShadowRoot()

ShadowRoot

AssignedNodes()

host tree

shadow tree

14 of 80

DOM

<my-widget>

<b>

<wrapper>

<slot>

ShadowRoot()

ShadowRoot

AssignedNodes()

host tree

shadow tree

A flat tree traversal presents a composed view.

FlatTreeTraversal

<my-widget>

<wrapper>

<b>

15 of 80

style

/* every <p> has red text */

p { color: red }

Document

HTML

BODY

DIV

P

P

Text

Text

selects

hello

world

16 of 80

style

hello

font-weight: bold;

margin-left: 2cm;

hello

outline: dashed blue;

hello

transform: rotate(20deg);

hello

background: url(kitten.jpg);

hello

17 of 80

style

/* every other <p> in any <div> without class="foo" */

div:not(.foo) > p:nth-of-type(2n) {

color: red !important;

}

p {

color: blue;

}

  • selectors can be complex!�
  • declarations may conflict!

18 of 80

style

div:not(.foo) > p {

color: red !important;

}

p {

margin: 0;

border-left-color: blue;

...

}

...

CSSParser

StyleSheetContents

builds

StyleRule

CSSPropertyValueSet

CSSPropertyValue

Property()

Value()

BorderLeftColor

CSSColorValue

0x0000FFFF

CSSSelectorList

css_properties.json5

make_css_property_subclasses.py

border_left_color.cc

CSSSelector

19 of 80

style

Document

HTML

BODY

DIV

P

P

Text

Text

Document::UpdateStyle

ComputedStyle

ComputedStyle

ComputedStyle

ComputedStyle

ComputedStyle

ComputedStyle

ComputedStyle

ComputedStyle

ComputedStyle {

fontWeight = ...

marginLeft = ...

outline = ...

transform = ...

background = ...

...�}

Style recalc: for every node in the DOM tree, compute the value of every style property.

StyleResolver::StyleForElement

StyleSheetContents

StyleSheetContents

StyleSheetContents

20 of 80

style

getComputedStyle(element)["padding-top"]

[ JS ]

(But: getComputedStyle returns some layout data also.)

21 of 80

layout

DIV

LayoutRect

x = 183

y = 148

width = 402

height = 116

22 of 80

layout

block flow

Simple "block" layout objects are placed one after another, flowing down the page.

23 of 80

<span class="bigger">

layout

inline flow

Text and "inline" elements flow left-to-right, and are broken into lines.

(Some languages flow right-to-left.)

Lorem ipsum dolor sit amet,

consectetur adipiscing elit.

#text

line break

العربية

עִבְרִית

24 of 80

layout

Layout measures runs of text in fonts.

"fire"

HarfBuzzShaper

Font

ShapeResult

HarfBuzz

fire

Shaping selects the glyphs and

computes their placement.

25 of 80

layout

CSS

IS

AWESOME

border box rect

layout overflow rect

The contents of a layout object can overflow its border box.

Overflow can be visible, hidden, or scrollable.

26 of 80

layout

Other kinds of layout are even more complex.

  • <table>
  • float: left
  • column-count: 3
  • display: flex
  • writing-mode: vertical-lr
  • ...

27 of 80

LocalFrameView::UpdateLayout

layout

Document

HTML

BODY

DIV

P

P

Text

LayoutView

LayoutBlockFlow

LayoutBlockFlow

LayoutBlockFlow

LayoutBlockFlow

LayoutBlockFlow

LayoutText

DOM tree

layout tree

StyleEngine::RebuildLayoutTree

Each layout pass walks the layout tree, computing the visual geometry of each LayoutObject.

28 of 80

layout

DOM nodes are not 1:1 with layout objects.

<div>

ComputedStyle {

display: none

}

no LayoutObject!

LayoutInline SPAN

<div> block </div>

<span> inline </span>

LayoutBlock DIV

LayoutBlock (anonymous)

no DOM node!

<my-widget>

<div>

ShadowRoot

LayoutBlock MY-WIDGET

LayoutBlock DIV

The layout tree also crosses shadow roots.

29 of 80

LayoutObject

layout

LayoutNGMixin

LayoutObject

LayoutObject

LayoutNGMixin

The layout tree has legacy and NG layout objects.

“next generation” layout engine!

30 of 80

LayoutObject

layout

LayoutNGMixin

LayoutObject

LayoutObject

LayoutNGMixin

UpdateLayout()

🔍

🔍

🔍

🔍

A legacy layout object holds inputs, outputs, and algorithms of layout.

It can see the state of the whole tree.

31 of 80

immutable!

NGLayoutInputNode

NGLayoutInputNode

LayoutObject

layout

LayoutNGMixin

LayoutObject

LayoutObject

LayoutNGMixin

NGLayoutAlgorithm

NGConstraintSpace

UpdateLayout()

NGLayoutResult

LayoutNG separates the inputs and outputs of layout.

32 of 80

LayoutObject

layout

LayoutNGMixin

LayoutObject

LayoutObject

LayoutNGMixin

UpdateLayout()

NGLayoutResult

A layout result has a tree of fragments.

NGPhysicalFragment

NGPhysicalFragment

NGPhysicalFragment

33 of 80

layout — example

<div style="max-width: 100px">

<div style="float: left; padding: 1ex">F</div>

<br>The <b>quick brown</b> fox

<div style="margin: -60px 0 0 80px">jumps</div>

</div>

34 of 80

layout — example

<div style="max-width: 100px">

<div style="float: left; padding: 1ex">F</div>

<br>The <b>quick brown</b> fox

<div style="margin: -60px 0 0 80px">jumps</div>

</div>

35 of 80

layout — example

<div style="max-width: 100px">

<div style="float: left; padding: 1ex">F</div>

<br>The <b>quick brown</b> fox

<div style="margin: -60px 0 0 80px">jumps</div>

</div>

DOM tree

BR

"The "

B

"quick brown"

" fox"

DIV

"F"

DIV

DIV

"jumps"

36 of 80

layout — example

layout tree

LayoutBR

LayoutText "The "

LayoutInline {B}

LayoutText "quick brown"

LayoutText " fox"

LayoutNGBlockFlow {DIV}

LayoutText "F"

LayoutNGBlockFlow (floating)

LayoutNGBlockFlow {DIV}

LayoutText "jumps"

LayoutNGBlockFlow (anonymous)

<div style="max-width: 100px">

<div style="float: left; padding: 1ex">F</div>

<br>The <b>quick brown</b> fox

<div style="margin: -60px 0 0 80px">jumps</div>

</div>

37 of 80

layout — example

Box (block-flow) at 0,0 100x12

Box (block-flow children-inline) at 0,0 100x54

LineBox at 24.9,0 0x18

Box (floating block-flow children-inline) at 0,0 24.9x34

LineBox at 8,8 8.9x18

Text 'F' at 8,8 8.9x17

Text '\n' at 24.9,0 0x17

LineBox at 24.9,18 67.1x18

Text 'The ' at 24.9,18 28.9x17

Text 'quick' at 53.8,18 38.25x17

LineBox at 0,36 69.5x18

Text 'brown' at 0,36 44.2x17

Text ' fox' at 44.2,36 25.3x17

Box (block-flow children-inline) at 80,-6 20x18

LineBox at 0,0 39.125x18

Text 'jumps' at 0,0 39.125x17

fragment tree

38 of 80

layout — example

Box (block-flow) at 0,0 100x12

Box (block-flow children-inline) at 0,0 100x54

LineBox at 24.9,0 0x18

Box (floating block-flow children-inline) at 0,0 24.9x34

LineBox at 8,8 8.9x18

Text 'F' at 8,8 8.9x17

Text '\n' at 24.9,0 0x17

LineBox at 24.9,18 67.1x18

Text 'The ' at 24.9,18 28.9x17

Text 'quick' at 53.8,18 38.25x17

LineBox at 0,36 69.5x18

Text 'brown' at 0,36 44.2x17

Text ' fox' at 44.2,36 25.3x17

Box (block-flow children-inline) at 80,-6 20x18

LineBox at 0,0 39.125x18

Text 'jumps' at 0,0 39.125x17

fragment tree

39 of 80

Box (block-flow) at 0,0 100x12

Box (block-flow children-inline) at 0,0 100x54

LineBox at 24.9,0 0x18

Box (floating block-flow children-inline) at 0,0 24.9x34

LineBox at 8,8 8.9x18

Text 'F' at 8,8 8.9x17

Text '\n' at 24.9,0 0x17

LineBox at 24.9,18 67.1x18

Text 'The ' at 24.9,18 28.9x17

Text 'quick' at 53.8,18 38.25x17

LineBox at 0,36 69.5x18

Text 'brown' at 0,36 44.2x17

Text ' fox' at 44.2,36 25.3x17

Box (block-flow children-inline) at 80,-6 20x18

LineBox at 0,0 39.125x18

Text 'jumps' at 0,0 39.125x17

layout — example

fragment tree

40 of 80

PaintArtifact

paint

LocalFrameView::PaintTree

PaintCanvas::drawRect

DIV

LayoutObject

DrawRectOp

- rect {x, y, w, h}

- PaintFlags {color, …}

PO

PO

PO

paint op buffer

DisplayItem

display

item

list

Paint builds a list of paint ops.

Paint()

41 of 80

paint

<div class="yellow"></div>

<div class="green"></div>

<style>

.yellow { z-index: 2; ... }

.green { z-index: 1; ... }

</style>

DIV .yellow

DIV .green

BODY

DOM tree

yellow paints last

Paint uses stacking order,

not DOM order.

42 of 80

paint

<div id="green">

green's text

</div>

<div id="blue"></div>

blue after green, but

foregrounds after backgrounds

Each paint phase is a separate traversal of a stacking context.

backgrounds

floats

foregrounds

outlines

paint phases

(simplified)

43 of 80

paint — example

<style> #p {

position: absolute; padding: 2px;

width: 50px; height: 20px;

left: 25px; top: 25px;

border: 4px solid purple;

background-color: lightgrey;

} </style>

<div id=p> pixels </div>

44 of 80

paint — example

<style> #p {

position: absolute; padding: 2px;

width: 50px; height: 20px;

left: 25px; top: 25px;

border: 4px solid purple;

background-color: lightgrey;

} </style>

<div id=p> pixels </div>

45 of 80

paint — example

<style> #p {

position: absolute; padding: 2px;

width: 50px; height: 20px;

left: 25px; top: 25px;

border: 4px solid purple;

background-color: lightgrey;

} </style>

<div id=p> pixels </div>

DrawingDocumentBackground

DrawingBoxDecorationBackground

DrawingPaintPhaseForeground

DrawRectOp

{0, 0, 610, 316} kFill_Style

DrawRectOp

{25, 25, 87, 57} kFill_Style

DrawRectOp

{27, 27, 85, 55} kStroke_Style (w=4)

DrawTextBlobOp

{31, 45} "pixels"

display items

46 of 80

DrawingPaintPhaseForeground

paint — example

<style> #p {

position: absolute; padding: 2px;

width: 50px; height: 20px;

left: 25px; top: 25px;

border: 4px solid purple;

background-color: lightgrey;

} </style>

<div id=p> pixels </div>

DrawTextBlobOp

{31, 45}

SkTextBlob

53 4C 5B 48 4F 56

0.0 8.0 12.4 20.4 27.5 32.0

47 of 80

raster

PaintOp

cc::DisplayItemList

bitmap

Rasterization turns (part of) a display item list into a bitmap of color values.

FFFF

FFFF

FFFF

FFFF

FFFF

FFFF

0000

00FF

0000

00FF

0000

00FF

FFFF

00FF

FFFF

00FF

FFFF

00FF

(red, green,

blue, alpha)

cc::ResourcePool::InUsePoolResource

raster

48 of 80

raster

<img src="kitten.jpeg">

DrawImageOp

paint

PaintImage

raster

DecodedDrawImage

ImageDecodeCache

Raster includes image decoding.

49 of 80

InUsePoolResource

raster

GpuRasterBacking

GPU memory

GL texture ID

Raster can be accelerated by the GPU.

PaintOp

cc::DisplayItemList

50 of 80

raster

DrawRectOp

{rect, flags}

Raster()

SkCanvas::drawRect

GrRenderTargetContext::addDrawOp

GrOp

FillRectOp

SkSurface::flush

GrOp::execute

GrGLOpsRenderPass::onDraw

glDrawArrays(...)

Raster uses the Skia graphics library to issue GL calls.

another op list!

DrawRectOp::RasterWithFlags

51 of 80

GPU process

gpu

renderer process

Raster runs in the GPU process.

RasterInterface

RasterTaskImpl

RasterDecoderImpl

PaintOp::Raster

PaintOp

PaintOp

52 of 80

GPU process

gpu

renderer process

GpuChannelHost

The request is sent through a command buffer.

RasterInterface

GpuChannel

RasterCHROMIUM

PaintOp

PaintOp

RasterDecoderImpl

GPU command

buffer

53 of 80

GPU process

gpu

In the GPU process, the GL functions

link dynamically to native OpenGL.

On Windows, we translate to DirectX.

GLApi::glDrawArraysFn

dlopen("libGLESv2.so")

dlsym("glDrawArrays")

LoadLibrary("libglesv2.dll")

GetProcAddress("glDrawArrays")

ANGLE

54 of 80

change

DOM

style

layout

paint

raster

gpu

We now have a complete pipeline.

(in memory)

  • scrolling
  • zooming
  • animations
  • incremental loading
  • JavaScript

“Be the change you wish

to see in the pixels.”

—not Gandhi

But what if state can change?

55 of 80

frames

The renderer produces animation frames.

time

VSync signal

= time spent to render the frame

Below 60 frames per second,

scrolling and animations look "janky".

56 of 80

invalidation

Each pipeline stage tracks granular asynchronous invalidations.

Node::SetNeedsStyleRecalc()

style

LayoutObject::SetNeedsLayout()

PaintInvalidator::InvalidatePaint()

RasterInvalidator::Generate()

layout

paint

raster

Outputs are reused from previous frames when possible.

57 of 80

repaint

Paint + raster remain expensive if a large region is transformed…

scroll

all the pixels changed!

58 of 80

jank

… and anything on the main thread competes with JavaScript.

<script>

mineSomeBitcoins(); // why not?

</script>

time

main

59 of 80

enter: compositing

layers in Developer Tools

main

impl*

build layers

draw layers

commit

* ("impl" = compositor thread) ¯\_(ツ)_/¯

  • Decompose the page into layers

which raster independently.�

  • Combine the layers on another thread.

60 of 80

layers

<div>

AAA

<p class="wobble"> BBB </p>

</div>

CCC

BODY

DIV

#text

P

#text

#text

A simple layer captures

a subtree of content.

layer

61 of 80

layers

<div class="wobble">

AAA

<p> BBB </p>

</div>

CCC

BODY

DIV

#text

P

#text

#text

layer

A simple layer captures

a subtree of content.

62 of 80

compositing

Animation:

a layer moves

Pinch Zoom:

a layer scales

Scrolling:

a layer moves; another clips

63 of 80

threaded input

The compositor thread

can handle input events.

main

impl

scroll

scroll

scroll

busy

input from

browser process

renderer process

64 of 80

LayerTreeHost

layer promotion

Layers are created by "promoting" elements with compositing triggers.

LayoutView

LayoutBlockFlow

LayoutBlockFlow

LayoutText

LayoutBlockFlow

transform: ...

layout tree

PaintLayer

PaintLayer

PaintLayer is a

compositing

"candidate".

layer tree list

cc::Layer

cc::Layer

🤦‍♂️

65 of 80

scrolling layers

Scroll containers

create a set of special layers.

LayoutBlockFlow

overflow: scroll

PaintLayer

main layer

CompositedLayerMapping

scrolling contents layer

horz. scrollbar layer

vert. scrollbar layer

scroll corner layer

66 of 80

scrolling layers

Compositing a transparent scroller disables subpixel antialiasing.

LayoutBlockFlow

overflow: scroll

CompositedLayerMapping

Android? ChromeOS?

high DPI display (DSF >= 1.5)?

solid background?

user disabled LCD font smoothing?

non-composited scroller

no

67 of 80

cc::Layer

GraphicsLayer

compositing assignments

DOM

style

layout

paint

main

compositing assignments

PaintLayerCompositor::UpdateAssignmentsIfNeeded

cc::Layer

GraphicsLayer

cc::Layer

GraphicsLayer

cc::Layer

builds

DI

GraphicsLayer::Paint

DI

DI

Each layer is painted separately.

68 of 80

property trees

The compositor can apply some paint properties to a layer.

transform

tree

clip

tree

effect

tree

scroll

tree

69 of 80

property trees

...

layout

comp. assignments

paint

main

prepaint

PrePaintTreeWalk::Walk

The prepaint stage builds the property trees.

PaintPropertyTreeBuilder

70 of 80

composite after paint (CAP)

In the future, layers will be created after paint.

PaintArtifact

PaintOp

paint

main

composite

prepaint

...

compositing

assignments

cc::Layer

PaintArtifactCompositor

71 of 80

commit

main

impl

commit

paint

(blocked)

ready to

commit

commit

complete

copy layers and properties

cc::Layer

LayerImpl

72 of 80

tiling

impl

prepare tiles

viewport

layer

visible

tiles

Layers are broken into tiles for raster.

raster

raster

raster task

CategorizedWorkerPool

rastered

tile

73 of 80

GPU memory

CompositorFrame

DrawQuad

DrawQuaDrawQuad

d

draw layers

Tiles are "drawn" as quads.

impl

draw

Tile

TileDrawInfo

raster output

PictureLayerImpl::AppendQuads

(raster complete)

DrawQuad

rect = …

GPU

process

CompositorFrame is the "output" of the renderer process.

resource_

74 of 80

activation

impl

commit

raster

activation

draw

draw

pending

tree

active

tree

Drawing can continue while

a new commit is rastered.

LayerImpl

LayerImpl

LayerImpl

75 of 80

GPU process

display (viz)

browser UI

The display compositor

combines frames for multiple surfaces.

renderer process

CompositorFrame

browser process

CompositorFrame

renderer process

CompositorFrame

ui::Compositor

SurfaceAggregator

display

compositor

viz

76 of 80

GPU process

display (viz)

Display::DrawAndSwap

GLRenderer::DrawTileQuad

CompositorFrame

DrawQuad

GLES2Implementation

Viz draws quads

to the back buffer.

GLES2DecoderImpl

DrawQuad

viz

gpu

command

buffer

77 of 80

GPU process

display (viz)

Display::DrawAndSwap

SkiaRenderer::DrawTileDrawQuad

SkiaOutputSurfaceImplOnGpu

viz

gpu

SkDeferredDisplayList

SkSurface::draw

CompositorFrame

DrawQuad

DrawQuad

78 of 80

GPU process

display (viz)

Display::DrawAndSwap

Finally, we swap the buffers.

GLSurface::SwapBuffers

viz

gpu

GLRenderer::SwapBuffers

eglSwapBuffers(...)

The user can see the pixels.

79 of 80

GPU process

review

DOM

style

layout

comp. assign

paint

commit

raster

activate

main

impl

draw

display

Blink

tiling

prepaint

renderer process

<html>...</html>

content

80 of 80

end

LIFE OF A