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
GBM/X11/Wayland: Implement asynchronous texture uploads #25139
base: master
Are you sure you want to change the base?
Conversation
Looks promising 🙂 I gave it a quick test on Wayland and GBM (GL + GLES) and noticed issue with thumbnail creation: Many errors like these in the log:
Without Full log: https://paste.kodi.tv/akuficugab.kodi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure what is async about this as it effectively blocks the current thread until the upload is done? So it really only works if the upload is happening from another thread? Do you know how many texture uploads happen in other threads? (I'm on my phone so I'm being lazy and asking other than checking myself).
The switching of the eglcontexts could be problematic if synchronization isn't handled correctly.
I wonder if it would be better to have a shared EGL context that is bound in another thread that basically just waits for uploads to enter a queue. This would avoid the need for locking and switching contexts.
@@ -229,6 +229,11 @@ void CGLTexture::LoadToGPU() | |||
m_loadedToGPU = true; | |||
} | |||
|
|||
void CGLTexture::SyncGPU() | |||
{ | |||
glFinish(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This effectively blocks no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's desired. It blocks the upload thread, and waits until the texture is visible for the GPU.
return false; | ||
} | ||
|
||
m_textureUploadLock.lock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love the fact of locking in the middle of these functions. I assume because the texture uploads may happen from other threads? It would be nice if there was the ability to add texture uploads to a queue to avoid needing the lock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there can be multiple jobs in parallel. Only one can have the upload context.
* \brief Binds a shared context to the current thread, in order to upload textures asynchronously. | ||
* \return Return true if a texture upload context exists and the binding succeeds. | ||
*/ | ||
virtual bool BindTextureUploadContext() { return false; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to avoid polluting CWinSystemBase with more stuff. Can this be moved somewhere else? Possibly the caller can cast the window system to the EGL intermediate class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've experimented with that, but I couldn't come up with an universal solution which does not look hacky. If you could give me some pointers, I'd appreciate it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See here:
auto winSystemEGL = |
The problem is that x11 uses a different EGL context class so that might be annoying. It was always the intention to get x11 to use the same EGL class that wayland and gbm are using, it just hasn't happened yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's why I shelved the branch for a long time. I'm still running X11. I'll see what I can do.
On the flip side, I believe that GLX and DX11 can have a very similar implementations. It is not EGL specific, if that's your concern.
xbmc/utils/EGLUtils.cpp
Outdated
return false; | ||
} | ||
|
||
bool success = eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this not bind back to the other context?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The job has no context to begin with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't this effectively unbind the main context if called from the main thread? I don't think we bind the main thread context unless windowing is being inited
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it checks if there is a context bound, and bails if so.
And in its current state, it is only called in the threaded texture loader.
To give some context how (I think) the current system works: XBT textures are done synchronously. They are loaded from disk and uploaded to the GPU in the main rendering thread, blocking execution. This might stall the renderer, but the textures are guaranteed to be available for the next frame. For assets like fanart or thumbnails, a upload job gets spawned. This job reads, decodes, converts and scales the texture in its own thread; otherwise we could stall for seconds. When the job is done, the main thread picks up the prepared texture and uploads it to the GPU. This stalls the thread. My PR gives the Jobs a GL/GLES context in which they can execute GL calls. So in addition of doing all the normal texture processing, the job also uploads the texture to the GPU. |
For caching, it is expected to keep the intermediate CPU buffer alive. But uploading deletes it, in this case right before the caching. I've moved the call for the async upload to the actual texture processing job, so the texture can be cached before upload. |
Tested the latest version and can confirm that caching images works as expected 🙂 I also think that making the calls to |
Maybe LoadToGPUThreadSafe? |
Doesn't sound right to me. Especially because it's not safe to call from the render thread, right? Anyway, that shouldn't be a blocker, and from my side this looks pretty solid 🙂 |
It is safe to call from the rendering thread. It checks if there is a context bound, and bails if so. I don't really like bringing the "context" into the name, because I think it will be different for Vulkan. For DX11, it should be similar tho. |
Ahh, that's what the |
Description
This PR implements asynchronous texture uploads, by having a helper EGL context.
Motivation and context
Previously, the texture uploads of assets (fanart, thumbs, etc.) were synchronous with the UI rendering, despite most of the texture pipeline (reading, decoding, converting, scaling) being asynchronous.
This meant that the main thread would need to wait for the upload to complete, which would block execution. While I don't have concrete numbers, the workload of processing a typical 1920x1080 texture (memcopy + driver stuff) could tax a low-end device for quite some time, which normally lead to dropped frames.
While there is no guarantee that async uploads will behave better, there is much more room for the driver to perform well. I expect Mesa to perform better.
The feature is currently gated behind an advanced setting:
The async upload could be further refined by uploading the texture in chunks, with pauses in between. In testing, pauses between texture processing steps reduced dips in framerate, presumably by relieving stress on the memory bus.
How has this been tested?
Runs fine on X11 with GL and GLES.
A prototype ran fine on GBM on my Ordroid C2, but needs retesting.
Wayland is fully untested.
What is the effect on users?
Less jank, better system performance.
Screenshots (if appropriate):
Estuary with synchronous (UI texture) upload disabled, only asynchronously uploaded textures are being shown (text is still synchronous):
Types of change
Checklist: