Skip to content

Rendering from Rust's wgpu and Pyo3 #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
rafaelbeckel opened this issue Mar 13, 2025 · 4 comments
Open

Rendering from Rust's wgpu and Pyo3 #64

rafaelbeckel opened this issue Mar 13, 2025 · 4 comments

Comments

@rafaelbeckel
Copy link
Contributor

rafaelbeckel commented Mar 13, 2025

Hey team!

I'm writing a rendering library in Rust and want to implement Python support for it with Pyo3.

I'm reading rendercanvas's documentation and trying to understand how to get a Window handle in Rust from a RenderCanvas instance from Python.

My library controls the wgpu context, so I'm trying to achieve surface creation from a Window handle in Rust. I can already access the RenderCanvas instance from Rust and call its methods. Is there any method in the API that provides a reference to the internal Window handle?

Can I somehow get it from the built-in get_context("wgpu")? Where does this "wgpu" context come from? Will it conflict with my internal wgpu context? Should I implement the ContextInterface in my library instead?

Thanks in advance!

@rafaelbeckel
Copy link
Contributor Author

Digging at the code, the answer is that .get_context("wgpu") uses pygfx's wgpu Python module (based on wgpu-native C bindings) to create the context internally, which would conflict with my Rust implementation.

To provide the same context from Rust, I must implement the ContextInterface as described here.

I'll document my learnings as I go and submit a PR expanding the docs when I finish the integration.

@almarklein
Copy link
Member

Hi @rafaelbeckel !

To answer your first question, you can call the canvas._rc_get_present_methods() method to get the presenting capabilities of the canvas. For a canvas that supports rendering to screen, it will provide the window id via the dict that that method returns.

To hook up your own render library, you indeed need to implement the ContextInterface. This is basically an adapter between the thing that does the rendering, and the rendercanvas (the thing that does the presenting).

Also have a look at the implementation in wgpu-py:
https://github.com/pygfx/wgpu-py/blob/f20ec990ba0d7352b00761edfb4bf64650efcc93/wgpu/_classes.py#L204
https://github.com/pygfx/wgpu-py/blob/f20ec990ba0d7352b00761edfb4bf64650efcc93/wgpu/backends/wgpu_native/_api.py#L573

@rafaelbeckel
Copy link
Contributor Author

rafaelbeckel commented Mar 15, 2025

Thanks for pointing out the implementation!

I managed to get the Window handle from _rc_get_present_methods() and convert it to a window handle in Rust, but now the Window crashes immediately after starting when I try to create the surface for it.

It could be due to AppKit requiring it to be created in the main thread or something else. I'm investigating it, but as far as RenderCanvas' scope goes, this issue is solved.

I have another question:

When exactly is the rendercanvas_context_hook function called?

I created the hook in my library as a top-level function, but it's never called. When I call canvas.get_context("mylibname.MyContextObject") from Python, it calls MyContextObject constructor directly (no hook).

Edit:

Oh, I got it. The hook is called when I don't specify the class in get_context():

  • get_context("mylibname") Calls the hook.
  • get_context("mylibname:MyContextObject") Calls MyContextObject constructor.

This allows me to implement multiple initializers and set a default one.

@almarklein
Copy link
Member

almarklein commented Mar 17, 2025

This allows me to implement multiple initializers and set a default one.

The mean reason for the hook is that you can tell your users to just use get_context("your_lib_name"), which is a lot easier to remember.

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

No branches or pull requests

2 participants