|
This is really cool! The toolbar requires imgui, was easy to get it working by just adding "imgui_bundle" to the deps. Was able to run stuff in chrome after enabling the wgpu experimental features. Firefox gave RuntimeError: WebAssembly stack switching not supported in this JavaScript runtime. Some examples that use uint8 don't work but this is a great start 🥳 |
Sorry, something went wrong.
|
The toolbar requires imgui, was easy to get it working by just adding "imgui_bundle" to the deps. The way micropip resolves dependencies is a little odd to me, especially when you use .whl instead of a wheel on the index. Like the order matters for version conflicts etc. I saw imgui examples working, but that's due to a pyodide helper. I will add imgui_bundle to the list of deps there. Which might not be needed for every single example anyway. Firefox gave RuntimeError: WebAssembly stack switching not supported in this JavaScript runtime. JSPI is used by pyodide to await an async function, it's used for the wgpu-py mappings. Looks like it's still worked on for Firefox. I don't think moving pygfx to all async would drop that requirement... not sure. Some examples that use uint8 don't work Can you share which examples caused errors for you? I can look into if they are are an issue in my wgpu changes that needs fixes. |
Sorry, something went wrong.
|
Firefox gave RuntimeError: WebAssembly stack switching not supported in this JavaScript runtime. JSPI is used by pyodide to await an async function, it's used for the wgpu-py mappings. Looks like it's still worked on for Firefox. I don't think moving pygfx to all async would drop that requirement... not sure. This is the full traceback I get on firefox, maybe I can try updating. Failed to load: PythonError: Traceback (most recent call last): File "/lib/python313.zip/_pyodide/_base.py", line 597, in eval_code_async await CodeRunner( ...<9 lines>... .run_async(globals, locals) File "/lib/python313.zip/_pyodide/_base.py", line 411, in run_async coroutine = eval(self.code, globals, locals) File "", line 11, in File "/lib/python3.13/site-packages/fastplotlib/__init__.py", line 6, in from .utils import loop # noqa ^^^^^^^^^^^^^^^^^^^^^^^ File "/lib/python3.13/site-packages/fastplotlib/utils/__init__.py", line 5, in from .functions import * File "/lib/python3.13/site-packages/fastplotlib/utils/functions.py", line 7, in from pygfx import Texture, Color File "/lib/python3.13/site-packages/pygfx/__init__.py", line 9, in from .objects import * File "/lib/python3.13/site-packages/pygfx/objects/__init__.py", line 79, in from ._ruler import Ruler File "/lib/python3.13/site-packages/pygfx/objects/_ruler.py", line 8, in from ._text import MultiText File "/lib/python3.13/site-packages/pygfx/objects/_text.py", line 43, in from ..utils import text as textmodule File "/lib/python3.13/site-packages/pygfx/utils/text/__init__.py", line 17, in from ._fontmanager import FontProps, FontManager, font_manager # noqa: F401 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/lib/python3.13/site-packages/pygfx/utils/text/_fontmanager.py", line 369, in font_manager = FontManager() File "/lib/python3.13/site-packages/pygfx/utils/text/_fontmanager.py", line 130, in __init__ self._load_fonts() ~~~~~~~~~~~~~~~~^^ File "/lib/python3.13/site-packages/pygfx/utils/text/_fontmanager.py", line 165, in _load_fonts for ff in get_all_fonts(): ~~~~~~~~~~~~~^^ File "/lib/python3.13/site-packages/pygfx/utils/text/_fontfinder.py", line 214, in get_all_fonts return get_builtin_fonts() | get_system_fonts() ~~~~~~~~~~~~~~~~~^^ File "/lib/python3.13/site-packages/pygfx/utils/text/_fontfinder.py", line 220, in get_builtin_fonts return {FontFile(p) for p in file_paths} ~~~~~~~~^^^ File "/lib/python3.13/site-packages/pygfx/utils/text/_fontfinder.py", line 71, in __init__ self._get_face() # load it early so the direct access to freetype attributes is skipped... feels janky. ~~~~~~~~~~~~~~^^ File "/lib/python3.13/site-packages/pygfx/utils/text/_fontfinder.py", line 97, in _get_face run_sync(face.load().then(lambda _: document.fonts.add(face), lambda err: print(err))) ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RuntimeError: WebAssembly stack switching not supported in this JavaScript runtime
Some examples that use uint8 don't work Can you share which examples caused errors for you? I can look into if they are are an issue in my wgpu changes that needs fixes. I got it in the ndwidget branch, but that might be because we recently allowed direct uint8 textures there. But in main the colormap picker doesn't show up when you right-click the colorbar in the image_widget/image_widget.py example. I wonder if it's an issue with the wgpu texture upload. It's basically this wgpu-imgui example: https://github.com/pygfx/wgpu-py/blob/main/examples/imgui_cmap_picker.py |
Sorry, something went wrong.
|
the colormap picker doesn't show up when you right-click the colorbar in the image_widget/image_widget.py example. I wonder if it's an issue with the wgpu texture upload. It's basically this wgpu-imgui example: https://github.com/pygfx/wgpu-py/blob/main/examples/imgui_cmap_picker.py ah, that was one example I never tried in wgpu-py and it also had this issue (it spews your browser console with a sorta helpful message). I pushed a commit that fixes the behaviour. But I think there might be an actual problem with how we transform data from python to js (some dimension of the shape might be lost), as the default (no args) for .create_view() seems to behave as expected natively but not in the browser. I will look into that later, thanks! |
Sorry, something went wrong.
|
I wonder what performance we can get with compute shaders here, like what's the overhead. When I get a chance I want to try running this stuff on it: https://github.com/kushalkolar/wgpu-sparse-matvec |
Sorry, something went wrong.
|
I am currently seeing about 20% performance compared to native when you look at framerates. But there is a lot to that. Moving data is probably the worst right now since it's doing 3 or more copies. And scheduling as issues because of the waiting and being driven by the browser framerate too. I think one limitation with wasm is 2GB of memory. But not sure if that applies to resources on the GPU |
Sorry, something went wrong.
|
Not a webdev so the styling is all awful - but this works locally when I built the docs (plenty of issues with duplication for example). And the imgui cmap picker works! interactive_docs.mp4I didn't want to mess the ci job for docs preview but I think it could work. Let me know if that is something I should try (also found additional issues for myself, something about unmapped buffers in the surface_ripple example). |
Sorry, something went wrong.
|
Hi! Dear ImGui Bundle author here (@Vipitis and I have talked on the bundle discord). First, congrats on this effort! Having worked on a similar approach, I know that this is a very complex endeavor. Main suggestion: reuse the Pyodide runtime between examples instead of calling loadPyodide() per page. Most of the per-example wait is not downloading the wheels (those get cached) but re-installing them into the fresh in-memory filesystem and re-initializing the interpreter. In the imgui_bundle playground (https://imgui-bundle.pages.dev/playground/), all demos share one runtime (and one canvas in my case): switching demos is near-instant because we only stop the previous app's render loop, tear it down, and exec the new code in the same runtime. How imgui_bundle supports this, in case it's a useful reference:
Since fastplotlib drives its own loop via rendercanvas, this is not drop-in for you. But the same pattern may apply: keep one runtime alive, add a "stop the current loop and tear down" hook on the rendercanvas side, then run the next example against the same runtime and canvas. Second suggestion: embrace the async model end to end. Two things mattered when I adapted imgui_bundle's runners to the browser:
A nice side effect of this fully callback-driven approach: it never needs run_sync, and therefore does not depend on JSPI ("WebAssembly stack switching", the experimental feature that lets run_sync suspend synchronous code while a promise resolves; currently Chrome-only). That is why the playground also works on Firefox, and it matches your hunch that moving fully async would remove the JSPI requirement. For what it's worth, when I tried this PR on Firefox, the first failure I hit was the font loading in pygfx: run_sync(face.load().then(...)) in pygfx/utils/text/_fontfinder.py. It would be an interesting candidate to convert to a plain await. One last trick: a synchronous download is still possible without JSPI, because the legacy synchronous XMLHttpRequest blocks inside the browser itself, not in the WASM stack. imgui_bundle uses this in immapp.download_url_bytes (binary data needs a small base64 detour, see the code; the tab is frozen while downloading, so it's for one-shot loading, not mid-frame use). It could be a Firefox-compatible replacement for (note that it only works for downloads though). Finally, there are more recent wheels for imgui bundle here : https://imgui-bundle.pages.dev/local_wheels/ Happy to discuss details here or on discord. |
Sorry, something went wrong.
|
hey @pthom thanks for you advice. I can see the benefit in all of and really think the playground kinda approach would work out. We can gladly chat more on Discord as I have some additional ideas. I'd say that this PR is way ahead of the current blockers which are still in wgpu-py. Pygfx and fastplotlib should just work once that is ready and released. How interactive docs/gallery might look and work is more of a design decision not really up to me. |
Sorry, something went wrong.
Hey,
as I getting wgpu-py and pygfx to run in the browser I also tried some of the fpl examples to find some more problems.
thought I will just share the script which is really janky and adapted from rendercanvas, wgpu and pygfx... but mostly works. It includes a bunch of hacks to make examples work. It also pulls in the dependencies from the PR branch doc previews... so this might break when those are updated or merged etc.
I haven't looked into any fpl specific problems with pyodide, but there are a few I saw already (like the tool bar is missing?, windows overlapping?) - just clicking around to see any issues that aren't covered in the wgpu and pygfx examples and already found something that is now fixed.
to try this yourself, simply clone this branch (or copy the script really...)
I only tested this on Win10 with Chrome but you should get this list of all the example files and most will run. You only run the script to patch in the local fpl wheel and serve the http. All execution is happening in the browser using pyodide. (so you could theoretically copy the html source and host it statically anywhere)
fpl_snippet.mp4load times and performance is really bad at the moment.
I haven't looked into how the docs work for fpl, there is a really janky iframe injection that puts embeds interactive examples in the pygfx docs for example which I might be able to port that to here to make it even more accessible.
let me know if you have any questions or suggestions and I will see how much time I can find - just really excited for what the future will enable with pyodide support.