Skip to content

Commit

Permalink
Prop diff cleanup (visgl#131)
Browse files Browse the repository at this point in the history
* Reorganized prop diffing. First step to perf improvements
* Streamlined lifecycle
* Layer code cleanup

* Reorganize LayerManager class

* docs

* Layer code cleanup
  • Loading branch information
ibgreen authored Oct 28, 2016
1 parent 7e6632e commit d885c63
Show file tree
Hide file tree
Showing 38 changed files with 1,137 additions and 82,908 deletions.
5 changes: 2 additions & 3 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
"plugins": [
"glslify",
"static-fs",
"transform-class-properties",
"transform-decorators-legacy",
["babel-plugin-transform-builtin-extend", {
"globals": ["Array"]
}]
["babel-plugin-transform-builtin-extend", {"globals": ["Array"]}]
],
"compact": "false"
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
dist/*
build/*
example/bundle.js
.project
.idea
.DS_Store
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ All notable changes to deck.gl will be documented in this file.
#### [v3.0.0-beta25] -
- FEATURE: Adds drawOutline option to ScatterplotLayer.
- FIX: update context.viewport issue #128
- BREAKING: deepCompare prop changed to dataComparator. lodash.isequal dependency removed.

#### [3.0.0-beta24] -
- FIX: Picking in most layers
Expand Down
23 changes: 22 additions & 1 deletion docs/custom-layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ There are a couple of ways to build a layer in deck.gl
* **Single-primitive, instanced layer** - This is type of layer renders
the same geometry many times. Usually the simplest way to go
when creating layers that renders a lot of similar objects (think
ScatterplotLayer, HexagonLayer etc).
ScatterplotLayer, ArcLayers etc).
* **Single-primitive, dynamic geometry layer** - This is needed when
dealing with data that needs to be rendered as unique, comples geometries,
such as polygons.
Expand Down Expand Up @@ -59,6 +59,27 @@ A layer is discarded when a new set of layers are rendered and none of
them match (have the same `id` prop) as your old layer.


## Change Detection

Before reading the description of each life cycle method, it is helpful
to consider change detection to understant what work a layer typically
needs to do in response to changes.

* `data` - Typically if a layer is rerendered with a changed `data` prop,
all WebGL attributes must be regenerated and the layer needs to be redrawn.
The default is to do exactly that, but sometimes a layer can be smarter
and limit updates, or more work needs to be done.

* If the viewport has changed, the layer will automatically be rerendered.
Many layers can thus ignore viewport changes, however, if the layer has
any dependencies on the viewport (such as a layer
that calculates extents or positions in screen space rather than world space)
it would need to update state or uniforms whenever the viewport changes.

* If other props change, it would typically mean that the layer needs to
update some uniform or state so that rendering is affected appropriately.


### Handling property updates

The key to writing good, performant deck.gl layers lies in understanding
Expand Down
105 changes: 66 additions & 39 deletions docs/layer-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,6 @@ at certain points in its lifecycle. The layer can specify how its state
is initialized and finalized, if and how it should react to property changes,
and how it should draw and pick the layer.

The key to creating (and using) deck.gl layers in the most efficient way
is to understand how the `shouldComponentUpdate` and `willReceiveProps`
methods collaborate to minimize state updates, as long as they are fed the
right type of props.


## Comparison with React's Lifecycle

If you are familiar with React and the
[React component lifecycle](https://facebook.github.io/react/docs/component-specs.html)
you will quickly understand the deck.gl layer lifecycle as it is based on
similar ideas. In particular, experience with the React lifecycle should help
you understand how to leverage the `shouldUpdate` and `willReceiveProps`
methods.

**Implementation Note**: deck.gl uses a simpler lifecycle model than React.
While React backs instance with a separate component, deck.gl just transfers
the old layers' state objects to any new matched layers.


## Properties and Methods

Expand All @@ -45,47 +26,54 @@ deck.gl will already have created the `state` object at this time, and
added the `gl` context and the `attributeManager` context.


### Finalization: Layer.finalizeSate()
### Finalization: Layer.finalizeState()

Called on layers from previous rendering cycle that did not get matched
with new layers. Called just before the reference to the state of that layer
is released.

This is the right time to destroy WebGL resources etc.
This is a good time to destroy any layer specific WebGL resources etc.


### Updating: shouldUpdate(oldProps, newProps)
### Updating: shouldUpdateState({oldProps, props, oldContext, context, changeFlags})

Called when a new layer has been matched with a layer from the previous
render cycle (resulting in new props being passed to that layer),
or when context has changed and layers are about to be drawn.

If this function return false, `updateState` will never be called.

Called when a new layer has been matched with an old one. If this function
return false, willReceiveProps will never be called.
The default implementation essentially does a shallow equal comparison
on the props and returns false if no properties have changed.

There are some exceptions, the `deepEqual` prop and the `updateTriggers`
There are some exceptions, the `dataComparator` prop and the `updateTriggers`
prop can be supplied additional checks. See the documentation of those
props in the Layer API.


### Updating: willReceiveProps(oldProps, newProps)
### Updating: updateState({oldProps, props, oldContext, context, changeFlags})

Called when a new layer has been matched with a layer from the previous
render cycle.
render cycle (resulting in new props being passed to that layer),
or when context has changed and layers are about to be drawn.

For information about changeFlags, see `shouldUpdateState`

The default implementation, the attributeManager will be updated with the
new data prop which will cause attributes to update if it has changed, or
any of the updateTriggers have changed.
The default implementation will invalidate all attributeManager attributes
if any change has been detected to the `data` prop.


### Decomposing: renderSublayers()
### Decomposing: renderLayers()

Allows a layer to "render" one or more Layers passing in its own state as props.
Allows a layer to "render" or generate one or more deck.gl Layers
passing in its own state as props.
The layers will be rendered after the rendering layer, but before the next
layer in the list. `renderSublayers` will be called on the new layers,
allowing a recursive decomposition of the drawing of a complex data set
into primitive layers.
layer in the list. `renderLayers` will be called on the new layers,
allowing the decomposition of the drawing of a complex data set
into "primitive" layers.

A layer can return null, a single layer, or an array of layers. The default
implementation of `renderSublayers` returns null.
implementation of `renderLayers` returns null.


### Drawing: draw({uniforms})
Expand All @@ -97,11 +85,50 @@ and calls `draw` on that model.
TBA


### Picking: getPickingModels()
### Picking: pick(uniforms, deviceX, deviceY)

The pick method should return an object with optional fields about
what was picked. This `info` object is then populated with additional
information by deck.gl and finally passed to the layer's `onHover` or
`onPick` callbacks.

The default implementation looks for a variable `model` in the layer's
state (which is expected to be an instance of the luma.gl `Model` class)
uses that model for picking.
uses that model for picking, and renders that model with attributes set
that allow the layer shaders to render picking colors instead of normal
colors.


## Comparison with React's Lifecycle

If you are familiar with React and the
[React component lifecycle](https://facebook.github.io/react/docs/component-specs.html)
you will quickly understand the deck.gl layer lifecycle as it is based on
similar ideas. In particular, experience with the React lifecycle should help
you understand property change management and how to use the
`shouldUpdateState` and `updateState` methods.

Still, there are a couple of notable differences between the lifecycle
methods provided by the two frameworks which are good to be aware of:

- deck.gl performs preliminary analysis on certain props and context and
provides a `changeFlags` object to your `shouldUpdateState` and
`updateState`.

- deck.gl's `updateState` method is called both on layer initialization and
on when props or context is updated. This is different from React's
`willReceiveProps` that is not called when the component is initially created,
The deck.gl model avoids requiring the same property checks to be performed
twice.

- deck.gl has both `draw` and `renderLayers` where React just has `render`.

- deck.gl's `pick` method has not correspondence in React's lifecycle.

TBD - this is not the best interface for enabling custom picking.
**Note**: deck.gl uses a simpler component model than React.
While React backs instance with a separate component, deck.gl just transfers
the old layers' state objects to any new matched layers.

**Note**: the data prop, attribute props and the viewport context are
central to deck.gl layers and get special handling. React is more generic
and leaves the interpretation of most props to the component.
21 changes: 14 additions & 7 deletions docs/layer.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,25 +135,32 @@ Normally the iterator for data is extracted by looking at the
`[Symbol.iterator]` is not defined, or doesn't provide the desired
iteration order, so deck.gl allows you to supply your own iterator.

Note: deck.gl even supplies an object iterator (`objectValueIterator`)
Note: deck.gl even supplies an object iterator (`makeObjectValueIterator`)
making it possible to use objects directly as `data` props in deck.gl
without first converting them to arrays.

```
import {ScatterplotLayer, objectValueIterator} from `deck.gl`;
new ScatterplotLayer({
data: {element1: [0, 0], element2: [1, 1]},
dataIterator: objectValueIterator
dataIterator: makeObjectValueIterator(data)
});
```


### deepEqual (Boolean, optional, false)
### dataComparator (Function, optional, null)

This prop causes the `data` prop to be compared using deep equality.
This has considerable performance impact and should only be used
as a temporary solution for small data sets until the application can be
refactored to avoid the need for it.
This prop causes the `data` prop to be compared using a custom comparison
function. The comparison function is called with the old data and the new
data objects, and is expected to return true if they compare equally.

Used to override the default shallow comparison of the `data` object.

As an illustration, the app could set this to e.g. 'lodash.isequal',
enabling deep comparison of the data structure. This particular examples
would obviously have considerable performance impact and should only
be used as a temporary solution for small data sets until the application
can be refactored to avoid the need.


### numInstances (Number, optional)
Expand Down
Loading

0 comments on commit d885c63

Please sign in to comment.