OffscreenCanvas - #175
Merged
Merged
OffscreenCanvas#175
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
triniwiz
force-pushed
the
feat/offscreen-canvas-threaded
branch
from
September 30, 2026 14:37
94d2e37 to
b3ca855
Compare
An OffscreenCanvas draws through the same native contexts as Canvas: on a detached canvas made at its first getContext(), or, after canvas.transferControlToOffscreen(), on that canvas's own surface so its frames show in the view. 2d, bitmaprenderer, webgl, webgl2 and webgpu contexts; transferToImageBitmap() (clearing the frame, as a present would); convertToBlob(); contextlost/restored events forwarded from the host. - Canvas: getContext() moves to CanvasBase, which throws InvalidStateError once the canvas is transferred and otherwise calls the host's _getContext(). transferControlToOffscreen() refuses a canvas with a context or one already transferred; a transferred canvas's width/height setters throw, and its OffscreenCanvas resizes it through _resizeBitmap(). - drawImage, createPattern, createImageBitmap, texImage2D/texSubImage2D and copyExternalImageToTexture take an OffscreenCanvas where they take a Canvas (isOffscreenCanvas, checked by tag to keep them free of the module). - OffscreenCanvas and OffscreenCanvasRenderingContext2D are exported, set on global, and on the polyfill's window. - Canvas typings gain transferControlToOffscreen() and threadedWebGL. Contexts still belong to the UI thread, so an OffscreenCanvas can't move to a Worker yet.
ConvertFromV8StringView and ConvertFromV8StringViewValue returned a std::string_view into a v8::String::Utf8Value that is destroyed when they return, so callers read freed memory. On iOS it read as empty: __base64Decode and __base64Encode returned '' for any input, and a WebGPU featureLevel option was never seen. They now return a std::string.
canvas_native_webgl_read_drawing_buffer bound framebuffer 0, but on iOS the drawing buffer is the GLKView's framebuffer (binding null binds it), so texImage2D and copyExternalImageToTexture from a WebGL canvas read back transparent. It now binds the drawing buffer the way bindFramebuffer(null) does.
… were bound FRAMEBUFFER_BINDING (and READ_FRAMEBUFFER_BINDING, PIXEL_PACK_BUFFER_BINDING) came back as a wrapper around the bare GL name, which binds nothing when passed back, and on iOS named the view's framebuffer when none was bound. So the usual save / bind / restore pattern silently left the drawing buffer bound. The context now keeps what it bound, clears it when that object is deleted, and returns it, without a round trip to the native side. OffscreenCanvas saves and restores through it.
offscreen.api, .2d, .source, .webgl, .webgpu and .transfer: the constructor and getContext rules, each context, transferToImageBitmap and convertToBlob, use as an image source by 2d, WebGL and createImageBitmap, and transferControlToOffscreen. Run with --suite offscreen.
A WebGPU canvas's toDataURL (and so OffscreenCanvas.transferToImageBitmap and convertToBlob) encoded the texture's bytes as BGRA on Apple and RGBA everywhere else, whatever the texture held. Windows canvases are bgra8unorm, so red and blue came back swapped there, as they would for an rgba8unorm canvas on iOS. The order now comes from the configured format.
triniwiz
force-pushed
the
feat/offscreen-canvas-threaded
branch
from
September 30, 2026 20:58
a50f6c8 to
0c9b9b3
Compare
This was referenced Sep 30, 2026
Closed
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
OffscreenCanvas, plus fixes found while testing it on iOS and Android.OffscreenCanvas
Canvas: on a detached canvas made at the firstgetContext(), or, aftercanvas.transferControlToOffscreen(), on that canvas's own surface so its frames show in the view.2d,bitmaprenderer,webgl,webgl2,webgpu.transferToImageBitmap()hands over the frame and clears the canvas, as a present would.convertToBlob()encodes it.contextlost/contextrestoredare forwarded from the host canvas.Canvas:getContext()moves toCanvasBase, which throwsInvalidStateErroronce the canvas is transferred and otherwise calls the host's_getContext().transferControlToOffscreen()refuses a canvas that has a context or was already transferred. A transferred canvas'swidth/heightsetters throw, and its OffscreenCanvas resizes it through_resizeBitmap().drawImage,createPattern,createImageBitmap,texImage2D/texSubImage2DandcopyExternalImageToTexturetake an OffscreenCanvas where they take a Canvas.isOffscreenCanvaschecks the tag, so these modules don't need to import it.OffscreenCanvasandOffscreenCanvasRenderingContext2Dare exported, set onglobal, and on the polyfill'swindow. The Canvas typings gaintransferControlToOffscreen()andthreadedWebGL.Fixes
ConvertFromV8StringView/ConvertFromV8StringViewValuereturned astd::string_viewinto av8::String::Utf8Valuedestroyed on return. On iOS,__base64Decode/__base64Encodereturned''for any input, and a WebGPUfeatureLeveloption was ignored. They now returnstd::string.getParameterreturns what was bound.FRAMEBUFFER_BINDING(andREAD_FRAMEBUFFER_BINDING,PIXEL_PACK_BUFFER_BINDING) came back as a wrapper around the bare GL name, which binds nothing when passed back, and on iOS named the view's framebuffer when none was bound. So the usual save → bind → restore pattern left the drawing buffer bound. The context now keeps the objects it bound, clears them when deleted, and answers without a native round trip.canvas_native_webgl_read_drawing_bufferbound framebuffer 0, but on iOS the drawing buffer is the GLKView's framebuffer.texImage2D/copyExternalImageToTexturefrom a WebGL canvas read back transparent there.canvas_native.hregenerated.Testing
offscreenintools/demo/canvas/spec/offscreen.ts(37 tests: API rules, each context, image-source use, transfer), run with--demo=canvas-spec --suite=offscreen. Also 4 WebGL tests for the binding fix.origin/mastertoo; scaling and lifecycle also vary from run to run.lmkdonorigin/mastertoo. Native heap grows about 7 MB per WebGL test until GC runs.