Browse Source

Platform: port WindowlessEglApplication to Emscripten.

Makes it finally possible to have magnum-info and GL tests.
pull/217/head
Vladimír Vondruš 9 years ago
parent
commit
fda77b8965
  1. 9
      CMakeLists.txt
  2. 2
      doc/changelog.dox
  3. 1
      package/archlinux/PKGBUILD-emscripten
  4. 1
      package/archlinux/PKGBUILD-emscripten-noopt
  5. 1
      package/archlinux/PKGBUILD-emscripten-noopt-webgl2
  6. 1
      package/archlinux/PKGBUILD-emscripten-wasm
  7. 1
      package/archlinux/PKGBUILD-emscripten-wasm-webgl2
  8. 1
      package/archlinux/PKGBUILD-emscripten-webgl2
  9. 1
      package/ci/travis-emscripten.sh
  10. 19
      src/Magnum/Platform/CMakeLists.txt
  11. 14
      src/Magnum/Platform/WebApplication.css
  12. 22
      src/Magnum/Platform/WindowlessEglApplication.cpp
  13. 100
      src/Magnum/Platform/WindowlessEglApplication.h
  14. 46
      src/Magnum/Platform/WindowlessEmscriptenApplication.js

9
CMakeLists.txt

@ -99,12 +99,9 @@ cmake_dependent_option(WITH_TEXTURETOOLS "Build TextureTools library" ON "NOT WI
# Magnum AL Info
cmake_dependent_option(WITH_AL_INFO "Build magnum-al-info utility" OFF "WITH_AUDIO" OFF)
# EGL context and windowless EGL application, available everywhere except on
# platforms which don't support extension loading
if(NOT CORRADE_TARGET_EMSCRIPTEN)
cmake_dependent_option(WITH_WINDOWLESSEGLAPPLICATION "Build WindowlessEglApplication library" OFF "NOT TARGET_GLES OR TARGET_DESKTOP_GLES OR NOT WITH_MAGNUMINFO" ON)
option(WITH_EGLCONTEXT "Build EglContext library" OFF)
endif()
# EGL context and windowless EGL application, available everywhere
cmake_dependent_option(WITH_WINDOWLESSEGLAPPLICATION "Build WindowlessEglApplication library" OFF "NOT TARGET_GLES OR TARGET_DESKTOP_GLES OR NOT WITH_MAGNUMINFO" ON)
option(WITH_EGLCONTEXT "Build EglContext library" OFF)
# Android-specific application libraries
if(CORRADE_TARGET_ANDROID)

2
doc/changelog.dox

@ -221,6 +221,8 @@ namespace Magnum {
@ref Platform::GlfwApplication
- Added @ref Platform::WindowlessWindowsEglApplication and
@ref Platform::WindowlessIosApplication for ANGLE and iOS
- New @ref Platform::WindowlessEglApplication that works on headless NVidia,
Mesa drivers and Emscripten
- New @ref Platform::WindowlessGlxContext "Platform::Windowless*Context"
classes that manage OpenGL context to make threaded context creation
possible

1
package/archlinux/PKGBUILD-emscripten

@ -30,6 +30,7 @@ build() {
-DCMAKE_INSTALL_PREFIX=/usr/lib/emscripten/system \
-DWITH_AUDIO=ON \
-DWITH_SDL2APPLICATION=ON \
-WDITH_WINDOWLESSEGLAPPLICATION=ON \
-DWITH_MAGNUMFONT=ON \
-DWITH_OBJIMPORTER=ON \
-DWITH_TGAIMAGECONVERTER=ON \

1
package/archlinux/PKGBUILD-emscripten-noopt

@ -32,6 +32,7 @@ build() {
-DCMAKE_EXE_LINKER_FLAGS_RELEASE="-O1" \
-DWITH_AUDIO=ON \
-DWITH_SDL2APPLICATION=ON \
-WDITH_WINDOWLESSEGLAPPLICATION=ON \
-DWITH_MAGNUMFONT=ON \
-DWITH_OBJIMPORTER=ON \
-DWITH_TGAIMAGECONVERTER=ON \

1
package/archlinux/PKGBUILD-emscripten-noopt-webgl2

@ -33,6 +33,7 @@ build() {
-DTARGET_GLES2=OFF \
-DWITH_AUDIO=ON \
-DWITH_SDL2APPLICATION=ON \
-WDITH_WINDOWLESSEGLAPPLICATION=ON \
-DWITH_MAGNUMFONT=ON \
-DWITH_OBJIMPORTER=ON \
-DWITH_TGAIMAGECONVERTER=ON \

1
package/archlinux/PKGBUILD-emscripten-wasm

@ -30,6 +30,7 @@ build() {
-DCMAKE_INSTALL_PREFIX=/usr/lib/emscripten/system \
-DWITH_AUDIO=ON \
-DWITH_SDL2APPLICATION=ON \
-WDITH_WINDOWLESSEGLAPPLICATION=ON \
-DWITH_MAGNUMFONT=ON \
-DWITH_OBJIMPORTER=ON \
-DWITH_TGAIMAGECONVERTER=ON \

1
package/archlinux/PKGBUILD-emscripten-wasm-webgl2

@ -31,6 +31,7 @@ build() {
-DTARGET_GLES2=OFF \
-DWITH_AUDIO=ON \
-DWITH_SDL2APPLICATION=ON \
-WDITH_WINDOWLESSEGLAPPLICATION=ON \
-DWITH_MAGNUMFONT=ON \
-DWITH_OBJIMPORTER=ON \
-DWITH_TGAIMAGECONVERTER=ON \

1
package/archlinux/PKGBUILD-emscripten-webgl2

@ -31,6 +31,7 @@ build() {
-DTARGET_GLES2=OFF \
-DWITH_AUDIO=ON \
-DWITH_SDL2APPLICATION=ON \
-WDITH_WINDOWLESSEGLAPPLICATION=ON \
-DWITH_MAGNUMFONT=ON \
-DWITH_OBJIMPORTER=ON \
-DWITH_TGAIMAGECONVERTER=ON \

1
package/ci/travis-emscripten.sh

@ -48,6 +48,7 @@ cmake .. \
-DCMAKE_FIND_ROOT_PATH=$HOME/deps \
-DWITH_AUDIO=ON \
-DWITH_SDL2APPLICATION=ON \
-WDITH_WINDOWLESSEGLAPPLICATION=ON \
-DWITH_MAGNUMFONT=ON \
-DWITH_MAGNUMFONTCONVERTER=ON \
-DWITH_OBJIMPORTER=ON \

19
src/Magnum/Platform/CMakeLists.txt

@ -201,12 +201,19 @@ if(WITH_SDL2APPLICATION)
endif()
# JavaScript and CSS stuff for Emscripten
if(WITH_SDL2APPLICATION AND CORRADE_TARGET_EMSCRIPTEN)
set(MagnumSdl2Application_FILES
EmscriptenApplication.js
WebApplication.css)
list(APPEND MagnumPlatform_FILES ${MagnumSdl2Application_FILES})
install(FILES ${MagnumSdl2Application_FILES} DESTINATION ${MAGNUM_DATA_INSTALL_DIR})
if(CORRADE_TARGET_EMSCRIPTEN)
set(MagnumEmscriptenApplication_FILES )
if(WITH_SDL2APPLICATION)
list(APPEND MagnumEmscriptenApplication_FILES EmscriptenApplication.js)
endif()
if(WITH_WINDOWLESSEGLAPPLICATION)
list(APPEND MagnumEmscriptenApplication_FILES WindowlessEmscriptenApplication.js)
endif()
if(WITH_SDL2APPLICATION OR WITH_WINDOWLESSEGLAPPLICATION)
list(APPEND MagnumEmscriptenApplication_FILES WebApplication.css)
list(APPEND MagnumPlatform_FILES ${MagnumEmscriptenApplication_FILES})
install(FILES ${MagnumEmscriptenApplication_FILES} DESTINATION ${MAGNUM_DATA_INSTALL_DIR})
endif()
endif()
# GLX application

14
src/Magnum/Platform/WebApplication.css

@ -49,3 +49,17 @@ h1 {
font-size: 15px;
z-index: 9;
}
#module.hidden {
display: none;
}
#log {
width: 640px;
height: 480px;
overflow-y: scroll;
overflow-x: hidden;
white-space: pre-wrap;
z-index: 10;
margin: 0;
}

22
src/Magnum/Platform/WindowlessEglApplication.cpp

@ -60,10 +60,13 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, C
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
#ifndef MAGNUM_TARGET_GLES
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
#elif defined(MAGNUM_TARGET_GLES2) || defined(CORRADE_TARGET_EMSCRIPTEN)
/* Emscripten doesn't know about EGL_OPENGL_ES3_BIT_KHR for WebGL 2 and
the whole thing is controlled by -s USE_WEBGL2=1 flag anyway, so it
doesn't matter that we ask for ES2 on WebGL 2 as well. */
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
#elif defined(MAGNUM_TARGET_GLES3)
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
#elif defined(MAGNUM_TARGET_GLES2)
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
#else
#error unsupported OpenGL edition
#endif
@ -84,18 +87,27 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, C
const EGLint attributes[] = {
#ifdef MAGNUM_TARGET_GLES
EGL_CONTEXT_CLIENT_VERSION,
#ifdef MAGNUM_TARGET_GLES3
3,
#elif defined(MAGNUM_TARGET_GLES2)
#if defined(MAGNUM_TARGET_GLES2) || defined(CORRADE_TARGET_EMSCRIPTEN)
/* Emscripten doesn't know about version 3 for WebGL 2 and the
whole thing is controlled by -s USE_WEBGL2=1 flag anyway, so it
doesn't matter that we ask for ES2 on WebGL 2 as well. */
2,
#elif defined(MAGNUM_TARGET_GLES3)
3,
#else
#error unsupported OpenGL ES version
#endif
#endif
#ifndef MAGNUM_TARGET_WEBGL
EGL_CONTEXT_FLAGS_KHR, EGLint(configuration.flags()),
#endif
EGL_NONE
};
#ifdef MAGNUM_TARGET_WEBGL
static_cast<void>(configuration);
#endif
if(!(_context = eglCreateContext(_display, config, EGL_NO_CONTEXT, attributes))) {
Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot create EGL context:" << Implementation::eglErrorString(eglGetError());
return;

100
src/Magnum/Platform/WindowlessEglApplication.h

@ -126,10 +126,12 @@ class WindowlessEglContext {
*/
class WindowlessEglContext::Configuration {
public:
#ifndef MAGNUM_TARGET_WEBGL
/**
* @brief Context flag
*
* @see @ref Flags, @ref setFlags(), @ref Context::Flag
* @requires_gles Context flags are not available in WebGL.
*/
enum class Flag: int {
Debug = EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR /**< Create debug context */
@ -139,16 +141,23 @@ class WindowlessEglContext::Configuration {
* @brief Context flags
*
* @see @ref setFlags(), @ref Context::Flags
* @requires_gles Context flags are not available in WebGL.
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
typedef Containers::EnumSet<Flag, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR> Flags;
#else
typedef Containers::EnumSet<Flag> Flags;
#endif
#endif
constexpr /*implicit*/ Configuration() {}
/** @brief Context flags */
#ifndef MAGNUM_TARGET_WEBGL
/**
* @brief Context flags
*
* @requires_gles Context flags are not available in WebGL.
*/
Flags flags() const { return _flags; }
/**
@ -156,25 +165,28 @@ class WindowlessEglContext::Configuration {
* @return Reference to self (for method chaining)
*
* Default is no flag. See also @ref Context::flags().
* @requires_gles Context flags are not available in WebGL.
*/
Configuration& setFlags(Flags flags) {
_flags = flags;
return *this;
}
#endif
private:
#ifndef MAGNUM_TARGET_WEBGL
Flags _flags;
#endif
};
/**
@brief Windowless EGL application
Application for offscreen rendering using @ref WindowlessEglContext. Supported
mainly on OpenGL ES drivers, for desktop OpenGL the only driver that supports
this configuration is NVidia >= 355. See other `Windowless*Application` classes
for an alternative.
It is built if `WITH_WINDOWLESSEGLAPPLICATION` is enabled in CMake.
Application for offscreen rendering using @ref WindowlessEglContext. This
application library is in theory available for all platforms for which EGL
works (Linux desktop or ES, iOS and also @ref CORRADE_TARGET_EMSCRIPTEN "Emscripten").
See other `Windowless*Application` classes for an alternative. It is built if
`WITH_WINDOWLESSEGLAPPLICATION` is enabled in CMake.
## Bootstrap application
@ -193,6 +205,39 @@ application with these four commands:
See @ref cmake for more information.
## Bootstrap application for Emscripten
Fully contained windowless application together with Emscripten support along
with full HTML markup and CMake setup is available in `windowless-emscripten`
branch of [Magnum Bootstrap](https://github.com/mosra/magnum-bootstrap)
repository, download it as [tar.gz](https://github.com/mosra/magnum-bootstrap/archive/windowless-emscripten.tar.gz)
or [zip](https://github.com/mosra/magnum-bootstrap/archive/windowless-emscripten.zip)
file. After extracting the downloaded archive, you can do the desktop build in
the same way as above. For the Emscripten build you also need to put the
contents of toolchains repository from https://github.com/mosra/toolchains
in `toolchains/` subdirectory. There are two toolchain files. The
`generic/Emscripten.cmake` is for the classical (asm.js) build, the
`generic/Emscripten-wasm.cmake` is for WebAssembly build. Don't forget to adapt
`EMSCRIPTEN_PREFIX` variable in `toolchains/generic/Emscripten*.cmake` to path
where Emscripten is installed; you can also pass it explicitly on command-line
using `-DEMSCRIPTEN_PREFIX`. Default is `/usr/emscripten`.
Then create build directory and run `cmake` and build/install commands in it.
Set `CMAKE_PREFIX_PATH` to where you have all the dependencies installed, set
`CMAKE_INSTALL_PREFIX` to have the files installed in proper location (a
webserver, e.g. `/srv/http/emscripten`).
mkdir build-emscripten && cd build-emscripten
cmake .. \
-DCMAKE_TOOLCHAIN_FILE="../toolchains/generic/Emscripten.cmake" \
-DCMAKE_PREFIX_PATH=/usr/lib/emscripten/system \
-DCMAKE_INSTALL_PREFIX=/srv/http/emscripten
cmake --build .
cmake --build . --target install
You can then open `MyApplication.html` in your browser (through webserver, e.g.
`http://localhost/emscripten/MyApplication.html`).
## General usage
In CMake you need to request `WindowlessEglApplication` component and link to
@ -213,6 +258,47 @@ MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN(MyApplication)
If no other application header is included, this class is also aliased to
`Platform::WindowlessApplication` and the macro is aliased to
`MAGNUM_WINDOWLESSAPPLICATION_MAIN()` to simplify porting.
## Usage with Emscripten
If you are targetting Emscripten, you need to provide HTML markup for your
application. Template one is below or in the bootstrap application, you can
modify it to your liking. The markup references two files,
`WindowlessEmscriptenApplication.js` and `WebApplication.css`, both are in
`Platform/` directory in the source tree and are also installed into
`share/magnum/` inside your Emscripten toolchain. Change `&lt;application&gt;`
to name of your executable.
@code
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Magnum Windowless Emscripten Application</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="WebApplication.css" />
</head>
<body>
<h1>Magnum Windowless Emscripten Application</h1>
<div id="listener">
<canvas id="module" class="hidden"></canvas>
<pre id="log"></pre>
<div id="status">Initialization...</div>
<div id="statusDescription"></div>
<script src="WindowlessEmscriptenApplication.js"></script>
<script async="async" src="<application>.js"></script>
</div>
</body>
</html>
@endcode
You can modify all the files to your liking, but the HTML file must contain at
least the `&lt;canvas&gt;` enclosed in listener `&lt;div&gt;`. The JavaScript
file contains event listeners which print loading status on the page. The
status displayed in the remaining two `&lt;div&gt;`s, if they are available.
The CSS file contains rudimentary style to avoid eye bleeding.
The application prints all output (thus also @ref Corrade::Utility::Debug "Debug",
@ref Corrade::Utility::Warning "Warning" and @ref Corrade::Utility::Error "Error")
to the `&lt;pre&gt;` on the page.
*/
class WindowlessEglApplication {
public:

46
src/Magnum/Platform/WindowlessEmscriptenApplication.js

@ -0,0 +1,46 @@
var Module = {
preRun: [],
postRun: [],
printErr: function(message) {
var log = document.getElementById('log');
log.innerHTML += Array.prototype.slice.call(arguments).join(' ') + '\n';
},
print: function(message) {
var log = document.getElementById('log');
log.innerHTML += Array.prototype.slice.call(arguments).join(' ') + '\n';
},
canvas: document.getElementById('module'),
setStatus: function(message) {
var status = document.getElementById('status');
if(status) status.innerHTML = message;
},
setStatusDescription: function(message) {
var statusDescription = document.getElementById('statusDescription');
if(statusDescription) statusDescription.innerHTML = message;
},
totalDependencies: 0,
monitorRunDependencies: function(left) {
this.totalDependencies = Math.max(this.totalDependencies, left);
if(left) {
Module.setStatus('Downloading...');
Module.setStatusDescription((this.totalDependencies - left) + '/' + this.totalDependencies);
} else {
Module.setStatus('');
Module.setStatusDescription('');
}
}
};
Module.setStatus('Downloading...');
Module.canvas.addEventListener('contextmenu', function(event) {
event.preventDefault();
}, true);
Loading…
Cancel
Save