gfx-rs / gfx

[maintenance mode] A low-overhead Vulkan-like GPU API for Rust.
http://gfx-rs.github.io/
Apache License 2.0
5.35k stars 549 forks source link

Wayland Support for the OpenGL Backend #3545

Closed nemosupremo closed 3 years ago

nemosupremo commented 3 years ago

This PR isn't meant to be merged in the current state, but to illustrate what changes were need to get it working.

I've been trying to get the OpenGL backend working with wayland. The current device I'm targeting doesn't currently support Vulkan so I'm trying to get the wayland renderer working. During my tests I'm hitting some blockers with the hal::Instance API and I'm wondering if I'm doing something wrong, or if the API just wasn't developed with this in mind.

I'm hitting the following roadblocks:

  1. I need a pointer to this display to pass to get_platform_display. Both creating a new connection and using egl::DEFAULT_DISPLAY results in the window not working or rendering in the quad example. To get around this I'm passing a pointer to the create function via the version parameter.
  2. To create wl_egl_window_create I need to pass in a window size. I don't think this is a show stopper as long as there was some way get resize events so I could call wl_egl_window_resize. I think this can be done by calling wl_egl_window_resize in PresentationSurface::configure_swapchain

Another thing I had to remove the GL_COLORSPACE. This attribute is only supported in EGL 1.5, but currently are only testing 1.4. Seeing how the default value is GL_COLORSPACE_SRGB, then I just removed this attribute entirely.

Is there a proper way to overcome these hurdles without drastically modifying gfx's api?

nemosupremo commented 3 years ago

So, quickly, the code works (as in it actually renders quad.rs), but as you can imagine it hacks the API.

kvark commented 3 years ago

Alright, so we have 2 major issues so far:

  1. The wl_display from winit is not compatible with wl_display that we could create in Instance::new. We need to figure out why exactly this is the case. If it just happens to be that your platform has the default WL display different from winit window, we should just make it so the surface (created from this window) can't be rendered to by the physical adapter from our Instance, so we'll never need to stitch the two wl_display things together.
  2. EGL needs the window size in order to create a surface. The main question to resolve (in order to make progress) here is to find out why X11 path doesn't require that. I suspect one possible answer is that X11 window handle already has that information associated with it, but WL surface doesn't. In this case, I suggest this to be a problem with RawWindowHandle, and we should file an issue accordingly and discuss this.

Edit: filed https://github.com/rust-windowing/raw-window-handle/issues/61

For (1) if we fail to use the default wl_display, we could also write the Wayland window integration in a way similar to the Web:

nemosupremo commented 3 years ago

wrt (2), I don't think this is an issue. You can call egl_window_create with some dummy resolution (I just call it with VGA 640×480), then when you configure the swap chain, you call egl_window_resize.

(1) seems to be the tricky one - I suspect wayland's design doesn't support intermixing of resources by two different wl_displays, so when we get our wl_surface from winit and try to use it libwayland hits an error. I don't think this has anything to do with the physical display (after all I only have one display), but something inherent with wayland's "secure by design" architecture. I'm going to try and take a deep dive into what the Vulkan instance does.

kvark commented 3 years ago

Digging up a bit... here is where winit establishes a Wayland display - https://docs.rs/wayland-client/0.28.2/wayland_client/struct.Display.html#method.connect_to_env

nemosupremo commented 3 years ago

For (1) if we fail to use the default wl_display, we could also write the Wayland window integration in a way similar to the Web:

This seems like the most successful path forward, but because we don't have a mutable reference to self, it would require wrapping everything in a mutex to get interior mutability. My plan was to leave eveerything as is (so enumerate_adapters still works) but then recreate the context once create_surface was called with a display other than our own. The downside of this would be calls to create_surface, enumerate_adapters and destroy_surface would be mutex protected even in X11 - would that cause an undue performance hit?

kvark commented 3 years ago

There is no performance concern about mutexing this :) I was wondering if the logic can all be tight in src/window/wayland.rs without affecting the main window/egl.rs module, but in the end you see this more clearly since you are writing the code. So I defer to your judgement on how to best roll this out.

fooishbar commented 3 years ago

The wl_display from winit is not compatible with wl_display that we could create in Instance::new. We need to figure out why exactly this is the case. If it just happens to be that your platform has the default WL display different from winit window, we should just make it so the surface (created from this window) can't be rendered to by the physical adapter from our Instance, so we'll never need to stitch the two wl_display things together.

You must use the same wl_display; objects such as wl_surface are not shareable between different instances of wl_display. The ID namespace is completely private to each client connection.

nemosupremo commented 3 years ago

Ok, taking into the account the design of wayland with the current API, I've gone the route of interior mutability. What I've done is move the create function largely into an Inner struct - that way it can easily be dropped/created in create_surface once we detect a new display. Under wayland when are asked to create a surface, we drop the previous context and recreate it. The X11 implementation is largely untouched from a functionality standpoint.

nemosupremo commented 3 years ago

The OpenGL renderer seems to work on wgpu using the gl renderer on a pi under wayland

wayland-screenshot-2020-12-22_00-21-01

kvark commented 3 years ago

bors retry

bors[bot] commented 3 years ago

Build succeeded: