skip to Main Content

I want to draw on canvases (more than 100) and have their content preserved. My setup is that I create canvases in a loop, and for each one I get the webgl2 context of it and do some drawing. The problem is, that after 16 canvases, when i get the webgl2 context from the 17th, the first one shows a missing image logo

enter image description here

I first thought that I should gracefully free up and release the context, but after googling a while, my only luck was to find some hack to call loseContext() on an extension, which ended in making the canvas look like the one in the image right after the call.

I also tried deleting all the resources used in the context (like buffers, programs and etc.) but no luck there either.

So, how do I achieve that? Is it possible at all?

2

Answers


  1. Based on WebGL Fundamentals most browsers support only 8 webgl contexts simultaneously.

    Chromium had a registered request to make a number of contexts adjustable. They also stated that it is not easy to make this value configurable because the actual contexts are tightly connected with hardware and, if exceeded, can make the video driver stop.

    However, the new config value --max-active-webgl-contexts=32 was introduced, and the official default value for Chromium is 16.

    There is also a project that allows virtualizing WebGL contexts, but it is not clear what impact its usage may have.

    To summarize:

    1. The number of contexts is browser and hardware dependent
    2. It cannot be changed via JS
    3. In most scenarios, it is assumed that only 1 context is used

    Please let me know if this helps.

    Login or Signup to reply.
  2. The simple answer is that each browser limits the amount of active (webgl) contexts. As you found out yourself, with Chromium-based browsers the limit is 16, while in Firefox it’s 300 for example.
    If you exceed this limit, the browser will start throwing away the oldest contexts, what means that it frees up all resources related to that particular context, including the backbuffer used to store it’s pixeldata. On Chrome you’ll get a warning like:

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

    Unfortunately the amount of contexts can’t be changed. What you can do depends on your definition of preserving content. If it means you want to backup the contents of it’s backbuffer, you can workaround like this:

    1. While creating the canvases and it’s contexts keep a counter of active contexts and store a reference to those inside an array e.g. canvases=[]
    2. If the number of active contexts exceeds a magic number – let’s say 16 – get the context 16 elements before the current from the canvases array
    3. Create a new canvas with a 2d context and copy the pixeldata from the webgl canvas using drawImage()
    4. Using the WEBGL_lose_context extension, get rid of the old webgl context
    5. Wait until it emits the webglcontextlost event, so you know it’s save to create a new webgl context
    6. Replace the old canvas with the webgl context by the just copied clone

    Here’s an example of generating 64 webgl contexts in the aforementioned way:

    const MAX_CONTEXTS = 16;
    
    (async() => {
      let canvas, context;
      let width = 40;
      let height = 40;
      let canvases = [];
      let oldCanvas;
      for (let a = 0; a < 64; a++) {
        if (canvases.length >= MAX_CONTEXTS) {
          oldCanvas = canvases[canvases.length - MAX_CONTEXTS];
          canvas = document.createElement("canvas");
          context = canvas.getContext("2d");
          canvas.width = width;
          canvas.height = height;
          context.drawImage(oldCanvas.canvas, 0, 0);
    
          oldCanvas.context.getExtension("WEBGL_lose_context").loseContext();
          await new Promise(resolve => {
            oldCanvas.canvas.addEventListener('webglcontextlost', resolve);
          });
          document.body.insertBefore(canvas, oldCanvas.canvas);
          canvas.id = oldCanvas.canvas.id;
          document.body.removeChild(oldCanvas.canvas);
        }
        canvas = document.createElement("canvas");
        canvas.id = a;
        canvas.width = width;
        canvas.height = height;
        context = canvas.getContext("webgl", {
          preserveDrawingBuffer: true
        });
        context.viewport(0, 0, context.drawingBufferWidth, context.drawingBufferHeight);
        context.clearColor(Math.random(), Math.random(), Math.random(), 1.0);
        context.clear(context.COLOR_BUFFER_BIT);
        document.body.appendChild(canvas);
        canvases.push({
          canvas: canvas,
          context: context
        });
      }
    })();
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search