Rendering WebGL image in headless chrome without a GPU

18,559

Solution 1

So i've partially solved the issue by setting premultipliedAlpha to false. When it's true (default), toDataURL would return an empty image. When false, it returns the rendered image.

<!DOCTYPE html>
<html>
<body>
  <canvas id="canvas" width="1080" height="1080"></canvas>
  <script type="text/javascript">
    var canvas = document.getElementById('canvas');
    var gl = canvas.getContext('webgl', {
        premultipliedAlpha: false
    });

    gl.viewportWidth = canvas.width;
    gl.viewportHeight = canvas.height;
    gl.clearColor(0.99, 0, 1, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);

    var IMAGE_PREFIX = 'data:image/png;base64,';
    var image = canvas.toDataURL('image/png').substring(IMAGE_PREFIX.length);

    // save(image)
  </script>
</body>
</html>

What is interesting is if I take a screenshot using puppeteer I can see the rendered image, regardless of whether premultipliedAlpha is true or false.

Solution 2

There's an open bug which affects systems without X11 libraries: crbug.com/swiftshader/79. It prevents Chrome OS from running with SwiftShader, but the same issue would also happen on a headless Linux system which has no X11 support.

Fortunately it should be feasible to install X11 and get things running. I'm not 100% sure which packages provide the necessary libraries, but try these: xorg xserver-xorg xvfb libx11-dev libxext-dev libxext-dev:i386

Eventually the SwiftShader bug will be fixed so it doesn't require X11 whatsoever.

Solution 3

I don't know if this can help you, but there are options you can set when creating a WebGL context. Depending on the browser implementation, you can have different default values.

Have you tried to force preserveDrawingBuffer to true ?

var gl = canvas.getContext( "webgl", {
    preserveDrawingBuffer: true
});

Here is what MDN says about this option:

preserveDrawingBuffer: If the value is true the buffers will not be cleared and will preserve their values until cleared or overwritten by the author.

Solution 4

If you want to run it on server and have no GPU available there, you need to use something instead of it.

WebGL 1.0 is based on OpenGL ES 2.0 spec, which is based on OpenGL 2.1 spec. There is Mesa library(https://en.wikipedia.org/wiki/Mesa_(computer_graphics)), which implements software renderer and is used for validation of OpenGL implementation by vendors. I think it supports OpenGL up to 3.1, but I can be wrong and now it supports version even highier.

It's possible to install Mesa as driver in *nix and have it do OpenGL rendering using software implementation.

I suggest to check accepted answer here: how to force chrome to use mesa software driver for webgl I'm pretty sure it will solve your issue

Share:
18,559
James Hollingworth
Author by

James Hollingworth

Updated on June 12, 2022

Comments

  • James Hollingworth
    James Hollingworth almost 2 years

    I'm trying to export an image rendered with WebGL on a linux server without a GPU. To do this I'm using headless Chrome however the exported image is black (example exported image, taking a screenshot of page shows its just canvas that is black). I was hoping for some help figuring out why this is happening.

    To export the image I render the image into a canvas, export data via canvas.toDataURL('image/jpeg') and then post the data to the server. I'm using Pixi.js for rendering, if I use canvas renderer then everything works on the server; It's WebGL rendering thats not working. It's worth noting the WebGL render works fine in Chrome 63 on a Macbook.

    To control Chrome I'm using Puppeteer. All I'm doing is opening a page, waiting a second, and then closing it again:

    puppeteer
      .launch({
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
        ],
      })
      .then(browser => {
        return browser.newPage().then(page => {
          return page
            .goto(url)
            .then(() => page.waitFor(1000))
            .then(() => browser.close())
            .catch(err => console.error('Failed', err));
        });
      })
    

    These are the arguments puppeteer passes to Chrome:

    [
      '--disable-background-networking',
      '--disable-background-timer-throttling',
      '--disable-client-side-phishing-detection',
      '--disable-default-apps',
      '--disable-extensions',
      '--disable-hang-monitor',
      '--disable-popup-blocking',
      '--disable-prompt-on-repost',
      '--disable-sync',
      '--disable-translate',
      '--metrics-recording-only',
      '--no-first-run',
      '--remote-debugging-port=0',
      '--safebrowsing-disable-auto-update',
      '--enable-automation',
      '--password-store=basic',
      '--use-mock-keychain',
      '--user-data-dir=/tmp/puppeteer_dev_profile-GhEAXZ',
      '--headless',
      '--disable-gpu',
      '--hide-scrollbars',
      '--mute-audio',
      '--no-sandbox',
      '--disable-setuid-sandbox'
    ]
    

    The swiftshader author said in June headless WebGL rendering is possible and it seems to be confirmed by this Chromium issue so I guess I'm missing something. Has anyone got any ideas what I'm doing wrong?

    A couple of things I've tried:

    • Not passing in --disable-gpu
    • --use-gl=swiftshader-webgl, --use-gl=swiftshader, --use-gl=osmesa
    • Taking a full screen screenshot to see if its just canvas. Whole screen is just black.

    Versions

    • Chrome: linux-515411
    • puppeteer: 0.13.0
    • node: 8.2.1
    • Linux: CentOS 7

    This is what I needed to install on my server to get chrome to run (Source)

    yum install cups-libs dbus-glib libXrandr libXcursor libXinerama cairo cairo-gobject pango ffmpeg
    rpm -ivh --nodeps http://mirror.centos.org/centos/7/os/x86_64/Packages/atk-2.22.0-3.el7.x86_64.rpm
    rpm -ivh --nodeps http://mirror.centos.org/centos/7/os/x86_64/Packages/at-spi2-atk-2.22.0-2.el7.x86_64.rpm
    rpm -ivh --nodeps http://mirror.centos.org/centos/7/os/x86_64/Packages/at-spi2-core-2.22.0-1.el7.x86_64.rpm
    rpm -ivh --nodeps http://dl.fedoraproject.org/pub/archive/fedora/linux/releases/20/Fedora/x86_64/os/Packages/g/GConf2-3.2.6-7.fc20.x86_64.rpm
    rpm -ivh --nodeps http://dl.fedoraproject.org/pub/archive/fedora/linux/releases/20/Fedora/x86_64/os/Packages/l/libXScrnSaver-1.2.2-6.fc20.x86_64.rpm
    rpm -ivh --nodeps http://dl.fedoraproject.org/pub/archive/fedora/linux/releases/20/Fedora/x86_64/os/Packages/l/libxkbcommon-0.3.1-1.fc20.x86_64.rpm
    rpm -ivh --nodeps http://dl.fedoraproject.org/pub/archive/fedora/linux/releases/20/Fedora/x86_64/os/Packages/l/libwayland-client-1.2.0-3.fc20.x86_64.rpm
    rpm -ivh --nodeps http://dl.fedoraproject.org/pub/archive/fedora/linux/releases/20/Fedora/x86_64/os/Packages/l/libwayland-cursor-1.2.0-3.fc20.x86_64.rpm
    rpm -ivh --nodeps http://dl.fedoraproject.org/pub/archive/fedora/linux/releases/20/Fedora/x86_64/os/Packages/g/gtk3-3.10.4-1.fc20.x86_64.rpm
    rpm -ivh --nodeps http://dl.fedoraproject.org/pub/archive/fedora/linux/releases/16/Fedora/x86_64/os/Packages/gdk-pixbuf2-2.24.0-1.fc16.x86_64.rpm