Skip to content

fix(gl): ask CGL for a stencil buffer — every stencil test passed on XQuartz - #353

Merged
sidorares merged 1 commit into
masterfrom
claude/cgl-stencil
Sep 12, 2026
Merged

fix(gl): ask CGL for a stencil buffer — every stencil test passed on XQuartz#353
sidorares merged 1 commit into
masterfrom
claude/cgl-stencil

Conversation

@sidorares

Copy link
Copy Markdown
Owner

A <glarea> drawn through the direct backend on macOS/XQuartz had no stencil buffer. lib/renderingcontext_cgl.js created its CGL context with a depth size and nothing else, and x11-dri's apple.Context defaults stencilSize to 0, so kCGLPFAStencilSize never reached the pixel format. The Cocoa backend's GL surfaces carry DEPTH24_STENCIL8, so the same draw code had a stencil buffer on one macOS path and not on the other.

Nothing errors when the buffer is missing: a stencil test against a framebuffer with no stencil buffer passes everywhere. The map renderer in sidorares/react-x11-components#101 fills polygons stencil-then-cover, so on XQuartz it has had to draw every frame into an offscreen framebuffer and copy it, 0.1–1.3 ms a frame.

What changes

  • appledri: STENCIL_SIZE from the spec, or stencilSize from a config, travels to kCGLPFAStencilSize, read under the same two spellings the depth size is. A config that names none gets 8 bits, the Cocoa backend's size; STENCIL_SIZE: 0 still asks for none. CGL takes the size as a minimum.
  • dri3: x11-dri 0.7.0's Gpu takes a format, a depth size and a GL version, and its CreateGpu passes no EGL_STENCIL_SIZE, so there is nothing to forward a stencil size to. This PR does not change x11-dri. The flavor now answers the request instead of dropping it, the way fix(gl): answer the multisample request instead of dropping it #343 answers SAMPLES: stencilSize: 0, and a warning once per connection naming the failure and the workaround.
  • Every backend reports it: chooseGLConfig and chooseGLXConfig answers carry stencilSize beside samples, and the direct contexts carry gl.stencilSize, so draw code can branch on what the window has. Indirect reports the chosen fbconfig's stencil bits, and null when the spec pins visual, as it already does for samples.
  • directStencilSize in lib/gl.js is the one place that decides the per-flavor answer, as DIRECT_SAMPLES is for samples. When x11-dri's Gpu grows the option, the dri3 answer changes there and nowhere else.
  • Docs: a Stencil section in docs/context-gles.md, including the probe that can tell a real stencil buffer from a missing one, and the config shape in docs/context-opengl.md.

Verified on real hardware

On this Apple M1 Pro under XQuartz, Apple-DRI flavor, with a probe that draws into a mapped window and reads back with gl.readPixels:

master this branch
FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE of the window's framebuffer 0 8
stencil cleared to 0, then a draw with NOTEQUAL 0 draws everywhere draws nothing
1 written into the left half, then a draw with EQUAL 1 fills both halves fills only the left half

The NOTEQUAL row is the one that decides it: "write 1, draw where it is 1" draws the same with or without a stencil buffer, so only a test that has to fail can tell them apart. That probe is now a live test in test/gl-appledri-live.test.js, and the file passed 5 of 5 on XQuartz in two runs. One earlier run of that file failed at xp_attach_gl_context failed 2, and one run of master's copy skipped everything; both trees did this, so that flake belongs to this machine's XQuartz rather than to this change.

Tests

  • test/gl-stencil.test.js, hermetic, on node-x11's JS X server with a recording stand-in for apple.Context: the options the CGL context is created with for the default, an explicit 0 and a hand-written config; the dri3 answer and its once-per-connection warning; indirect answers from the GLX emulator's fbconfig and from a pinned visual; the docs anchor the warning points at.
  • test/gl-appledri-live.test.js: the stencil probe above. It skips wherever Apple-DRI is missing, CI included.
  • Full suite on Xvfb: 1190 pass, 8 skip, 0 fail.

Not verified: a real dri3 context. There is no Linux GPU here, so the dri3 side of this is the reported number and the warning, both covered by the hermetic test.

…XQuartz

The appledri flavor created its CGL context with a depth size only, so its
windows had no stencil buffer, and a stencil test against no buffer passes
everywhere. STENCIL_SIZE now travels from the spec or config to
kCGLPFAStencilSize, 8 bits by default as on the Cocoa backend.

chooseGLConfig, chooseGLXConfig and the direct contexts report stencilSize.
The dri3 flavor answers 0 and warns once, since x11-dri's Gpu takes no
stencil size to pass on.
@sidorares
sidorares merged commit 4abff02 into master Sep 12, 2026
6 checks passed
@sidorares
sidorares deleted the claude/cgl-stencil branch September 12, 2026 22:42
sidorares pushed a commit that referenced this pull request Sep 12, 2026
🤖 I have created a release *beep* *boop*
---


## [8.8.2](v8.8.1...v8.8.2)
(2026-09-12)


### Bug Fixes

* **gl:** ask CGL for a stencil buffer — every stencil test passed on
XQuartz ([#353](#353))
([4abff02](4abff02))
* **text:** resolve sans-serif to Helvetica on macOS, not a face with
full-width Cyrillic
([#354](#354))
([8af0711](8af0711))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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