Skip to content

Commit 8a439eb

Browse files
committed
Add canvas transfer API for next thread
Useful for thread creation APIs that do not let you pass a pthread_attr_t. Fixes #10307.
1 parent 7968c8c commit 8a439eb

File tree

5 files changed

+91
-1
lines changed

5 files changed

+91
-1
lines changed

site/source/docs/api_reference/html5.h.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2040,7 +2040,9 @@ Struct
20402040
20412041
.. c:member:: bool proxyContextToMainThread
20422042
2043-
This member specifies the threading model that will be used for the created WebGL context, when the WebGL context is created in a pthread. Three values are possible: ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW``, ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK`` or ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS``. If ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW`` is specified, the WebGLRenderingContext object will be created inside the pthread that is calling the ``emscripten_webgl_create_context()`` function as an OffscreenCanvas-based rendering context. This is only possible if 1) current browser supports OffscreenCanvas specification, 2) code was compiled with ``-sOFFSCREENCANVAS_SUPPORT`` linker flag enabled, 3) the Canvas object that the context is being created on was transferred over to the calling pthread with function ``emscripten_pthread_attr_settransferredcanvases()`` when the pthread was originally created, and 4) no OffscreenCanvas-based context already exists from the given Canvas at the same time.
2043+
This member specifies the threading model that will be used for the created WebGL context, when the WebGL context is created in a pthread. Three values are possible: ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW``, ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_FALLBACK`` or ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_ALWAYS``.
2044+
2045+
If ``EMSCRIPTEN_WEBGL_CONTEXT_PROXY_DISALLOW`` is specified, the WebGLRenderingContext object will be created inside the pthread that is calling the ``emscripten_webgl_create_context()`` function as an OffscreenCanvas-based rendering context. This is only possible if 1) current browser supports OffscreenCanvas specification, 2) code was compiled with ``-sOFFSCREENCANVAS_SUPPORT`` linker flag enabled, 3) the Canvas object that the context is being created on was transferred over to the calling pthread with function ``emscripten_pthread_attr_settransferredcanvases()`` when the pthread was originally created, and 4) no OffscreenCanvas-based context already exists from the given Canvas at the same time. For thread creation APIs that do not let you pass a ``pthread_attr_t`` (for example ``std::thread``, ``boost::thread`` or C11 threads), use ``emscripten_set_next_thread_transferredcanvases()`` before creating the thread. The pending canvas selector string is consumed by the next ``pthread_create()`` on that thread and then cleared automatically.
20442046
20452047
If a WebGL rendering context is created as an OffscreenCanvas-based context, it will have the limitation that only the pthread that created the context can enable access to it (via ``emscripten_webgl_make_context_current()`` function). Other threads will not be able to activate rendering to the context, i.e. OffscreenCanvas-based contexts are essentially "pinned" to the pthread that created them.
20462048

