Skip to content

Commit

Permalink
Allow to choose libraries as alternatives of ANGLE
Browse files Browse the repository at this point in the history
This change effectively enables to use Mesa instead of ANGLE.
  • Loading branch information
akihikodaki committed Aug 1, 2017
1 parent 74df983 commit bb74a64
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 98 deletions.
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/)
Expand All @@ -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?
Expand Down Expand Up @@ -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:

Expand Down
153 changes: 84 additions & 69 deletions binding.gyp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
'variables': {
'platform': '<(OS)',
'headless_gl_pkg_config': '',
},
'conditions': [
['platform == "mac"', {'variables': {'platform': 'darwin'}}],
Expand All @@ -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': [
'<!@(<(headless_gl_pkg_config) --cflags egl glesv2)',
],
'ldflags': [
'<!@(<(headless_gl_pkg_config) --libs-only-L --libs-only-other egl glesv2)',
],
'libraries': [
'<!@(<(headless_gl_pkg_config) --libs-only-l egl glesv2)',
]
}],
]
}
]
Expand Down
1 change: 1 addition & 0 deletions src/bindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ NAN_MODULE_INIT(Init) {
JS_GL_METHOD("getVertexAttrib", GetVertexAttrib);
JS_GL_METHOD("getSupportedExtensions", GetSupportedExtensions);
JS_GL_METHOD("getExtension", GetExtension);
JS_GL_METHOD("checkAngleInstancedArraysSupport", CheckAngleInstancedArraysSupport);
JS_GL_METHOD("checkFramebufferStatus", CheckFramebufferStatus);
JS_GL_METHOD("getShaderPrecisionFormat", GetShaderPrecisionFormat);
JS_GL_METHOD("frontFace", FrontFace);
Expand Down
49 changes: 32 additions & 17 deletions src/webgl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ EGLDisplay WebGLRenderingContext::DISPLAY;
WebGLRenderingContext* WebGLRenderingContext::ACTIVE = NULL;
WebGLRenderingContext* WebGLRenderingContext::CONTEXT_LIST_HEAD = NULL;

const char* REQUIRED_EXTENSIONS[] = {
"GL_OES_packed_depth_stencil",
"GL_ANGLE_instanced_arrays",
NULL
};

#define GL_METHOD(method_name) NAN_METHOD(WebGLRenderingContext:: method_name)

#define GL_BOILERPLATE \
Expand Down Expand Up @@ -128,13 +122,11 @@ WebGLRenderingContext::WebGLRenderingContext(
//Check extensions
const char *extensionString = (const char*)((glGetString)(GL_EXTENSIONS));

//Load required extensions
for(const char** rext = REQUIRED_EXTENSIONS; *rext; ++rext) {
if(!strstr(extensionString, *rext)) {
dispose();
state = GLCONTEXT_STATE_ERROR;
return;
}
//Load a required extension: OES_packed_depth_stencil
if(!strstr(extensionString, "GL_OES_packed_depth_stencil")) {
dispose();
state = GLCONTEXT_STATE_ERROR;
return;
}

//Select best preferred depth
Expand Down Expand Up @@ -171,6 +163,13 @@ void WebGLRenderingContext::setError(GLenum error) {
}
}

bool WebGLRenderingContext::checkAngleInstancedArraysSupportInternal() {
const char *extensions = reinterpret_cast<const char*>(
glGetString(GL_EXTENSIONS));

return strstr(extensions, "GL_ANGLE_instanced_arrays");
}

void WebGLRenderingContext::dispose() {
//Unregister context
unregisterContext();
Expand Down Expand Up @@ -2037,11 +2036,21 @@ GL_METHOD(GetVertexAttrib) {
GL_METHOD(GetSupportedExtensions) {
GL_BOILERPLATE;

const char *extensions = reinterpret_cast<const char*>(
(inst->glGetString)(GL_EXTENSIONS));
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::EscapableHandleScope scope(isolate);
v8::Local<v8::Array> array;

info.GetReturnValue().Set(
Nan::New<v8::String>(extensions).ToLocalChecked());
if ((inst->checkAngleInstancedArraysSupportInternal)()) {
array = v8::Array::New(isolate, 3);
Nan::Set(array, 2, Nan::New<v8::String>("ANGLE_instanced_arrays").ToLocalChecked());
} else {
array = v8::Array::New(isolate, 2);
}

Nan::Set(array, 0, Nan::New<v8::String>("STACKGL_resize_drawingbuffer").ToLocalChecked());
Nan::Set(array, 1, Nan::New<v8::String>("STACKGL_destroy_context").ToLocalChecked());

info.GetReturnValue().Set(scope.Escape(array));
}

GL_METHOD(GetExtension) {
Expand All @@ -2050,6 +2059,12 @@ GL_METHOD(GetExtension) {
//TODO
}

GL_METHOD(CheckAngleInstancedArraysSupport) {
GL_BOILERPLATE;

info.GetReturnValue().Set(Nan::New<v8::Boolean>((inst->checkAngleInstancedArraysSupportInternal)()));
}

GL_METHOD(CheckFramebufferStatus) {
GL_BOILERPLATE;

Expand Down
4 changes: 4 additions & 0 deletions src/webgl.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ struct WebGLRenderingContext : public node::ObjectWrap {
//Preferred depth format
GLenum preferredDepth;

//Extension Check
bool checkAngleInstancedArraysSupportInternal();

//Destructors
void dispose();

Expand Down Expand Up @@ -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);
Expand Down
15 changes: 6 additions & 9 deletions webgl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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()
Expand Down

0 comments on commit bb74a64

Please sign in to comment.