danvd / wlroots-eglstreams

A modular Wayland compositor library with EGLStreams support
MIT License
105 stars 11 forks source link

eglStreamConsumerAcquireAttribNV sometimes fails with EGL_RESOURCE_BUSY_EXT #39

Open hansihe opened 2 years ago

hansihe commented 2 years ago

On some hardware, eglStreamConsumerAcquireAttribNV seems to fail with EGL_RESOURCE_BUSY_EXT on the first render.

This failure not being handled seems to make the EGLStream go out of sync, causing very unpredictable behavior as to what is displayed on the screen. It seems to me like the initial image is successfully presented, but then no further changes are displayed, freezing the screen. The compositor seems to function as normally otherwise.

I found some references to this error in this unpublished extension (Who even wrote this, and what is the current status? I have no idea). This seems to suggest that an acquire can be retried later on the same stream.

I wrote a hacky fix, which seemed to fix the problem.

diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c
index 3dd3902d..f760e990 100644
--- a/render/gles2/renderer.c
+++ b/render/gles2/renderer.c
@@ -370,9 +370,24 @@ static bool gles2_render_subtexture_with_matrix(
                if(egl->procs.eglQueryStreamKHR(egl->display, texture->stream,
                        EGL_STREAM_STATE_KHR, &state) == EGL_TRUE &&
                        (state != EGL_STREAM_STATE_EMPTY_KHR)) {
-                               egl->procs.eglStreamFlushNV(egl->display, texture->stream);
-                               egl->procs.eglStreamConsumerAcquireAttribNV(egl->display,
-                                       texture->stream, NULL);
+
+            egl->procs.eglStreamFlushNV(egl->display, texture->stream);
+
+            while (true) {
+                bool ok = egl->procs.eglStreamConsumerAcquireAttribNV(egl->display,
+                    texture->stream, NULL);
+                if (ok) break;
+
+                EGLint errcode = eglGetError();
+                if (errcode == EGL_RESOURCE_BUSY_EXT) {
+                    wlr_log(WLR_INFO, "got eglStreamConsumerAcquireAttribNV EGL_RESOURCE_BUSY_EXT, retrying...");
+                    sleep(1);
+                    continue;
+                }
+
+                wlr_log(WLR_ERROR, "eglStreamConsumerAcquireAttribNV failed with code %d", errcode);
+                return false;
+            }
                }
        }

This error only seems to occur once on the first render, so the only downside with this fix is an additional 1s delay on startup. It is obviously very suboptimal though.

As I am not very aware of the overall architecture of wlroots, I am not sure what a proper fix here would be. Do you have any suggestions?

danvd commented 2 years ago

Hi, @hansihe, thanks for the hack! I prettified it a bit: https://github.com/danvd/wlroots-eglstreams/blob/96929beacf6fee8a826a036c337e5e12dc5f5720/render/egl.c#L1283 It's used here, just like you suggested: https://github.com/danvd/wlroots-eglstreams/blob/96929beacf6fee8a826a036c337e5e12dc5f5720/render/gles2/renderer.c#L378

Please check if it works for you (my GTX 970 never suffer from such issue)