system/include/emscripten/threading.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,20 @@ int emscripten_pthread_attr_gettransferredcanvases(const pthread_attr_t * _Nonnu
8787
// The special value "#canvas" denotes the element stored in Module.canvas.
8888
int emscripten_pthread_attr_settransferredcanvases(pthread_attr_t * _Nonnull a, const char * _Nonnull str);
8989

90+
// Specifies a comma-delimited list of canvas DOM element IDs to transfer to
91+
// the next thread created by the current thread when no explicit
92+
// pthread_attr_t::_a_transferredcanvases value is provided.
93+
//
94+
// This is intended for creation paths such as std::thread, boost::thread, or
95+
// C11 threads where the caller cannot provide a pthread_attr_t before the
96+
// underlying pthread is launched.
97+
//
98+
// The next pthread creation on the current thread consumes this value and
99+
// clears it automatically. Pass 0 or "" to clear any pending setting manually.
100+
// The pointer is weakly stored and must remain valid until the next
101+
// pthread_create() call returns.
102+
int emscripten_set_next_thread_transferredcanvases(const char * _Nonnull str);
103+
90104
// Called when blocking on the main thread. This will error if main thread
91105
// blocking is not enabled, see ALLOW_BLOCKING_ON_MAIN_THREAD.
92106
void emscripten_check_blocking_allowed(void);

system/lib/pthread/pthread_create.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@
2828
#define dbg(fmt, ...)
2929
#endif
3030

31+
static _Thread_local const char* next_thread_transferredcanvases;
32+
33+
int emscripten_set_next_thread_transferredcanvases(const char* str) {
34+
next_thread_transferredcanvases = (str && str[0]) ? str : NULL;
35+
return 0;
36+
}
37+
3138
// See musl's pthread_create.c
3239

3340
static void dummy_0() {}
@@ -142,6 +149,12 @@ int __pthread_create(pthread_t* restrict res,
142149
if (!attr._a_stacksize) {
143150
attr._a_stacksize = __default_stacksize;
144151
}
152+
if (!attr._a_transferredcanvases) {
153+
if (next_thread_transferredcanvases) {
154+
attr._a_transferredcanvases = next_thread_transferredcanvases;
155+
next_thread_transferredcanvases = NULL;
156+
}
157+
}
145158

146159
// Allocate memory for new thread. The layout of the thread block is
147160
// as follows. From low to high address:
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include <atomic>
2+
#include <thread>
3+
#include <stdio.h>
4+
5+
#include <GLES2/gl2.h>
6+
7+
#include <emscripten/emscripten.h>
8+
#include <emscripten/html5.h>
9+
#include <emscripten/threading.h>
10+
11+
static std::atomic<bool> g_done = false;
12+
static std::atomic<bool> g_ok = false;
13+
14+
static void thread_main() {
15+
EmscriptenWebGLContextAttributes attr;
16+
emscripten_webgl_init_context_attributes(&attr);
17+
attr.explicitSwapControl = true;
18+
19+
EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attr);
20+
if (ctx > 0 && emscripten_webgl_make_context_current(ctx) == EMSCRIPTEN_RESULT_SUCCESS) {
21+
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
22+
glClear(GL_COLOR_BUFFER_BIT);
23+
emscripten_webgl_commit_frame();
24+
emscripten_webgl_make_context_current(0);
25+
emscripten_webgl_destroy_context(ctx);
26+
g_ok = true;
27+
}
28+
29+
g_done = true;
30+
}
31+
32+
static void poll_done(void*) {
33+
if (!g_done) {
34+
emscripten_async_call(poll_done, nullptr, 20);
35+
return;
36+
}
37+
emscripten_force_exit(g_ok ? 0 : 1);
38+
}
39+
40+
int main() {
41+
if (!emscripten_supports_offscreencanvas()) {
42+
printf("Current browser does not support OffscreenCanvas. Skipping this test.\n");
43+
return 0;
44+
}
45+
46+
// The new API is intended for std::thread users that cannot pass
47+
// pthread_attr_t into thread construction.
48+
emscripten_set_next_thread_transferredcanvases("#canvas");
49+
50+
std::thread worker(thread_main);
51+
worker.detach();
52+
53+
emscripten_async_call(poll_done, nullptr, 20);
54+
emscripten_exit_with_live_runtime();
55+
__builtin_trap();
56+
}

test/test_browser.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4212,6 +4212,11 @@ def test_webgl_offscreen_canvas_in_mainthread_after_pthread(self, args):
42124212
def test_webgl_offscreen_canvas_only_in_pthread(self):
42134213
self.btest_exit('gl_only_in_pthread.c', cflags=['-pthread', '-sPTHREAD_POOL_SIZE', '-sOFFSCREENCANVAS_SUPPORT', '-lGL', '-sOFFSCREEN_FRAMEBUFFER'])
42144214

4215+
@requires_offscreen_canvas
4216+
@requires_graphics_hardware
4217+
def test_std_thread_transferred_canvas(self):
4218+
self.btest_exit('std_thread_transferred_canvas.cpp', cflags=['-pthread', '-sOFFSCREENCANVAS_SUPPORT', '-lGL'])
4219+
42154220
# Tests that rendering from client side memory without default-enabling extensions works.
42164221
@requires_graphics_hardware
42174222
def test_webgl_from_client_side_memory_without_default_enabled_extensions(self):

0 commit comments

Comments
 (0)