Skip to content

Commit

Permalink
Make WebGL context attribute explicitSwapControl imply renderViaOffsc…
Browse files Browse the repository at this point in the history
…reenBackBuffer when OffscreenCanvas is not supported. Add testing for OFFSCREEN_FRAMEBUFFER mode. Add documentation for OFFSCREEN_FRAMEBUFFER.
  • Loading branch information
juj committed Jul 21, 2018
1 parent 0a3719b commit 02aa29f
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 4 deletions.
23 changes: 22 additions & 1 deletion site/source/docs/api_reference/html5.h.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1949,6 +1949,20 @@ Struct
If ``true``, all GLES2-compatible non-performance-impacting WebGL extensions will automatically be enabled for you after the context has been created. If ``false``, no extensions are enabled by default, and you need to manually call :c:func:`emscripten_webgl_enable_extension` to enable each extension that you want to use. Default value: ``true``.
.. c:member:: EM_BOOL explicitSwapControl
By default, when ``explicitSwapControl`` is in its default state ``false``, rendered WebGL content is implicitly presented (displayed to the user) on the canvas when the event handler that renders with WebGL returns back to the browser event loop. If ``explicitSwapControl`` is set to ``true``, rendered content will not be displayed on screen automatically when event handler function finishes, but the control of swapping is given to the user to manage, via the ``emscripten_webgl_commit_frame()`` function.
In order to be able to set ``explicitSwapControl==true``, support for it must explicitly be enabled either 1) via adding the ``-s OFFSCREEN_FRAMEBUFFER=1`` Emscripten linker flag, and enabling ``renderViaOffscreenBackBuffer==1``, or 2) via adding the the linker flag ``-s OFFSCREENCANVAS_SUPPORT=1``, and running in a browser that supports OffscreenCanvas.
.. c:member:: EM_BOOL renderViaOffscreenBackBuffer
If ``true``, an extra intermediate backbuffer (offscreen render target) is allocated to the created WebGL context, and rendering occurs to this backbuffer instead of directly onto the WebGL "default backbuffer". This is required to be enabled if 1) ``explicitSwapControl==true`` and the browser does not support OffscreenCanvas, 2) when performing WebGL rendering in a worker thread and the browser does not support OffscreenCanvas, and 3) when performing WebGL context accesses from multiple threads simultaneously (independent of whether OffscreenCanvas is supported or not).
Because supporting offscreen framebuffer adds some amount of extra code to the compiled output, support for it must explicitly be enabled via the ``-s OFFSCREEN_FRAMEBUFFER=1`` Emscripten linker flag. When building simultaneously with both ``-s OFFSCREEN_FRAMEBUFFER=1`` and ``-s OFFSCREENCANVAS_SUPPORT=1`` linker flags enabled, offscreen backbuffer can be used as a polyfill-like compatibility fallback to enable rendering WebGL from a pthread when the browser does not support the OffscreenCanvas API.
Callback functions
------------------
Expand Down Expand Up @@ -2044,7 +2058,14 @@ Functions
:rtype: |EMSCRIPTEN_WEBGL_CONTEXT_HANDLE|
.. c:function:: EMSCRIPTEN_RESULT emscripten_webgl_commit_frame()
Presents ("swaps") the content rendered on the currently active WebGL context to be visible on the canvas. This function is available on WebGL contexts that were created with the ``explicitSwapControl==true`` context creation attribute. If ``explicitSwapControl==false``, then the rendered content is displayed on the screen "implicitly" when yielding back to the browser from the calling event handler.
:returns: :c:data:`EMSCRIPTEN_RESULT_SUCCESS`, or one of the other result values, denoting a reason for failure.
:rtype: |EMSCRIPTEN_RESULT|
.. c:function:: EMSCRIPTEN_RESULT emscripten_webgl_get_drawing_buffer_size(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context, int *width, int *height)
Gets the ``drawingBufferWidth`` and ``drawingBufferHeight`` of the specified WebGL context.
Expand Down
16 changes: 14 additions & 2 deletions src/library_html5.js
Original file line number Diff line number Diff line change
Expand Up @@ -1814,6 +1814,7 @@ var LibraryJSEvents = {
contextAttributes['minorVersion'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.minorVersion, 'i32') }}};
var enableExtensionsByDefault = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.enableExtensionsByDefault, 'i32') }}};
contextAttributes['explicitSwapControl'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.explicitSwapControl, 'i32') }}};
contextAttributes['renderViaOffscreenBackBuffer'] = {{{ makeGetValue('attributes', C_STRUCTS.EmscriptenWebGLContextAttributes.renderViaOffscreenBackBuffer, 'i32') }}};

target = Pointer_stringify(target);
var canvas;
Expand All @@ -1836,7 +1837,9 @@ var LibraryJSEvents = {
#if OFFSCREEN_FRAMEBUFFER
if (!contextAttributes['renderViaOffscreenBackBuffer']) {
contextAttributes['renderViaOffscreenBackBuffer'] = true;
#if GL_DEBUG
console.error('emscripten_webgl_create_context: Performance warning, OffscreenCanvas is not supported but explicitSwapControl was requested, so force-enabling renderViaOffscreenBackBuffer=true to allow explicit swapping!');
#endif
}
#else
#if GL_DEBUG
Expand All @@ -1852,14 +1855,23 @@ var LibraryJSEvents = {
canvas = GL.offscreenCanvases[canvas.id];
}
}
#else
#else // !OFFSCREENCANVAS_SUPPORT
#if !OFFSCREEN_FRAMEBUFFER
#if OFFSCREEN_FRAMEBUFFER
if (contextAttributes['explicitSwapControl'] && !contextAttributes['renderViaOffscreenBackBuffer']) {
contextAttributes['renderViaOffscreenBackBuffer'] = true;
#if GL_DEBUG
console.error('emscripten_webgl_create_context: Performance warning, not building with OffscreenCanvas support enabled but explicitSwapControl was requested, so force-enabling renderViaOffscreenBackBuffer=true to allow explicit swapping!');
#endif
}
#else
if (contextAttributes['explicitSwapControl']) {
#if GL_DEBUG
console.error('emscripten_webgl_create_context failed: explicitSwapControl is not supported, please rebuild with -s OFFSCREENCANVAS_SUPPORT=1 to enable targeting the experimental OffscreenCanvas specification, or rebuild with -s OFFSCREEN_FRAMEBUFFER=1 to emulate explicitSwapControl in the absence of OffscreenCanvas support!');
#endif
return 0;
}
#endif // ~!OFFSCREEN_FRAMEBUFFER

#endif // ~!OFFSCREENCANVAS_SUPPORT

var contextHandle = GL.createContext(canvas, contextAttributes);
Expand Down
2 changes: 1 addition & 1 deletion system/include/emscripten/html5.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ extern EMSCRIPTEN_RESULT emscripten_set_webglcontextrestored_callback(const char

extern EM_BOOL emscripten_is_webgl_context_lost(const char *target);

extern EMSCRIPTEN_RESULT emscripten_webgl_commit_frame();
extern EMSCRIPTEN_RESULT emscripten_webgl_commit_frame(void);

extern EMSCRIPTEN_RESULT emscripten_set_canvas_element_size(const char *target, int width, int height);
extern EMSCRIPTEN_RESULT emscripten_get_canvas_element_size(const char *target, int *width, int *height);
Expand Down
5 changes: 5 additions & 0 deletions tests/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3731,6 +3731,11 @@ def test_webgl_offscreen_canvas_in_mainthread_after_pthread(self):
for args in [[], ['-DTEST_MAIN_THREAD_EXPLICIT_COMMIT']]:
self.btest('gl_in_mainthread_after_pthread.cpp', expected='0', args=args+['-s', 'USE_PTHREADS=1', '-s', 'PTHREAD_POOL_SIZE=2', '-s', 'OFFSCREENCANVAS_SUPPORT=1', '-lGL'])

# Tests that -s OFFSCREEN_FRAMEBUFFER=1 rendering works.
@requires_graphics_hardware
def test_webgl_offscreen_framebuffer(self):
self.btest('webgl_draw_triangle.c', '0', args=['-lGL', '-s', 'OFFSCREEN_FRAMEBUFFER=1', '-DEXPLICIT_SWAP=1'])

# Tests the feature that shell html page can preallocate the typed array and place it to Module.buffer before loading the script page.
# In this build mode, the -s TOTAL_MEMORY=xxx option will be ignored.
# Preallocating the buffer in this was is asm.js only (wasm needs a Memory).
Expand Down
98 changes: 98 additions & 0 deletions tests/webgl_draw_triangle.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include <stdio.h>
#include <stdlib.h>
#include <emscripten/emscripten.h>
#include <emscripten/html5.h>
#include <GLES2/gl2.h>

GLuint compile_shader(GLenum shaderType, const char *src)
{
GLuint shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &src, NULL);
glCompileShader(shader);

GLint isCompiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
if (!isCompiled)
{
GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
char *buf = (char*)malloc(maxLength+1);
glGetShaderInfoLog(shader, maxLength, &maxLength, buf);
printf("%s\n", buf);
free(buf);
return 0;
}

return shader;
}

GLuint create_program(GLuint vertexShader, GLuint fragmentShader)
{
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glBindAttribLocation(program, 0, "apos");
glBindAttribLocation(program, 1, "acolor");
glLinkProgram(program);
return program;
}

int main()
{
EmscriptenWebGLContextAttributes attr;
emscripten_webgl_init_context_attributes(&attr);
#ifdef EXPLICIT_SWAP
attr.explicitSwapControl = 1;
#endif

EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attr);
emscripten_webgl_make_context_current(ctx);

static const char vertex_shader[] =
"attribute vec4 apos;"
"attribute vec4 acolor;"
"varying vec4 color;"
"void main() {"
"color = acolor;"
"gl_Position = apos;"
"}";
GLuint vs = compile_shader(GL_VERTEX_SHADER, vertex_shader);

static const char fragment_shader[] =
"precision lowp float;"
"varying vec4 color;"
"void main() {"
"gl_FragColor = color;"
"}";
GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader);

GLuint program = create_program(vs, fs);
glUseProgram(program);

GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
const float pos_and_color[] = {
// x, y, r, g, b
-0.6f, -0.6f, 1, 0, 0,
0.6f, -0.6f, 0, 1, 0,
0.f, 0.6f, 0, 0, 1,
};
glBufferData(GL_ARRAY_BUFFER, sizeof(pos_and_color), pos_and_color, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 20, 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 20, (void*)8);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);

glClearColor(0.3f,0.3f,0.3f,1);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);

#ifdef EXPLICIT_SWAP
emscripten_webgl_commit_frame();
#endif

#ifdef REPORT_RESULT
REPORT_RESULT(0);
#endif
}

0 comments on commit 02aa29f

Please sign in to comment.