softbuffer icon indicating copy to clipboard operation
softbuffer copied to clipboard

Multi-threaded

Open lylythechosenone opened this issue 1 year ago • 16 comments

Currently, Context and Surface are neither Send nor Sync. What is the reason for this decision? Is it possible to make them at least Send, so they could be made Sync with a Mutex?

lylythechosenone avatar Mar 12 '24 18:03 lylythechosenone

I can't speak for other backends, but at least for Web this isn't possible (without a custom wire implementation which comes with significant overhead).

This introduces the problem of cross-platform compatibility, either all backends have to be Send or none can.

daxpedda avatar Mar 13 '24 09:03 daxpedda

Hmm... In wgpu, there is actually a different concept—all platforms require Send + Sync except web. I think if we could implement Send + Sync for everything except web, that would be completely fine.

However, I can see why it would be undesirable to have different trait implementations on different platforms.

lylythechosenone avatar Mar 13 '24 10:03 lylythechosenone

As a maintainer of the Web backend I'm fine with going down that route as well.

daxpedda avatar Mar 13 '24 11:03 daxpedda

For Windows and macOS this would require a significant reworking of how the backends work. winit makes it work on these platforms by sending messages across threads; since softbuffer deals with large buffers of data this is impractical.

(In a future release Window probably won't be Send + Sync, JFYI)

notgull avatar Mar 13 '24 14:03 notgull

You mean winit::Window? I'll relay this to the wgpu team and see if they have similar plans.

lylythechosenone avatar Mar 13 '24 21:03 lylythechosenone

@notgull so to be fully clear, this is a platform limitation, and will likely not be changed by the softbuffer team?

lylythechosenone avatar Mar 16 '24 16:03 lylythechosenone

@notgull so to be fully clear, this is a platform limitation, and will likely not be changed by the softbuffer team?

Raymond Chen confirms that device contexts can't be sent between threads. To quote:

The thread that calls functions such as GetDC must also be the one that calls ReleaseDC, but as with window handles, during the lifetime of the DC, any thread can use it. If you choose to use a DC in a multi-threaded manner, it’s your responsibility to coordinate the consumers of that device context so that only one thread uses it at a time

Essentially, a DC is Send in that other threads can use it (at least when given an &mut reference), but it is !Send in that it has to be dropped on the same thread that created it. Therefore Surface on Win32 is pretty unavoidably !Send.

A similar conundrum affects window handles in winit. However winit can get around this by sending a message to the main thread that then destroys the window on the thread that created it. Even if we had the ability to access this mechanism from softbuffer, it would add a significant amount of synchronization overhead to this crate.

I think something similar happens with macOS, but we should check with @madsmtm about that.

X11 is perfectly thread safe, by the way, and could be Send if it wanted to. But for consistency with other platforms it is kept !Send.

notgull avatar Mar 16 '24 17:03 notgull

On macOS, the window handle is basically an &Arc<NSView> that can be cloned and passed between threads safely, but you can only ever access anything from the main thread, including creating a surface / rendering context and attaching that to it. So ideally, we should restrict surface (and context) creation to the main thread.

The rendering can be done from another thread though, i.e. I'm fairly sure the Surface (inner: CALayer) is thread safe, and could be marked Send + Sync (you often need to do a bit of synchronization to not get tearing and other artifacts, so I can't comment on whether it would be a good idea or not).

madsmtm avatar Mar 17 '24 16:03 madsmtm

I'd be alright with Send and not Sync, because then one could use a Mutex to get both. That is something I am willing to do. However, because it has neither, even Mutex currently does not work.

lylythechosenone avatar Mar 17 '24 18:03 lylythechosenone

Even if we had the ability to access this mechanism from softbuffer, it would add a significant amount of synchronization overhead to this crate.

@notgull what makes it impossible? Also, would that overhead exist even if only that singular message is required? I.e. only Send, and not Sync.

lylythechosenone avatar Mar 17 '24 18:03 lylythechosenone

@notgull what makes it impossible?

Unlike macOS, Windows doesn't have a singular "run this function on the GUI thread" function. The intended solution is to post a message to the window which will then be processed on the window's main thread. The problem is that the message is intended to be handled by the window's WndProc (window procedure), which is controlled by the windowing framework.

The intended solution to this is subclassing, where a second WndProc is uploaded to the window and used as an "additional" window procedure. The issue with this is that the subclassing itself is not thread safe, so we would have to be careful about how we do it.

notgull avatar Mar 17 '24 22:03 notgull

Actually, this would be 100% safe if it were guaranteed that the HWND wasn't allowed to leak outside of the origin thread. I've filed https://github.com/rust-windowing/winit/pull/3593 to fix this.

notgull avatar Mar 17 '24 22:03 notgull

Very nice.

lylythechosenone avatar Mar 17 '24 22:03 lylythechosenone

Thanks!

AustinMReppert avatar Apr 27 '24 23:04 AustinMReppert

@notgull now that that's merged, are we any further to multi-threaded support?

lylythechosenone avatar May 01 '24 18:05 lylythechosenone

@notgull now that that's merged, are we any further to multi-threaded support?

I've filed a PR, but it needs more testing

notgull avatar May 08 '24 03:05 notgull