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

Using a shared webgl context across graph divs (e.g for dashboard apps)? #2333

Closed
sgentile opened this issue Feb 5, 2018 · 29 comments · Fixed by #6784
Closed

Using a shared webgl context across graph divs (e.g for dashboard apps)? #2333

sgentile opened this issue Feb 5, 2018 · 29 comments · Fixed by #6784
Labels
feature something new

Comments

@sgentile
Copy link

sgentile commented Feb 5, 2018

We are adding 10+ scattergl charts to a page, but running into issues where we run out of webgl contexts. It appears that creating a new plot.ly chart uses it's own webgl context.

Is there a way to create a webgl context and use it for all the charts, or is there a another way to have many charts on the page without having this issue ?

Thanks

@etpinard
Copy link
Contributor

etpinard commented Feb 5, 2018

That shouldn't happen since 1.33.0. Can you share a reproducible example?

@sgentile
Copy link
Author

sgentile commented Feb 5, 2018

Unfortunately I cannot share the code.
image

@etpinard
Copy link
Contributor

etpinard commented Feb 5, 2018

Ok. Are you embedding plots from our cloud service on plot.ly?

@sgentile
Copy link
Author

sgentile commented Feb 5, 2018

no, we are not using the cloud service

we directly create the plots - we are using Angular 4

ie.

Plotly.newPlot(this.element.nativeElement, this.data, this.plotlyLayout, this.plotlyConfiguration );

our layout for a chart looks like this:

{
            "chartType": "scattergl",
            "label": "xxxxx",
            "displayByDataType": false,
            "x": "xxx",
            "y": "yyyy",
            "chartLayout":{
              "title": "xxxxx",
              "xaxis": {"title": "xxxx(Z)", "type":"date", "hoverformat": "%Y-%m-%dT%H:%M:%S" },
              "yaxis": {"title": "xxxx"},
              "margin": {"t": 80},
              "hovermode": "closest",
              "showlegend": true,
              "legend": {
                  "orientation": "h",
                  "xanchor": "center",
                  "yancher": "bottom",
                  "y": -0.35,
                  "x": 0.5
                },
              "autosize": true,
              "annotations": [{
                "xref": "paper",
                "yref": "paper",
                "x": 0,
                "xanchor": "middle",
                "y": 1,
                "yanchor": "bottom",
                "text": "xxxx",
                "showarrow": false,
                "font": {
                    "size": 16,
                    "color": "black"
                }
              }]
            }
        },

@etpinard
Copy link
Contributor

etpinard commented Feb 5, 2018

... and what does console.log(Plotly.version) output?

@sgentile
Copy link
Author

sgentile commented Feb 5, 2018

1.33.1

@etpinard
Copy link
Contributor

etpinard commented Feb 5, 2018

Oh. Sorry I misread. You have 10 different scattergl charts in different <div> elements? Not 10 scattergl subplots? Correct?

If the former is correct, I think I know what's happening:

Before 1.33.0, we created 1 WebGL context per scattergl subplot. So in your case (assuming you have one scattergl subplot per graph), that means 10 WebGL contexts - which is under the Chrome threshold for that console warning you're seeing (I believe that threshold is 16).

Now starting in 1.33.0, we create 3 WebGL contexts per graph, no matter how many scattergl subplots there are. In the other words, those three WebGL contexts are shared across subplots in a given graph making graphs like this one generate reasonable fast and without console warnings using only 3 WebGL contexts (compare to 35 before version 1.33.0). But for your use case this means 3 times 10 (number of graphs) WebGL contexts on your page which is above Chrome's threshold.

We aren't planning on extended WebGL-context sharing across multiple graphs in the near future unfortunately, but it's something we should think about. So, thanks very much for posting. In the meantime, I suggest converting your 10 graph <div>s into subplots on the least number of graph <div>s possible. Sorry for the inconvenience.

@sgentile
Copy link
Author

sgentile commented Feb 5, 2018 via email

@etpinard etpinard changed the title scattergl version 1.33.1 - using a shared webgl context for scattergl? Using a shared webgl context across graph divs (e.g for dashboard apps)? Feb 6, 2018
@etpinard
Copy link
Contributor

etpinard commented Mar 5, 2018

We should give https://github.com/regl-project/multi-regl a try. It might be just what we need here.

@brian428
Copy link

brian428 commented May 24, 2018

I saw the last reply from @etpinard on #2614. It sounds like they've reduced the number of contexts used by each plot from 3 to 2. Which theoretically should allow 7 GL plots at once.

In practice, this doesn't work very well though. We're constantly updating the plot data and layout, and after trying out the various Plotly methods (update(), relayout(), etc.), the only option that worked for the extensive and arbitrary updates we must allow for is to purge() and then recreate with plot(). This introduces a ton of complications though (maintaining zoom, maintaining selection, maintaining modebar state, etc.). I've dealt with most of these (though it was certainly not easy).

Apparently, a purge() tears everything down and attempts to release the GL context(s) back to the browser's pool. However, it seems like this release is not immediate/synchronous (it looks like they are garbage collected eventually, but not necessarily instantly). So "out of context" warnings will often appear, even if the browser subsequently gets the released context back again after the next GC run.

So, one thing that could help is to provide a way to re-create a chart (like a purge() + plot()), but in a way that tells Plotly to re-use the previous contexts instead of releasing them and trying to grab 2 more from the browser pool. Perhaps a Plotly.replot() function, or something similar?

Obviously, it would also help if each plot only used one context (instead of two). If each plot used one context, and we could re-create plots while continuing to use the same contexts, that would allow for up to 16 GL plots.

Now, an ever better option would be to share contexts across all of the plots. To help with performance, the best option would be a way to tell Plotly to pre-allocate N contexts to a "Plotly GL context pool", and use them as needed across all plots on the page. So we could set this to 12, or 14 or whatever we wanted to. We have chart sets that include a Cesium plot, so we need to allow for some contexts to be used outside of Plotly.

On a loosely related note, I have an open issue with Chrome to make the GL context limit configurable (https://bugs.chromium.org/p/chromium/issues/detail?id=771792#c10). The limit of 16 was chosen arbitrarily, and anyone with a powerful GPU should easily be able to handle more than 16.

@etpinard
Copy link
Contributor

We're constantly updating the plot data and layout, and after trying out the various Plotly methods (update(), relayout(), etc.), the only option that worked for the extensive and arbitrary updates we must allow for is to purge() and then recreate with plot()

That shouldn't happen. Calling Plotly.relayout or Plotly.update should not create new WebGL contexts (unless you're restyling from an SVG trace to a WebGL trace).

Would you mind sharing a reproducible example?

@brian428
Copy link

The problems we had with relayout() and update() weren't related to WebGL contexts, but were related to trying to get the arbitrary updates we must make to go through:

  • restyle() doesn't work, because each trace's data is changing independent of the others (X/Y values, individual point color, size, etc.). Further, traces can also be getting added and removed.
  • relayout() seems fine for updates to only the layout, but our updates are usually to both the trace data and the layout.
  • update() covers both cases, but doesn't account for the fact that traces may be getting added and removed, nor the fact that the X/Y arrays, individual point color, size, etc. for each trace are changing separately.

At one point I tried combining several of these (e.g.: deleteTraces() + addTraces() + relayout()), but running each of these on each plot was actually slower than just re-creating it with a purge() and a plot().

I have not had a chance to look at react() yet, since it is very new. Does it maintain the GL context?

@etpinard
Copy link
Contributor

At one point I tried combining several of these (e.g.: deleteTraces() + addTraces() + relayout()), but running each of these on each plot was actually slower than just re-creating it with a purge() and a plot().

Ha I see. You might want to try our newest API method Plotly.react.

@brian428
Copy link

Right, I mentioned react() in the last line above. Would using react() re-use the existing WebGL contexts for the plot?

@etpinard
Copy link
Contributor

Would using react() re-use the existing WebGL contexts for the plot?

Yes 🎉

@brian428
Copy link

OK, that may be something to look at. It would help with the asynchronous release of the contexts causing context limit errors. However, the underlying issue of allowing for more than 6 or 7 GL plots at once is still a challenge. Some sort of context sharing across all of the GL plots would be a great option.

I've also looked at subplots to handle this, but the lack of a modebar for each subplot is one issue (e.g. can't autoscale only one subplot), and determining a "dynamic" full-screen layout for the subplots is challenging, when the number of plots shown varies, and when the user can add and remove plots at will. Right now we're using divs with column-based flex layout for the chart sets, so they effectively use all the available browser width and height.

@joshua-gould
Copy link
Contributor

Another workaround is to virtual scrolling so that only 6 charts are rendered at one time.

@joshua-gould
Copy link
Contributor

Is there a scatter renderer that uses 2d instead of webgl? Thanks.

@bidva
Copy link

bidva commented Jul 9, 2020

I'm using react-plotly.js and having the same issue, my case is a dashboard page that loads ~20 different plots with too many points. the page can handle 11 of them but after adding 12th one it starts showing the warning and some old plot randomly will be broken. The react component using the react() method underneath.

list.map((item)=><Plot
          useResizeHandler
          onRelayout={onLayoutChange}
          onClick={onClick}
          onLegendClick={onLegendClick}
          onLegendDoubleClick={onLegendDoubleClick}
          style={{ width: '100%', height: '100%' }}
          layout={_.cloneDeep(plotProps.layout)}
          config={{
            displaylogo: false,
            scrollZoom: true,
            modeBarButtonsToRemove: ['sendDataToCloud'],
          }}
        />)

@leeoniya
Copy link

leeoniya commented Jul 9, 2020

in case someone finds this helpful, i'll plug https://github.com/leeoniya/uPlot, which does not use GL, is lightweight and can handle hundreds of charts with millions of datapoints without issue.

@jackparmer
Copy link
Contributor

This issue has been tagged with NEEDS SPON$OR

A community PR for this feature would certainly be welcome, but our experience is deeper features like this are difficult to complete without the Plotly maintainers leading the effort.

Sponsorship range: $25k-$30k

What Sponsorship includes:

  • Completion of this feature to the Sponsor's satisfaction, in a manner coherent with the rest of the Plotly.js library and API
  • Tests for this feature
  • Long-term support (continued support of this feature in the latest version of Plotly.js)
  • Documentation at plotly.com/javascript
  • Possibility of integrating this feature with Plotly Graphing Libraries (Python, R, F#, Julia, MATLAB, etc)
  • Possibility of integrating this feature with Dash
  • Feature announcement on community.plotly.com with shout out to Sponsor (or can remain anonymous)
  • Gratification of advancing the world's most downloaded, interactive scientific graphing libraries (>50M downloads across supported languages)

Please include the link to this issue when contacting us to discuss.

@stackedsax
Copy link

This issue has been tagged with NEEDS SPON$OR

@jackparmer I just reached out to Rob to discuss what would be involved. We'd definitely like to have this fixed.

@alexcjohnson
Copy link
Collaborator

@selimonat not if they all use WebGL trace types and they're all rendered on the page simultaneously. You can use as many SVG plots as you want, but if you have lots of WebGL plots you'll need to either combine them as subplots or take some of them off the page - for example put them in separate dcc.Tab components so only a few are rendered at a time.

@CnrLwlss
Copy link

I've added a question about this on Stack Overflow, but please note that this is also problem when sharing webgl across subplots, not just when sharing webgl context across divs (as suggested in issue title).

https://stackoverflow.com/questions/76523820/how-can-i-plot-a-large-array-of-scatter3d-subplots-using-plotly

@alexcjohnson
Copy link
Collaborator

Thanks @CnrLwlss - I had forgotten about this part, but I think (correct me if I'm wrong @archmoj) it's just the 3D plots, which use the old stack.gl system rather than regl, that still create one WebGL context per subplot. If you do the same test using scattergl instead of scatter3d it works correctly with an arbitrary number of subplots.

@archmoj I guess as we're considering a full solution to this issue we'll have to figure out either porting 3D into regl or making the 3D subplots reuse regl's context before we can consider it done.

@ssalveMobiltex
Copy link

Even I am getting the webgl issue when using scattergl with 2 subplots on plotly js

@sebwills
Copy link

sebwills commented Aug 3, 2023

Just noting that I am hitting this in the use case of using plotly express in a jupyter notebook where I generate lots of plots.

@jarrisondev
Copy link

jarrisondev commented Oct 24, 2023

We are experiencing the same issue with the WebGL context. We are rendering multiple ScatterGL plots within a draggable Dash, so there is no way to use them as subplots. Furthermore, the map is a choroplethmapbox, and I am unsure of how many contexts it is using. The context issue arises as we continuously create, update, or manipulate the graphs

image

@Spikhalskiy
Copy link

Spikhalskiy commented Aug 1, 2024

To resolve the problem, a new plotly version and

<script src="https://unpkg.com/virtual-webgl@1.0.6/src/virtual-webgl.js"></script>

needs to be included in the page.

With Jupyter a cell

from IPython.display import display, HTML
js = "<script src=\"https://unpkg.com/virtual-webgl@1.0.6/src/virtual-webgl.js\"></script>"
display(HTML(js))

does the trick.

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

Successfully merging a pull request may close this issue.