在没有 GPU 的情况下以无头 chrome 渲染 WebGL 图像

Rendering WebGL image in headless chrome without a GPU

我正在尝试在没有 GPU 的 linux 服务器上导出使用 WebGL 渲染的图像。为此,我使用无头 Chrome,但导出的图像是黑色的 (example exported image, taking a screenshot of page shows its just canvas that is black)。我希望得到一些帮助来弄清楚为什么会这样。

为了导出图像,我将图像渲染成 canvas,通过 canvas.toDataURL('image/jpeg') 导出数据,然后 post 将数据导出到服务器。我正在使用 Pixi.js 进行渲染,如果我使用 canvas 渲染器那么一切都在服务器上工作; WebGL 渲染不起作用。值得注意的是,WebGL 渲染在 Chrome 63 在 Macbook 上运行良好。

为了控制Chrome,我正在使用Puppeteer。我所做的只是打开一个页面,等待一秒钟,然后再次关闭它:

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));
    });
  })

这些是 puppeteer 传递给 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'
]

swiftshader author said in June headless WebGL rendering is possible and it seems to be confirmed by this Chromium issue 所以我想我遗漏了什么。有没有人知道我做错了什么?

我试过的一些东西:

版本

这就是我需要在我的服务器上安装才能使 chrome 到 运行 (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

有一个未解决的错误会影响没有 X11 库的系统:crbug.com/swiftshader/79。它阻止 Chrome OS 来自 运行 SwiftShader,但同样的问题也会发生在没有 X11 支持的无头 Linux 系统上。

幸好安装X11,搞定东西应该是可行的运行。我不是 100% 确定哪些包提供了必要的库,但试试这些:xorg xserver-xorg xvfb libx11-dev libxext-dev libxext-dev:i386

最终将修复 SwiftShader 错误,因此它不需要任何 X11。

我不知道这是否对您有帮助,但是您可以在创建 WebGL 上下文时设置一些选项。根据浏览器的实现,您可以有不同的默认值。

您是否尝试过将 preserveDrawingBuffer 强制设置为 true

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

以下是 MDN 对这个选项的描述:

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

如果你想运行它在服务器上并且那里没有可用的 GPU,你需要使用一些东西来代替它。

WebGL 1.0 基于 OpenGL ES 2.0 规范,后者基于 OpenGL 2.1 规范。有 Mesa 库(https://en.wikipedia.org/wiki/Mesa_(computer_graphics)),它实现了软件渲染器,并用于供应商验证 OpenGL 实现。我认为它支持 OpenGL 到 3.1,但我可能是错的,现在它支持更高的版本。

可以在 *nix 中安装 Mesa 作为驱动程序,并让它使用软件实现进行 OpenGL 渲染。

我建议在这里检查接受的答案:how to force chrome to use mesa software driver for webgl我很确定它会解决你的问题

所以我通过将 premultipliedAlpha 设置为 false 部分解决了这个问题。当它为真(默认)时,toDataURL 将 return 一个空图像。当为 false 时,它​​ returns 渲染图像。

<!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>

有趣的是,如果我使用 puppeteer 截屏,我可以看到渲染图像,无论 premultipliedAlpha 是真还是假。

preserveDrawingBuffer 设置为 true 解决了我的问题

我通过添加

解决了这个问题
'--use-gl=angle' 

到无头 chrome 设置。 我还删除了 '--disable-gpu',因为我认为这是修复一些错误的临时解决方法,不再需要。

您可以在 Chrome 源代码中找到与 'angle' here 不同的选项(我最不希望在 Chrome 源代码中找到它!)