-
Notifications
You must be signed in to change notification settings - Fork 73
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
Add multithreading option to gl port #294
base: master
Are you sure you want to change the base?
Conversation
While it possibly has less effect - shouldn't this applied identical to other ports, at least SDL2? |
It could actually be even more useful in other ports, as it sidesteps basically all issues with slow rendering. The rendering/windowing APIs they use will likely mandate different approaches to multithreading, and I'm not familiar with their details. I know that OpenGL only allows a GL context being used from one thread at a time, which is why there's the This is actually also why this PR had to rework some of the glyph caching stuff as well; we can't load the glyph textures during SDL2 has a blanket recommendation to not call the built-in video functions from anywhere else than the main thread (SDL_Renderer, SDL_Surface software rendering etc). This shouldn't affect the OpenGL port since it's not really using those functions, but OpenGL directly instead. But it does mean that trying to apply this same rendering thread approach to the SDL2 port is a bit iffy. |
Seems like a good idea to me. Actually, I'd been thinking along these lines, but thought : do we really want to deal with threads in Windows, OS/X, Linux, BSD, and OS/2 (yes, there are people using the SDL2 platform in OS/2)? I'd forgotten that SDL2 includes thread-handling routines. On non-SDL2 platforms... well, in truth, if you're trying to display, say, 1000 frames/second, the Right Thing To Do is to render 60 frames/second. On X11 or VT, say, you could render 1000 frames/second, making your fans spin wildly, and your brain and eyes would only be capable of seeing 6% of them anyway. For that matter, on this platform, you can tell PDCursesMod to render 1000 fps, and 94% of them will get thrown out. Generally speaking, if your program is generating more than 60 fps, you should stop and ask yourself why. |
Yeah, it would be preferrable from the library's point of view that the user program wouldn't spam frames that are never going to be shown anyway. The (simplified) use case I have for this is a program that has an adjustable rate of cumulative state updates, meaning it draws on top of what was previously drawn, one step at a time. At its slowest at like 200 ms per step, it's easy to implement in such a way that the program sets I could use some scheme to start skipping My personal goal for the GL port is to have performance be a non-issue on every supported platform in every situation. Still, I do acknowledge that most programs don't care and performance pitfalls are usually easy to avoid on the user side, so it's probably not worth spending non-trivial effort for other ports. This threaded approach doesn't directly translate to power savings either, unless the program is actually trying exceed 60 fps (or whatever the user's display refresh rate is). |
So... should there be:
Further more, should there be
|
This PR adds a multithreaded mode to the GL port. It can be enabled and disabled through the new
pdc_threading_mode
variable. It is currently enabled by default in this PR branch.If the multithreaded mode is enabled, OpenGL rendering is delegated to a separate thread. This thread continuously renders the latest state submitted from
PDC_doupdate()
. In this mode, vertical sync is also enabled, meaning that rendering should be done no faster than the display's refresh rate, regardless of how oftenPDC_doupdate()
gets called. The rendering thread also knows when no new frames have been submitted, and pauses rendering until there is a new frame.The reason why this definitely needs to be done in a separate thread instead of just skipping rendering sometimes in
PDC_doupdate()
, is that the latter approach could skip the last frame in a long "animation" sequence before a pause, leaving the wrong image on screen until the nextPDC_doupdate()
happens sometime much later. The thread doesn't have this issue, as it can redraw at any time afterPDC_doupdate()
has been called and make sure that the latest available frame is on screen at all times.In practice, the multithreaded mode completely decouples OpenGL rendering performance from the curses refresh rate: the GPU doesn't have to render every frame submitted from curses and curses API functions aren't affected by rendering performance.
In single-threaded mode,
speed
gives me around 5700 frames/sec withPDC_FONT_SIZE=17
, and 2650 frames/sec withPDC_FONT_SIZE=64
. In the new multi-threaded mode, I get around 15500 frames/sec, and as expected, this is independent of font size! The program is completely CPU-bound in multithreaded mode; the main thread ofspeed
is now running at 100% core utilization.Do you think the threading mode should be an environment variable? Either way, I know that I'd like to keep it optional, since multithreading can make debugging harder, and there may be use cases where
PDC_doupdate()
should be guaranteed to always render (even if the frame would be skipped by the screen, it could be captured by some tool like RenderDoc).