Skip to content

fix(ios): take the Metal drawable on first draw; threaded WebGL layer changes off main - #190

Merged
triniwiz merged 2 commits into
masterfrom
perf/metal-lazy-drawable
Nov 7, 2026
Merged

triniwiz merged 2 commits into
masterfrom
perf/metal-lazy-drawable

Conversation

@triniwiz

@triniwiz triniwiz commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

Metal 2D: take the drawable on first draw

Context::present presented and then immediately took the next drawable from the CAMetalLayer, so every canvas held one of the layer's drawables between frames. With several canvases this starves the pool and nextDrawable blocks sooner. The drawable is now taken on the frame's first draw (bind_surface), the path tvOS has used since #146. Contents carry over through replace_backend_texture, as before. Offscreen Metal contexts are unchanged.

Threaded WebGL: layer changes off the main thread

Since threaded WebGL (#176), renderbufferStorage:fromDrawable: runs on the WebGL thread. It sets the contents of CanvasGLView's backing layer, so UIKit logged "Modifying properties of a view's layer off the main thread" once per context. The Worker transfer path did the same with setDrawableProperties:. Both now go through canvas_core::gpu::gl::change_view_layer, which wraps the change in a CATransaction with actions disabled when off main. That skips UIKit's action lookup, and the explicit commit applies the change. The main-thread path is untouched.

Libraries

Rebuilt the xcframework (all 6 slices) and the Android AAR. The AAR's libcanvasnativev8.so picks up #187's CanvasJSIModule.cpp change. Both fixes are Apple-only; Android was rebuilt for #187 and tested on the emulator below.

Testing

New spec: lifecycle "a 2d context keeps earlier frames' drawing (threaded|direct)" draws across several presents and idle frames and checks every earlier draw is still there.

iOS 26.4 simulator, rebuilt xcframework: lifecycle 12/12, 2d 180/180, imagebitmap 18/18, bitmaprenderer 15/15, canvassource 9/9, offscreen 64/64, webgl 83/83, webgpu 19/19, scaling 6/6. UIKit warnings: 16 before, 0 after. The busy-screen trail scene keeps its whole trail.

Android emulator (Pixel 9 Pro), rebuilt AAR: 2d 180/180, webgl 83/83, imagebitmap 18/18, bitmaprenderer 15/15, scaling 6/6, offscreen 63/64, canvassource 7/9, lifecycle 9 to 11 of 15. All remaining failures are either WebGPU, which the emulator has no adapter for, or "webgl/webgl2 frame drawn before the surface exists". That second pair also fails in 4 of 5 runs with the previous AAR, so it is an existing flake.

Not tested on a physical iOS device. The simulator runs at its 60 fps cap with headroom, so the frame-pacing gain from the drawable change was not measurable there; it should show with several canvases on a device.

… changes off main

Metal 2D presented and then immediately took the next drawable, holding one
of the layer's drawables between frames. It now takes it on the frame's first
draw, as tvOS already did (#146). Offscreen Metal contexts are unchanged.

Threaded WebGL called renderbufferStorage:fromDrawable: (and, for Worker
transfers, setDrawableProperties:) on the WebGL thread, which modifies the
CanvasGLView's backing layer and made UIKit log "Modifying properties of a
view's layer off the main thread". Both now run inside an explicit
CATransaction with actions disabled when off main.

Adds a lifecycle spec checking 2D contents survive presents and idle frames.
Includes the Metal drawable and threaded WebGL layer fixes, and #187's CanvasJSIModule change in libcanvasnativev8.so.
@coderabbitai

coderabbitai Bot commented Nov 7, 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

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6b9c3d3e-424a-4728-a564-09c759c30b11

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
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@triniwiz
triniwiz merged commit 527655e into master Nov 7, 2026
10 of 19 checks passed
@triniwiz
triniwiz deleted the perf/metal-lazy-drawable branch November 7, 2026 19:58
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