Skip to content

feat: Linux support - #189

Merged
triniwiz merged 23 commits into
masterfrom
feat/linux
Nov 7, 2026
Merged

triniwiz merged 23 commits into
masterfrom
feat/linux

Conversation

@triniwiz

@triniwiz triniwiz commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

Adds NativeScript Linux (GTK 4) support to canvas and its plugins, through the Node-API modules the Windows port uses.

Packages

  • canvas
    • A GTK 4 widget drives the canvasnative.node Node-API module.
    • 2D, WebGL and WebGL2 run on EGL using GDK's display. Offscreen surfaces use pbuffers, and transferred OffscreenCanvas is supported.
    • WebGPU runs on Vulkan. Its frames are shared with GL through exported memory.
  • canvas-svg: a GTK 4 view.
  • canvas-media: video and audio play through GStreamer.
  • audio-context: works through its Node-API module.
  • canvas-gamepad:
    • Reads evdev devices through gilrs, which uses SDL's mapping database. Hot-plug is watched through udev.
    • Reports the standard 17-button / 4-axis layout.
    • Reading /dev/input/event* requires read access. Desktop sessions normally grant it to gamepads.
  • canvas-polyfill:
    • fetch works for local files.
    • Blob-URL Workers work on Node-API hosts.
    • The window size is taken from the app window.
  • canvas-chartjs: Linux V8 ships without ICU data, so the Intl polyfills are forced on there.
  • The x64 and arm64 .node modules are committed under each package's platforms/linux/lib/<arch>. They're built by tools/scripts/build-linux.sh, which needs libudev-dev on the build machine. The arm64 ones were built on Ubuntu 22.04 arm64 (glibc 2.35, GTK 4.6, clang 18) and load in Node 22 on arm64, but haven't been run in a GTK app on arm64 hardware yet.

Cross-platform fixes found along the way

  • WebGL:
    • clearBuffer* now takes the buffer enum.
    • bindTransformFeedback now binds the transform feedback.
    • bindVertexArrayOES(null) now unbinds.
  • WebGPU: subgroups are no longer advertised, because naga can't enable them yet. WGSL compile errors are now logged.
  • 2D:
    • Non-finite values for lineDashOffset, globalAlpha, miterLimit and lineWidth are now ignored.
    • createImageBitmap accepts Blob-like sources.
  • Images: an image that fails to decode now fires error.
  • Node-API: Windows-only methods are now in their own impl blocks, so the module builds on other hosts.

GPU video frames

  • Display: canvas-media decodes into GL textures (glupload ! glcolorconvert) in a GStreamer GL context that shares GDK's, and GTK shows them as GdkGLTextures. If the GL path isn't available it falls back to CPU memory; NS_CANVAS_MEDIA_CPU=1 forces the fallback.
  • WebGL: texImage2D / texImage3D with a video does a GPU blit through an EGLImage, guarded by an EGL fence.
  • WebGPU: importExternalTexture and copyExternalImageToTexture(video) read Vulkan images that GL imports and fills on the GPU, synchronised by exported semaphores, as on Windows.
    • wgpu's Vulkan backend has no external textures, so texture_external is emulated by binding the frame as a texture_2d<f32>.
  • Software decode: mapped frames go to WebGL / WebGPU natively and are uploaded once.
  • VA-API: disable-vpp on vaapidecodebin fixes H.264 producing no frames on the Intel iHD driver.

1080p, process CPU:

Scenario GL path CPU path
H.264 playback ~11–16% ~28–35%
VP9 playback, VA-API decode ~11% ~44%

WebGL texImage2D(video): 0.19 ms per call, down from ~3.9 ms.

Not done: displaying DMABuf frames with GdkDmabufTexture (needs GTK 4.14+) and importing them straight into Vulkan. Both are TODOs.

Tests: media.video 9/9 on both paths, webgpu 19/19, webgl 83/83.

Depends on

Known gaps

  • Draco decoding in a Worker (three)
  • webgpu_backdrop renders black
  • an error in the webgpu_cube sample
  • gamepad tested only with no controller attached

…binds the transform feedback, bindVertexArrayOES(null) unbinds
…t and lineWidth; createImageBitmap takes Blob-like sources
…cles skip the DOM-only inspector, phaser-ce's accelerometer is Android/Apple only
@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: b9fbcc04-d80e-4704-a1ac-70e7d619f416

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

…nvas

GStreamer's GL context shares GDK's on its EGL display: glupload ! glcolorconvert
makes RGBA textures, shown as GdkGLTextures and handed to canvas as
NSCVideoFrame descriptors (an EGLImage for WebGL, a blit into WebGPU's
exportable Vulkan image synchronized with its semaphores). videoconvert to
memory stays the fallback (NS_CANVAS_MEDIA_CPU=1, no GDK EGL, GL errors).

vaapidecodebin runs without vaapipostproc, whose NV12 buffers fail to map on
iHD (H.264 showed no frames), and the preroll frame is delivered.
__texImageVideoFrame copies a GL frame through its EGLImage into the bound
texture (texImage2D/3D, sub-images, flipY) or uploads a memory frame once.
WebGPU imports frames into an exportable Vulkan image per video, waiting on
the producer's semaphore and signalling release (devices enable
VK_KHR_external_semaphore_fd); __getAdapterLuid gives the device UUID key.
Vulkan lacks EXTERNAL_TEXTURE: external textures bind their plane as a
texture_2d<f32> that shader modules use in place of texture_external.
getCurrentFrame and the texImage3D hooks go through __texImageVideoFrame;
supportsGPUFrames/getGPUFrameTexture return NSCVideoFrame descriptors (GPU on
the same device, else memory). importExternalTexture closes frames the import
copies (Linux, Windows) instead of holding them until collected.
canvas, canvas-svg, canvas-media, canvas-gamepad and audio-context, built with tools/scripts/build-linux.sh on Ubuntu 22.04 arm64 (glibc 2.35, GTK 4.6, clang 18).
@triniwiz triniwiz assigned Copilot and unassigned Copilot Nov 7, 2026
@triniwiz
triniwiz merged commit 8f008ff into master Nov 7, 2026
14 of 23 checks passed
@triniwiz
triniwiz deleted the feat/linux branch November 7, 2026 19:59
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.

2 participants