Skip to content

Windows: run WebGL contexts on the WebGL thread - #177

Merged
triniwiz merged 2 commits into
masterfrom
feat/windows-webgl-thread
Sep 30, 2026
Merged

triniwiz merged 2 commits into
masterfrom
feat/windows-webgl-thread

Conversation

@triniwiz

@triniwiz triniwiz commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Summary

WebGL on Windows ran on the JS thread, which is the UI thread, so every ANGLE call blocked it. Shader compiles and links (D3D HLSL compilation) froze the UI, the more so with several WebGL views on a page. On-screen contexts now run on the shared nsc-webgl thread that Android (#174) and iOS (#176) contexts use, reached through the same WebGLState::post / sync. getContext(..., { threaded: false }) opts out, and offscreen contexts stay on the JS thread, as on Android.

  • Creation and attach: canvas_native_webgl_create_d3d_threaded makes the context on the WebGL thread. The panel's swapchain is made there and bound on the UI thread; a transparent canvas presents into its SurfaceImageSource through a XamlHandoff, as threaded 2D does. Resizes and swapchain transforms are queued.
  • Present: queued. The thread never waits on the display, since a hidden panel would stall every context on it. A frame the swapchain isn't ready for is copied aside and shown from the thread's retries, unless a newer frame comes first.
  • Context loss: a present that finds the context lost records it, so isContextLost() needs no round trip.
  • ANGLE's immediate context is multithread-protected: presents copy with it outside ANGLE's lock while contexts on the UI thread call into ANGLE.
  • napi bindings: the ones that reached the GL state directly (shader and program queries, getExtension, WebGL 2 uniform blocks and invalidation) go through sync, and their extensions follow the context's thread.
  • Frame pacing: CanvasModule.__canvasesBehind is exposed from the napi addon, so requestAnimationFrame is held back while a threaded 2D or WebGL canvas is behind, as on Android and iOS.
  • Canvas.threadedWebGL (default true on Windows) sets the default. Rebuilt x64 and arm64 canvasnative.node are in the branch.

Testing

  • New crates/canvas-c/tests/threaded_webgl_d3d.rs (cargo test -p canvas-c --features 2d,webgl,gl,d3d --test threaded_webgl_d3d): threaded frames read back like unthreaded ones; presenting into a headless panel that never takes frames (held, then forced out), resizing, and dropping a context with a frame still held; four threaded contexts linking programs beside one on the calling thread. threaded_d3d still passes.
  • Windows 11 (Intel Iris Xe), apps/demo, on master with OffscreenCanvas #175 and iOS: threaded WebGL on a CAEAGLLayer, deleteProgram fix #176: webgl 83/83, offscreen 37/37 (an OffscreenCanvas's detached WebGL host is threaded now too), 2d 180/180; the WebGL perf demo runs on screen through the thread.
  • Not tested: several WebGL views on screen at once in the app (canvas-busy lays out nothing on Windows, threaded or not); the Rust test above covers several contexts on the thread.

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 7708a329-4a97-4222-88f8-763f39b2c804

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Base automatically changed from fix/android-layout-and-gl-thread to master September 30, 2026 20:56
WebGL on Windows ran on the JS thread, which is the UI thread, so every
ANGLE call blocked it: shader compiles and links (D3D HLSL compilation)
froze the UI, the more so with several WebGL views on a page.

An on-screen context (the default, getContext(..., { threaded: false })
opts out) is created, used, presented and dropped on the shared
"nsc-webgl" thread that Android's contexts use, reached through
WebGLState::post/sync like them:

- canvas_native_webgl_create_d3d_threaded makes it there. The panel's
  swapchain is made there and bound on the UI thread; a transparent
  canvas presents into its SurfaceImageSource through a XamlHandoff, as
  threaded 2D does. Resizes and swapchain transforms are queued.
- The present is queued. The thread never waits on the display (a hidden
  panel would stall every context on it): a frame the swapchain isn't
  ready for is copied aside and shown from the thread's retries, unless a
  newer frame comes first. A present that finds the context lost records
  it, so isContextLost() needs no round trip.
- ANGLE's immediate context is multithread-protected: presents copy with
  it outside ANGLE's lock while contexts on the UI thread call ANGLE.
- The napi bindings that reached the GL state directly (shader and
  program queries, getExtension, WebGL 2 uniform blocks and invalidation)
  go through sync, and their extensions follow the context's thread.
- CanvasModule.__canvasesBehind is exposed, so requestAnimationFrame is
  held back while a threaded 2D or WebGL canvas is behind, as on Android.
@triniwiz
triniwiz force-pushed the feat/windows-webgl-thread branch from d2e7d47 to d2d807e Compare September 30, 2026 21:43
@triniwiz
triniwiz merged commit f5424ba into master Sep 30, 2026
14 of 21 checks passed
triniwiz added a commit that referenced this pull request Sep 30, 2026
* fix(canvas-core): require glutin 0.32.3

The Android GL context asks for a low priority with ContextAttributesBuilder::with_priority,
which glutin 0.32.0 does not have. A lockfile still on 0.32.0 fails to build canvas-core.

* chore: rebuild Android and Apple native libraries

canvas-release.aar and CanvasNative.xcframework, canvassvg-release.aar and CanvasSVG.xcframework,
rebuilt from f5424ba with the steps of build-native.yml, for #174, #175, #176 and #177.
canvas_native.h catches up with declarations already in the source.

* chore: 3.0.0-beta.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant