From f71a5e60a25016f7ab9df055093d214d793f6f1d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 31 Jul 2017 20:58:04 +0900 Subject: [PATCH] Allow to choose libraries as alternatives of ANGLE This change effectively enables to use Mesa instead of ANGLE. --- README.md | 40 ++++++++++++- binding.gyp | 153 ++++++++++++++++++++++++++---------------------- src/bindings.cc | 1 + src/webgl.cc | 49 ++++++++++------ src/webgl.h | 4 ++ webgl.js | 15 ++--- 6 files changed, 164 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 8c7b7f3d..dd9223a8 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,8 @@ Immediately destroys the context and all associated resources. In most cases installing `headless-gl` from npm should just work. However, if you run into problems you might need to adjust your system configuration and make sure all your dependencies are up to date. For general information on building native modules, see the [`node-gyp`](https://github.com/nodejs/node-gyp) documentation. +`headless-gl` includes [ANGLE](https://github.com/google/angle) as an OpenGL ES 2 implementation with EGL interface. You may choose another library in your system. + #### Mac OS X * [Python 2.7](https://www.python.org/) @@ -144,6 +146,17 @@ $ sudo apt-get install -y build-essential libxi-dev libglu1-mesa-dev libglew-dev * d3dcompiler_47.dll should be in c:\windows\system32, but if isn't then you can find another copy in the deps/ folder * (optional) A modern nodejs supporting es6 to run some examples https://iojs.org/en/es6.html +#### Using a library other than ANGLE +In some cases, ANGLE is not available while other libraries are. For example, Unix-like operating systems except GNU/Linux and Android does have [the Mesa 3D Graphics Library](https://www.mesa3d.org/opengles.html) even though they are not supported by ANGLE. + +The Mesa 3D Graphics Library also supports Wayland or surfaceless platforms. + +`headless-gl` lets you choose another library with [`pkg-config`](https://www.freedesktop.org/wiki/Software/pkg-config/). Prepare `egl` and `glesv2` package for `pkg-config`, and set the execution command of `pkg-config` to `npm_config_headless_gl_pkg_config`. Typically you can install `headless-gl` with libraries in your system by typing the following command: + +``` +npm_config_headless_gl_pkg_config=pkg-config npm install headless-gl +``` + ## FAQ ### How can I use headless-gl with a continuous integration service? @@ -177,10 +190,31 @@ If you know of a service not listed here, open an issue I'll add it to the list. ### How can `headless-gl` be used on a headless Linux machine? -If you are running your own minimal Linux server, such as the one one would want to use on Amazon AWS or equivalent, it will likely not provide an X11 nor an OpenGL environment. To setup such an environment you can use those two packages: +If you are running your own minimal Linux server, such as the one one would want to use on Amazon AWS or equivalent, it will likely not provide an X11 nor an OpenGL environment. To setup such an environment you can choose one of the following configurations: + +1. Use [Mesa](http://www.mesa3d.org/intro.html) with surfaceless context +2. Use [ANGLE](https://github.com/google/angle) included in `headless-gl`, Mesa and Xvfb + +#### Use [Mesa](http://www.mesa3d.org/intro.html) with its surfaceless context +Mesa provides what you need to setup headless OpenGL environment, and does not require additional dependencies with notable exceptions of drivers, if your server has GPUs and you are going to utilize them. + +It does not support `ANGLE_instanced_arrays` extension. Check whether your application uses the extension before setup. + +Unfortunately, its EGL frontend does not support `swrast` (its software +renderer), but there is an attempt to get it work. See +[101397 – [EGL] Surfaceless lacks swrast support](https://bugs.freedesktop.org/show_bug.cgi?id=101397). + +To use it without X11 server or Wayland, you need surfaceless platform support of its EGL interface. It may not be available in your distribution because it is relatively new (merged in 2015). It could even have been disabled in your distribution because it is not necessary for desktops and. In those cases, build it by yourself by following [the official instruction](https://www.mesa3d.org/egl.html). Do not forget to enable OpenGL ES 2, EGL and surfaceless platform. + +To use the installed Mesa for this purpose, follow the instruction of _Using a library other than ANGLE_ section. + +#### Use [ANGLE](https://github.com/google/angle) included in `headless-gl`, Mesa and Xvfb +ANGLE can use Mesa as its backend with [GLX](https://dri.freedesktop.org/wiki/GLX/). GLX requires X11 server, and Xvfb is a X11 server suitable for headless servers. + +While `ANGLE_instanced_arrays` extension is not supported by the OpenGL implementation of Mesa, ANGLE supports it even if the backend is Mesa. -1. [Xvfb](https://en.wikipedia.org/wiki/Xvfb) is a lightweight X11 server which provides a back buffer for displaying X11 application offscreen and reading back the pixels which were drawn offscreen. It is typically used in Continuous Integration systems. It can be installed on CentOS with `yum install -y Xvfb`, and comes preinstalled on Ubuntu. -2. [Mesa](http://www.mesa3d.org/intro.html) is the reference open source software implementation of OpenGL. It can be installed on CentOS with `yum install -y mesa-dri-drivers`, or `apt-get install libgl1-mesa-dev`. Since a cloud Linux instance will typically run on a machine that does not have a GPU, a software implementation of OpenGL will be required. +1. Mesa can be installed on CentOS with `yum install -y mesa-dri-drivers`, or `apt-get install libgl1-mesa-dev` on Debian. Since a cloud Linux instance will typically run on a machine that does not have a GPU, a software implementation of OpenGL will be required. +2. [Xvfb](https://en.wikipedia.org/wiki/Xvfb) provides a back buffer for displaying X11 application offscreen and reading back the pixels which were drawn offscreen. It is typically used in Continuous Integration systems. It can be installed on CentOS with `yum install -y Xvfb`, and comes preinstalled on Ubuntu. Interacting with `Xvfb` requires you to start it on the background and to execute your `node` program with the DISPLAY environment variable set to whatever was configured when running Xvfb (the default being :99). If you want to do that reliably you'll have to start Xvfb from an init.d script at boot time, which is extra configuration burden. Fortunately there is a wrapper script shipped with Xvfb known as `xvfb-run` which can start Xvfb on the fly, execute your node program and finally shut Xvfb down. Here's how to run it: diff --git a/binding.gyp b/binding.gyp index 65384ed9..6131450e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -1,6 +1,7 @@ { 'variables': { 'platform': '<(OS)', + 'headless_gl_pkg_config': '', }, 'conditions': [ ['platform == "mac"', {'variables': {'platform': 'darwin'}}], @@ -26,79 +27,93 @@ '<(module_root_dir)/deps/<(platform)' ], 'conditions': [ - ['OS=="mac"', { - 'dependencies': - [ - 'angle/src/angle.gyp:libEGL', - 'angle/src/angle.gyp:libGLESv2' - ], - 'libraries': [ - '-framework QuartzCore', - '-framework Quartz' - ], - }], - ['OS=="linux"', { - 'dependencies': - [ - 'angle/src/angle.gyp:libEGL', - 'angle/src/angle.gyp:libGLESv2' - ] - }], - ['OS=="win"', { - 'library_dirs': [ - '<(module_root_dir)/deps/windows/lib/<(target_arch)', - ], - 'libraries': [ - 'libEGL.lib', - 'libGLESv2.lib' - ], - 'defines' : [ - 'WIN32_LEAN_AND_MEAN', - 'VC_EXTRALEAN' - ], - 'configurations': { - 'Release': { - 'msvs_settings': { - 'VCCLCompilerTool': { - 'RuntimeLibrary': 0, # static release - 'Optimization': 0, # /Od, disabled - 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size - 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible - 'WholeProgramOptimization': 'false', # No - 'OmitFramePointers': 'true', - 'EnableFunctionLevelLinking': 'true', - 'EnableIntrinsicFunctions': 'true', - 'RuntimeTypeInfo': 'false', - 'ExceptionHandling': '0', - 'AdditionalOptions': [ - '/MP', # compile across multiple CPUs - ] - }, - 'VCLinkerTool': { - 'LinkTimeCodeGeneration': 0, # Link Time Code generation default - 'OptimizeReferences': 1, # /OPT:NOREF - 'EnableCOMDATFolding': 1, # /OPT:NOICF - 'LinkIncremental': 2, # /INCREMENTAL + ['"<(headless_gl_pkg_config)"==""', { + 'conditions': [ + ['OS=="mac"', { + 'dependencies': + [ + 'angle/src/angle.gyp:libEGL', + 'angle/src/angle.gyp:libGLESv2' + ], + 'libraries': [ + '-framework QuartzCore', + '-framework Quartz' + ], + }], + ['OS=="linux"', { + 'dependencies': + [ + 'angle/src/angle.gyp:libEGL', + 'angle/src/angle.gyp:libGLESv2' + ] + }], + ['OS=="win"', { + 'library_dirs': [ + '<(module_root_dir)/deps/windows/lib/<(target_arch)', + ], + 'libraries': [ + 'libEGL.lib', + 'libGLESv2.lib' + ], + 'defines' : [ + 'WIN32_LEAN_AND_MEAN', + 'VC_EXTRALEAN' + ], + 'configurations': { + 'Release': { + 'msvs_settings': { + 'VCCLCompilerTool': { + 'RuntimeLibrary': 0, # static release + 'Optimization': 0, # /Od, disabled + 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size + 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible + 'WholeProgramOptimization': 'false', # No + 'OmitFramePointers': 'true', + 'EnableFunctionLevelLinking': 'true', + 'EnableIntrinsicFunctions': 'true', + 'RuntimeTypeInfo': 'false', + 'ExceptionHandling': '0', + 'AdditionalOptions': [ + '/MP', # compile across multiple CPUs + ] + }, + 'VCLinkerTool': { + 'LinkTimeCodeGeneration': 0, # Link Time Code generation default + 'OptimizeReferences': 1, # /OPT:NOREF + 'EnableCOMDATFolding': 1, # /OPT:NOICF + 'LinkIncremental': 2, # /INCREMENTAL + } + }, + 'msvs_configuration_attributes': + { + 'OutputDirectory': '$(SolutionDir)$(ConfigurationName)', + 'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)' + } } }, - 'msvs_configuration_attributes': - { - 'OutputDirectory': '$(SolutionDir)$(ConfigurationName)', - 'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)' - } - } - }, - "copies": [ - { - 'destination': '$(SolutionDir)$(ConfigurationName)', - 'files': [ - '<(module_root_dir)/deps/windows/dll/<(target_arch)/libEGL.dll', - '<(module_root_dir)/deps/windows/dll/<(target_arch)/libGLESv2.dll' + "copies": [ + { + 'destination': '$(SolutionDir)$(ConfigurationName)', + 'files': [ + '<(module_root_dir)/deps/windows/dll/<(target_arch)/libEGL.dll', + '<(module_root_dir)/deps/windows/dll/<(target_arch)/libGLESv2.dll' + ] + } ] } - ] - } - ] + ] + ] + }, { + 'cflags': [ + '( + glGetString(GL_EXTENSIONS)); + + return strstr(extensions, "GL_ANGLE_instanced_arrays"); +} + void WebGLRenderingContext::dispose() { //Unregister context unregisterContext(); @@ -2037,11 +2036,21 @@ GL_METHOD(GetVertexAttrib) { GL_METHOD(GetSupportedExtensions) { GL_BOILERPLATE; - const char *extensions = reinterpret_cast( - (inst->glGetString)(GL_EXTENSIONS)); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::EscapableHandleScope scope(isolate); + v8::Local array; - info.GetReturnValue().Set( - Nan::New(extensions).ToLocalChecked()); + if ((inst->checkAngleInstancedArraysSupportInternal)()) { + array = v8::Array::New(isolate, 3); + Nan::Set(array, 2, Nan::New("ANGLE_instanced_arrays").ToLocalChecked()); + } else { + array = v8::Array::New(isolate, 2); + } + + Nan::Set(array, 0, Nan::New("STACKGL_resize_drawingbuffer").ToLocalChecked()); + Nan::Set(array, 1, Nan::New("STACKGL_destroy_context").ToLocalChecked()); + + info.GetReturnValue().Set(scope.Escape(array)); } GL_METHOD(GetExtension) { @@ -2050,6 +2059,12 @@ GL_METHOD(GetExtension) { //TODO } +GL_METHOD(CheckAngleInstancedArraysSupport) { + GL_BOILERPLATE; + + info.GetReturnValue().Set(Nan::New((inst->checkAngleInstancedArraysSupportInternal)())); +} + GL_METHOD(CheckFramebufferStatus) { GL_BOILERPLATE; diff --git a/src/webgl.h b/src/webgl.h index 6299e897..f3e97d32 100644 --- a/src/webgl.h +++ b/src/webgl.h @@ -119,6 +119,9 @@ struct WebGLRenderingContext : public node::ObjectWrap { //Preferred depth format GLenum preferredDepth; + //Extension Check + bool checkAngleInstancedArraysSupportInternal(); + //Destructors void dispose(); @@ -258,6 +261,7 @@ struct WebGLRenderingContext : public node::ObjectWrap { static NAN_METHOD(GetVertexAttrib); static NAN_METHOD(GetSupportedExtensions); static NAN_METHOD(GetExtension); + static NAN_METHOD(CheckAngleInstancedArraysSupport); static NAN_METHOD(CheckFramebufferStatus); static NAN_METHOD(FrontFace); diff --git a/webgl.js b/webgl.js index 02fb9466..668e3e78 100644 --- a/webgl.js +++ b/webgl.js @@ -666,14 +666,6 @@ gl.getContextAttributes = function () { return this._contextattributes } -gl.getSupportedExtensions = function getSupportedExtensions () { - return [ - 'ANGLE_instanced_arrays', - 'STACKGL_resize_drawingbuffer', - 'STACKGL_destroy_context' - ] -} - function createANGLEInstancedArrays (context) { function checkInstancedVertexAttribState (context, maxIndex, primCount) { var program = context._activeProgram @@ -874,6 +866,9 @@ function createANGLEInstancedArrays (context) { return result } +var _checkAngleInstancedArraysSupport = gl.checkAngleInstancedArraysSupport; +delete gl.checkAngleInstancedArraysSupport; + gl.getExtension = function getExtension (name) { var str = name.toLowerCase() if (str in this._extensions) { @@ -882,7 +877,9 @@ gl.getExtension = function getExtension (name) { var ext = null switch (str) { case 'angle_instanced_arrays': - ext = createANGLEInstancedArrays(this) + if (_checkAngleInstancedArraysSupport.call(this)) { + ext = createANGLEInstancedArrays(this) + } break case 'stackgl_destroy_context': ext = new STACKGL_destroy_context()