Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ScatterGL, a million scatter points using WebGL #661

Merged
merged 29 commits into from
May 17, 2019

Conversation

maartenbreddels
Copy link
Member

@maartenbreddels maartenbreddels commented Jun 15, 2018

This continues #630, not sure what the best strategy is, history wise etc.

cc @astrofrog
For https://github.com/glue-viz/glue-jupyter we need bqplot to handle larger scatter plots, say up to 100k-1M. This PR shows it is possible to do using WebGL (based on ipyvolume code), and I think a first good attempt. It doesn't support yet all features the Scatter mark has, for instance, only linear scales.

The demo shows it runs pretty smooth, except that at 1 million, the non-binary serialization causes quite a lag.
scattermega_example

@SylvainCorlay
Copy link
Member

This is really cool!

@maartenbreddels maartenbreddels force-pushed the scatter_webgl branch 2 times, most recently from 8268b15 to 36e1209 Compare July 26, 2018 14:07
@mprogram
Copy link

mprogram commented Jul 31, 2018

just FYI, I'm getting the following trace after the plot in the second input:

---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
/usr/lib/python3/dist-packages/ipywidgets/widgets/widget.py in _handle_msg(self, msg)
667 if 'buffer_paths' in data:
668 _put_buffers(state, data['buffer_paths'], msg['buffers'])
--> 669 self.set_state(state)
670
671 # Handle a state request.
`/usr/lib/python3/dist-packages/ipywidgets/widgets/widget.py in set_state(self, sync_data)` ` 537 from_json = self.trait_metadata(name, 'from_json',` ` 538 self._trait_from_json)` `--> 539 self.set_trait(name, from_json(sync_data[name], self))` ` 540 ` ` 541 def send(self, content, buffers=None):`
/usr/lib/python3/dist-packages/bqplot/traits.py in array_from_json_binary(value, obj)
168 def array_from_json_binary(value, obj=None):
169 if value is not None:
--> 170 return np.frombuffer(value['buffer'], dtype=value['dtype']).reshape(value['shape'])
171
172 def array_from_binary_json(value, obj=None):
``
KeyError: 'buffer'

It doesn't affect functionality though (as of commit 36e1209, with ipywidgets = 7.3.1, numpy = 1:1.14.5-1ubuntu2, and jupyterlab = 0.33.4 in the same environment).

@mprogram
Copy link

mprogram commented Aug 1, 2018

I truly like the implementation. This is just to mention, that bqplot/examples/Advanced Plotting/Animations.ipynb seems not to be working with this branch.

@maartenbreddels
Copy link
Member Author

Thanks, it is still WIP, but thanks for the feedback, I'll need to check that. This branch continues from #630, which continues #684, which continues from #680 (which just got merged), I wouldn't be surprised if #684 breaks it already.
But thanks for the feedback, keep it coming!

@maartenbreddels
Copy link
Member Author

@mprogram those issues are partly resolved in #684, and I rebased to that.

@mprogram
Copy link

I've merged locally #684, #630, #696 (rmenegaux:latex), #661, #701, #705 (davidbrochart:drag_map) #706 (box_plot_outlier_detection), – in the present order, and to mention all of my local merges – resolving trivial interim conflicts. However, I could no longer run the above example, it doesn't show anything. In addition, I'm getting the following warning under jupyter-lab build:

WARNING in d3
  Multiple versions of d3 found:
    3.5.17 ./~/d3 from ./~/bqplot/src/ScatterMegaModel.js
    4.6.0 ./~/ipyvolume/~/d3 from ./~/ipyvolume/lib/figure.js

(with the master branch of ipyvolume)

The above referenced example bqplot/examples/Advanced Plotting/Animations.ipynb, however, runs just fine with all the merges, except of the pie animation, for which it further differs if the notebook is run cell-by-cell, or from the 'run all cells' menu.

(for the curious like me: the comment is about WIP, it may not be related to a finished release)

@maartenbreddels
Copy link
Member Author

Did some rebasing and splitting of the WebGL part to a separate branch (#714). The Animation notebook should run (although the widget state should be removed first from that notebook).

@maartenbreddels
Copy link
Member Author

Some findings:
I've been doing some benchmarking/comparison, and it seems that Chrome is quite slow with large dataframes over websocket, and firefox is really fast, using a million points with firefox is no problem at all, it renders it almost instantly (the non-smoothness is mostly the limited captured fps):
scatter-mega-1e6
1 million is possible with chrome, but it just takes a few seconds for each ~4m array to transfer.
And 10 million is even possible with firefox, not super comfortable/smooth though:
scatter-mega-1e7

@maartenbreddels
Copy link
Member Author

New finding:
Quite some time was being spend in Float32Array.from, doing some benchmarking:
https://jsperf.com/comparing-typed-array-casting
I find a normal loop is about 100x faster.
Opened an issue from chromium/chrome:
https://bugs.chromium.org/p/chromium/issues/detail?id=884671

@maartenbreddels
Copy link
Member Author

The issue with chrome exists only when the dev console is open, which means that profiling (which opens de dev console) gives a distorted performance.

@DougRzz
Copy link

DougRzz commented Mar 11, 2019

@maartenbreddels On a vaguely related point, I created a set of linked plots to visualise and interactively edit 8 dimensions. It was just for fun (sad I know). Basically, it involves jslinking the skew, rotation, color, skew and opacity attributes with the x and y attributes. The animated gif shows this implimented with the standard Scatter mark. With 25 points it started to get a little sluggish. Would the ScatterMega handle many more points for this type of linked plots?

Would love to see this PR in BQplot master sometime soon :D

dimensionalViewer_8D

@martinRenou
Copy link
Member

martinRenou commented May 7, 2019

@maartenbreddels I quickly added a "cross" implementation in the shaders.
It's not perfect:

  • we should change the cross sizes that I chose for having the same visual aspect as when using SVG markers.
  • not using the smoothstep so we might have some aliasing when the cross is rotating.

cross

EDIT: The visual aspect is fixed

cross

@martinRenou
Copy link
Member

We are not taking the skew into account

@martinRenou martinRenou mentioned this pull request May 7, 2019
15 tasks
bqplot/marks.py Outdated
@@ -522,8 +522,9 @@ class _ScatterBase(Mark):
'opacity': {'dimension': 'opacity'},
'rotation': {'dimension': 'rotation'}
}).tag(sync=True)
default_opacities = List(trait=Float(1.0, min=0, max=1, allow_none=True))\
.tag(sync=True, display_name='Opacities')
default_opacities = Array(None, allow_none=True)\
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure, but I think this change breaks the default_opacities in the default Scatter implementation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed c167c2a

@martinRenou martinRenou force-pushed the scatter_webgl branch 7 times, most recently from d711e83 to 3c45439 Compare May 13, 2019 11:15
Clean dead code and clean fragment shader
Remove GLAttribute class which makes lots of copies and recreate each and every attribute buffer
on every update (e.g. changing the `x` value was triggering an `update_geometry` which was triggering
the creation of the `x`, `x_previous`, `y`, `y_previous`, `color`, `size`, `size_previous`, `rotation`,
`rotation_previous`... buffers). Creating a new AttributeBuffer means that it needs to be sent to the
GPU, and that is a slow operation.

Now the AttributeBuffers are created only once, and changing the `x`
value only triggers a change on the `x` and `x_previous` AttributeBuffers.
@maartenbreddels
Copy link
Member Author

This is good to go I think!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants