8

I'm currently working on a website with a list of items. Each item has a thumbnail, and I'm adding a shader effect on all of them using PixiJS. The problem is that there's more than 16 items on the list, so I'm getting the following error :

WARNING: Too many active WebGL contexts. Oldest context will be lost.

Is there a way to increase this limit ? I can't make the whole page on WebGL, and the usage is so limited (no interaction, lite effect) that I think more WebGL contexts will not make the page laggy or something.

Treast
  • 1,095
  • 1
  • 9
  • 20

2 Answers2

19

Not it is not possible to increase the limit. (well you could write your own browser).

To make a list of items you can use solutions like in this Q&A

Multiple WebGL models on the same page

which are detailed in this article for WebGL and this article for three.js

Here are 3 solutions.

  1. (fastest) Use a single WebGL canvas that covers the page. Use place holder elements to mark where you want to draw things. Loop through those elements calling element.getBoundingClientRect and use the viewport and scissor settings to draw in those places, only drawing the ones that are visible (some may be offscreen and don't need to be drawn). This is the soution shown in the links above.

  2. Use a single offscreen WebGL canvas. Put 2D canvases in your page. Draw each item to the offscreen canvas and use drawImage to draw it to the correct 2D canvas. this solution is slightly more flexible since the 2D canvas elements can be more freely styled but it's slower than the previous solution and uses more memory.

    Note: it's probably best to make the WebGL canvas the size of the largest 2D canvas, then for each 2D canvas, set gl.viewport to the size of that 2D canvas and then use the full form of drawImage to select a portion of the WebGL the correct size portion of the WebGL canvas to draw the current 2D canvas. Resizing a canvas is a heavy operation I believe. In other words something like:

    for each 2D canvas
       webgl canvas size = max(webgl canvas size, 2D canvas size) // for each dimension
       gl.viewport(0, 0, 2D canvas size);
       draw scene for canvas
       ctx.drawImage(
           0, 0, 2D canvas size, 
           0, webgl canvas height - 2d canvas height, 2D canvas size)
    
  3. Use a virtual webgl context which you can implement on your own or use a library. Not recommended (slowest) but it is a quick solution.

Note: having multiple contexts is not a recommended solution. Textures, vertices, and shaders can not be shared between WebGL contexts. That means if you use the same image in 2 or more contexts it has to be loaded into memory once for each context. Similarly for each context a shader is used it has to be compliled and linked for each context. In other words using multiple contexts uses more memory and increases startup time significantly.

Unfortunately since you tagged your questions both WebGL and pixi.js this answer is probably irrelevant to you. I have no idea if this is possible in pixi.js. I see no documentation to suggest how to do it efficiently.

gman
  • 100,619
  • 31
  • 269
  • 393
  • 1
    4. Assuming not all of them are visible at the same time (you have to scroll), you could have a few contexts and swap between them (same principle as virtualized lists). – XCS Dec 03 '19 at 01:05
  • That sounds a like a slow solution. Shaders and resources can not be shared so each time you swap you have to compile the correct shaders and load the correct resources into that context. – gman Dec 03 '19 at 01:15
  • Thank you for your answer. I choose the solution n°2, and it works fine. I've still have some stuff to do, in order to limit the texture size to 4096x4096. – Treast Dec 03 '19 at 09:52
  • 2
    @gman You don't have to actually swap contexts, you can re-use canvases and just move them around, same as with virtualized lists. This solution might work if you have a long scrolling list, but depends on his content/layout design. I wouldn't recommend this solution, it was just a possible alternative. – XCS Dec 03 '19 at 13:58
  • @xcs could you expand on how to move around canvases ? – Aerodynamic Nov 19 '20 at 09:57
  • 1
    @Aerodynamic The same way virtualized lists work. If you have a list with 20 rows, but only 4 of them are visible in the viewport at one point, you could just have canvases for 4 rows (+1 above and 1 below). When you scroll down, you just move the first row (that was hidden) below the last row and draw on it the new thumbnails. See this image reference: https://webdev.imgix.net/virtualize-long-lists-react-window/difference-in-scrolling.jpg – XCS Nov 19 '20 at 11:25
  • 1
    @xcs, That won't work though because you can't arbitrarily move resources between contexts. You have canvases A(cube),B(sphere),C(prism), When A scrolls off you put it at the bottom now have B(sphere),C(prism),A(torus), A now needs to put whatever content you want in that canvas (the torus) but it's already got a context with state for a cube still in it. – gman Nov 19 '20 at 12:02
  • @gman Yes, the context is there, but you can issue a new draw call to draw the torus, right? – XCS Nov 20 '20 at 13:48
  • @XCS, You have to compile the shader for the torus, upload the data for the torus, etc. And unless you magically always get the same canvas for the same objects you'll need to do it for all canvas wasting memory and making things slow. You'll end up having to compile the shaders and upload the data for the torus once for every canvas. and further you're still limited to the number of canvases. For example if I run [this example](https://threejs.org/examples/webgl_multiple_elements.html) there are 18 areas, scroll down 1/2 and there are 24 visible, that's well beyond what most browsers support. – gman Nov 20 '20 at 17:14
  • @gman I think in the question he said he only uses one shader: "I'm adding a shader effect on all of them using PixiJS". So only the texture uploading chanes. Anyway, the best solution is to just render everything in one big canvas I guess. – XCS Nov 20 '20 at 19:22
4

There is an alternative solution in case you control the browser being used. Chrome has the following command line switch to control the maximum amount of active contexts.

--max-active-webgl-contexts=<number>

You can setup a shortcut with this argument to have a Chrome browser that's less limited.

Robert Massa
  • 4,345
  • 1
  • 32
  • 44