Add pygfx/WGPU backend for 2D/3D plotting#4520
Add pygfx/WGPU backend for 2D/3D plotting#4520physwkim wants to merge 20 commits intosilx-kit:mainfrom
Conversation
94a4304 to
5f1985e
Compare
Add a GPU-accelerated 2D plot backend using pygfx (WebGPU/Vulkan/Metal). New files: - BackendPygfx.py: Full 2D plot backend implementation supporting curves, images, scatter, markers, shapes, error bars, and color bar. Includes GPU-accelerated colormap pipeline (scalar texture + shader), WGSL compute shaders for min/max reduction and histogram, and async compute for non-blocking streaming updates. - _PlotFrameCore.py: Rendering-independent plot frame math extracted from GLPlotFrame. Provides coordinate transforms, tick generation, and layout calculation shared between OpenGL and pygfx backends. Modified files: - PlotWidget.py: Register "pygfx"/"wgpu" backend aliases and add updateImageData() method for efficient streaming data updates. - image.py: Add updateData() fast path on ImageDataBase that bypasses the item dirty/remove/add cycle. Supports autoscale recomputation. - core.py: Optional GPU stats hook in ColormapMixIn._setColormappedData() to pre-fill autoscale cache with GPU-computed min/max. - ColormapDialog.py: Optional GPU histogram hook for accelerated histogram computation when pygfx backend is active. - pyproject.toml: Add [pygfx] optional dependency group. - _config.py: Document pygfx backend option. Examples: - compareBackendsFPS.py: Live curve update FPS benchmark across matplotlib, opengl, and pygfx backends. - imageStreamingBenchmark.py: Image streaming throughput benchmark for the pygfx backend with per-frame timing breakdown. - plot3dVolume.py: 3D scalar field volume with isosurfaces and interactive cut plane.
5f1985e to
13b6513
Compare
|
Wow, thanks for the big contribution, that looks very promising! We'll get back to you but know that the review may take a bit of time 😉 |
|
Thanks for the contribution! We'll come back to you and iterate over this PR. A first quick comment, we aim at keeping plot backends playing an equal role, so adding a plot backend should only modify |
Remove optimizations outside of PlotWidget to keep plot backends playing an equal role: - image.py: Remove updateData() fast path - core.py: Remove GPU stats hook in ColormapMixIn - ColormapDialog.py: Remove GPU histogram hook - PlotWidget.py: Remove updateImageData() method
- BackendPygfx updateData: compute clim from data when None (nanmin/nanmax) instead of keeping stale values - Remove benchmark and example scripts not part of the backend PR
|
Unlike the previous PR, this one has been cleaned up to contain only the backend-related changes. A separate PR for the addImage fast path optimization has been created at #4524. |
|
Thanks for the split! I had a quick look at the code and a first try, here are some more early comments:
@pytest.mark.usefixtures("use_opengl")
class TestPlotWidget_Gl(TestPlotWidget):
backend = "gl"Add similar ones for pygfx.
from silx import sx
w = sx.PlotWidget(backend="pygfx")Error: Do you know what could go wrong? |
|
To complete previous comment, it also panics when setting |
Add --no-pygfx pytest option, use_pygfx fixture, and _TestOptions support for conditionally skipping pygfx tests. Add pygfx backend parametrization to existing plot widget, active item, line style, and plot window tests. Add Linux display/Wayland guard in PlotWidget for pygfx backend initialization.
Implement _pickImage and _pickTriangles methods that were left as TODO stubs. Store origin/scale/dataShape on _PygfxImageItem and x/y/triangles on _PygfxTrianglesItem for use during picking. Add PRESENT_METHOD class variable and emit axis limits changed signals on resize.
rendercanvas's request_draw uses a scheduler that may delay the actual draw, so processEvents() does not always flush pending item updates. Add QWidget.update() call to postRedisplay/replot to ensure Qt paint events are processed synchronously, matching the OpenGL backend's behavior.
|
Thanks for testing! I've added the "pygfx" backend to the test parametrizations and added the _Pygfx test classes — that's already included in the latest push. About the Wayland crash — yeah, I was able to reproduce it too. This is a known upstream issue in wgpu. The wgpu-py docs mention that Wayland support is currently broken, so it'll probably take some time on their side to fix it. For now I've added a guard in PlotWidget that raises a clear error for Wayland sessions instead of letting it panic. While adding the tests, I also found and fixed a couple of issues — image/triangle picking was stubbed out (return None # TODO), and processEvents() wasn't reliably flushing pending item updates due to rendercanvas's deferred draw scheduling. Both are fixed in the latest commits. You mentioned recently that AI-generated code should be disclosed. Could you clarify where you'd like it noted — in the commit messages, PR description, or somewhere in the source code? |
Override paintEvent to flush dirty plot items via _paintContext() inside the paint event handler, where GPU operations are safe. This mirrors the OpenGL backend's paintGL pattern and ensures _backendRenderer is up-to-date before pick() is called. Without this, processEvents() on Linux does not reliably trigger rendercanvas's async scheduler, leaving _backendRenderer as None.
Replace Python for-loops with numpy vectorized operations for building error bar line segments, improving performance for large datasets. Also fix handling of scalar (ndim=0) and column-vector (N,1) shaped error values that were previously unhandled.
8bd519f to
a497784
Compare
2D and 3D example scripts demonstrating pygfx backend usage: curves, images, scatter, line styles, markers, error bars, log axes, backend comparison, benchmarks, dual Y-axis, ImageView, large data, 3D scatter/volume/surface/mesh/heightmap, and clipping groups.
a497784 to
1951689
Compare
- Fix Box/Cylinder/Hexagon setColor -> color property - Remove unused import and variable (flake8) - Remove debug_overlay.py - Add GPU colormap streaming benchmark (19_gpu_colormap_benchmark.py)
719b90e to
35d26b5
Compare
|
The Linux CI doesn't have a GPU, so wgpu device creation fails. OpenGL works fine there because Mesa provides a software renderer (llvmpipe), but wgpu doesn't have that kind of fallback. I added a check that tries to create a wgpu device before running pygfx tests — if it fails, the tests are skipped. To run pygfx tests on Linux CI, one option would be using a GPU-equipped runner, or installing mesa-vulkan-drivers (lavapipe) for software Vulkan. I'm not sure how to set that up in the CI pipeline though. |
61387ec to
7f7592f
Compare
Just a quick sentence in the PR description is enough. Something like:
or
|
Implemented with Claude Code
e0753b4 to
ca29dff
Compare
|
Hi, Sorry for the delayed reply. We are currently preparing a major release after a very long time and we want to have this available as soon as possible, so we will not integrate your PR before then. For the review process, it would be best to focus on one feature at a time: Reviews are still done by humans in this project. It would be much simpler to go step by step and have a first PR for the 2d plot backend only (you can keep this one opened to demo 2d/3d plot and open another one providing only the 2d plot backend). |


OpenGL has been deprecated on macOS. This PR adds a new rendering backend based on pygfx/WGPU, enabling native Metal (macOS), Vulkan (Linux), and DirectX (Windows) support.
Drop-in alternative to the existing OpenGL backend for both 2D and 3D plotting.
2D (silx.gui.plot)
3D (silx.gui.plot3d)
Key changes
Benchmarks
Two benchmarks were added to evaluate the backend performance.
Benchmarks
Benchmarks were run on a MacBook M4 Pro.
compareBackendsFPS.pyfor live line update performanceimageStreamingBenchmark.pyfor image streaming throughputImage streaming benchmark
Example result for 4096 × 4096 streaming images:
The backend maintains >120 FPS for 4K images during continuous streaming updates.
Curve update benchmark
Live curve updates comparison between backends:
matplotlib : ~136 FPS
OpenGL : ~120 FPS
pygfx/WGPU : ~244 FPS
The pygfx backend shows ~2× higher performance than the OpenGL backend for dynamic curve updates.
Platform Status
Motivation
Using
pygfxallows silx to benefit from:AI Disclosure
Claude Code was used to generate a significant portion of the code in this PR, including the pygfx backend, synchronization layer, and associated tests.
The author guided the implementation by providing high-level design direction and examples, and performed iterative validation through manual testing, debugging, and benchmarking.