From 989301a89b0f618c20a93b245f783e4c812836eb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 16 Mar 2020 17:24:25 +0100 Subject: [PATCH 1/3] render/egl: add support for explicit sync extensions --- include/render/egl.h | 12 +++++++ render/egl.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/include/render/egl.h b/include/render/egl.h index f77e11476..2faad1b04 100644 --- a/include/render/egl.h +++ b/include/render/egl.h @@ -37,6 +37,10 @@ struct wlr_egl { PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT; PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT; PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT; + PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; + PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; + PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID; + PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR; } procs; bool has_modifiers; @@ -113,4 +117,12 @@ bool wlr_egl_unset_current(struct wlr_egl *egl); bool wlr_egl_is_current(struct wlr_egl *egl); +EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd); + +void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync); + +int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync); + +bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync); + #endif diff --git a/render/egl.c b/render/egl.c index 168e358c6..6672cb1d1 100644 --- a/render/egl.c +++ b/render/egl.c @@ -323,6 +323,18 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay *display) { egl->exts.IMG_context_priority = check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); + if (check_egl_ext(display_exts_str, "EGL_KHR_fence_sync") && + check_egl_ext(display_exts_str, "EGL_ANDROID_native_fence_sync")) { + load_egl_proc(&egl->procs.eglCreateSyncKHR, "eglCreateSyncKHR"); + load_egl_proc(&egl->procs.eglDestroySyncKHR, "eglDestroySyncKHR"); + load_egl_proc(&egl->procs.eglDupNativeFenceFDANDROID, + "eglDupNativeFenceFDANDROID"); + } + + if (check_egl_ext(display_exts_str, "EGL_KHR_wait_sync")) { + load_egl_proc(&egl->procs.eglWaitSyncKHR, "eglWaitSyncKHR"); + } + wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str); if (device_exts_str != NULL) { @@ -954,3 +966,65 @@ int wlr_egl_dup_drm_fd(struct wlr_egl *egl) { return render_fd; } + +EGLSyncKHR wlr_egl_create_sync(struct wlr_egl *egl, int fence_fd) { + if (!egl->procs.eglCreateSyncKHR) { + return EGL_NO_SYNC_KHR; + } + + EGLint attribs[3] = { EGL_NONE }; + int dup_fd = -1; + if (fence_fd >= 0) { + dup_fd = fcntl(fence_fd, F_DUPFD_CLOEXEC, 0); + if (dup_fd < 0) { + wlr_log_errno(WLR_ERROR, "dup failed"); + return EGL_NO_SYNC_KHR; + } + + attribs[0] = EGL_SYNC_NATIVE_FENCE_FD_ANDROID; + attribs[1] = dup_fd; + attribs[2] = EGL_NONE; + } + + EGLSyncKHR sync = egl->procs.eglCreateSyncKHR(egl->display, + EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); + if (sync == EGL_NO_SYNC_KHR) { + wlr_log(WLR_ERROR, "eglCreateSyncKHR failed"); + if (dup_fd >= 0) { + close(dup_fd); + } + } + return sync; +} + +void wlr_egl_destroy_sync(struct wlr_egl *egl, EGLSyncKHR sync) { + if (sync == EGL_NO_SYNC_KHR) { + return; + } + assert(egl->procs.eglDestroySyncKHR); + if (egl->procs.eglDestroySyncKHR(egl->display, sync) != EGL_TRUE) { + wlr_log(WLR_ERROR, "eglDestroySyncKHR failed"); + } +} + +int wlr_egl_dup_fence_fd(struct wlr_egl *egl, EGLSyncKHR sync) { + if (!egl->procs.eglDupNativeFenceFDANDROID) { + return -1; + } + + int fd = egl->procs.eglDupNativeFenceFDANDROID(egl->display, sync); + if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) { + wlr_log(WLR_ERROR, "eglDupNativeFenceFDANDROID failed"); + return -1; + } + + return fd; +} + +bool wlr_egl_wait_sync(struct wlr_egl *egl, EGLSyncKHR sync) { + if (egl->procs.eglWaitSyncKHR(egl->display, sync, 0) != EGL_TRUE) { + wlr_log(WLR_ERROR, "eglWaitSyncKHR failed"); + return false; + } + return true; +} From ff1028b5450c77ec854a6bc855fe8d8613c281e1 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 16 Feb 2021 16:24:24 +0100 Subject: [PATCH 2/3] render/gles2: setup PBO extensions --- include/render/gles2.h | 5 +++++ render/gles2/renderer.c | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/include/render/gles2.h b/include/render/gles2.h index 714bacf56..c60159d67 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -48,6 +48,9 @@ struct wlr_gles2_renderer { bool EXT_texture_type_2_10_10_10_REV; bool OES_texture_half_float_linear; bool EXT_texture_norm16; + bool NV_pixel_buffer_object; + bool OES_mapbuffer; + bool EXT_map_buffer_range; } exts; struct { @@ -57,6 +60,8 @@ struct wlr_gles2_renderer { PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR; PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR; PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES; + PFNGLMAPBUFFERRANGEEXTPROC glMapBufferRangeEXT; + PFNGLUNMAPBUFFEROESPROC glUnmapBufferOES; } procs; struct { diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 5a2f31b55..c74df637c 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -780,6 +780,19 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { "glEGLImageTargetRenderbufferStorageOES"); } + renderer->exts.NV_pixel_buffer_object = + check_gl_ext(exts_str, "GL_NV_pixel_buffer_object"); + + if (check_gl_ext(exts_str, "GL_OES_mapbuffer")) { + renderer->exts.OES_mapbuffer = true; + load_gl_proc(&renderer->procs.glUnmapBufferOES, "glUnmapBufferOES"); + } + if (check_gl_ext(exts_str, "GL_EXT_map_buffer_range")) { + renderer->exts.EXT_map_buffer_range = true; + load_gl_proc(&renderer->procs.glMapBufferRangeEXT, + "glMapBufferRangeEXT"); + } + if (renderer->exts.KHR_debug) { glEnable(GL_DEBUG_OUTPUT_KHR); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); From ee7a9de7cbfb341f1db11c7c0d137620978019d5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 16 Feb 2021 16:33:07 +0100 Subject: [PATCH 3/3] wip: render/gles2: use PBOs for read_pixels --- include/render/egl.h | 1 + render/egl.c | 1 + render/gles2/renderer.c | 88 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/include/render/egl.h b/include/render/egl.h index 2faad1b04..2663ecf4f 100644 --- a/include/render/egl.h +++ b/include/render/egl.h @@ -41,6 +41,7 @@ struct wlr_egl { PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID; PFNEGLWAITSYNCKHRPROC eglWaitSyncKHR; + PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR; } procs; bool has_modifiers; diff --git a/render/egl.c b/render/egl.c index 6672cb1d1..d4af13bb3 100644 --- a/render/egl.c +++ b/render/egl.c @@ -329,6 +329,7 @@ static bool egl_init_display(struct wlr_egl *egl, EGLDisplay *display) { load_egl_proc(&egl->procs.eglDestroySyncKHR, "eglDestroySyncKHR"); load_egl_proc(&egl->procs.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID"); + load_egl_proc(&egl->procs.eglClientWaitSyncKHR, "eglClientWaitSyncKHR"); } if (check_egl_ext(display_exts_str, "EGL_KHR_wait_sync")) { diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index c74df637c..f1f552342 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -445,28 +446,105 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, push_gles2_debug(renderer); - // Make sure any pending drawing is finished before we try to read it - glFinish(); - glGetError(); // Clear the error flag unsigned char *p = (unsigned char *)data + dst_y * stride; uint32_t pack_stride = width * drm_fmt->bpp / 8; + unsigned char *out_p = p; + +#if 1 + GLuint pbo = 0; +#else + static GLuint pbo = 0; +#endif + bool use_pbo = true; + if (renderer->exts.NV_pixel_buffer_object && renderer->exts.OES_mapbuffer && + renderer->exts.EXT_map_buffer_range && use_pbo) { + // Ideally we should use the GL_STREAM_READ usage instead of + // GL_STREAM_DRAW, however it's not available in GLES2. It's just a + // hint and it's in general ignored by GL implementations, see: + // https://github.com/KhronosGroup/OpenGL-API/issues/66 + + if (pbo == 0) { + wlr_log(WLR_DEBUG, "create PBO"); + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, pbo); + glBufferData(GL_PIXEL_PACK_BUFFER_NV, stride * height, NULL, + GL_STREAM_DRAW); + } else { + glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, pbo); + } + + out_p = NULL; + } + if (pack_stride == stride && dst_x == 0) { // Under these particular conditions, we can read the pixels with only // one glReadPixels call - glReadPixels(src_x, src_y, width, height, fmt->gl_format, fmt->gl_type, p); + wlr_log(WLR_DEBUG, "glReadPixels start"); + glReadPixels(src_x, src_y, width, height, fmt->gl_format, fmt->gl_type, out_p); + wlr_log(WLR_DEBUG, "glReadPixels end"); } else { // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read // the lines out row by row for (size_t i = 0; i < height; ++i) { uint32_t y = src_y + i; glReadPixels(src_x, y, width, 1, fmt->gl_format, - fmt->gl_type, p + i * stride + dst_x * drm_fmt->bpp / 8); + fmt->gl_type, NULL /* TODO */); } } + if (pbo != 0) { + glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, 0); + glFlush(); + + EGLSyncKHR sync = wlr_egl_create_sync(renderer->egl, -1); + +#if 0 + int fence_fd = wlr_egl_dup_fence_fd(renderer->egl, sync); + + wlr_log(WLR_DEBUG, "poll start %d", fence_fd); + struct pollfd pollfd = { .fd = fence_fd, .events = POLLIN }; + if (poll(&pollfd, 1, -1) <= 0) { + wlr_log(WLR_ERROR, "poll failed"); + } + wlr_log(WLR_DEBUG, "poll end"); + + close(fence_fd); +#else + wlr_log(WLR_DEBUG, "eglClientWaitSyncKHR start"); + EGLint ret = renderer->egl->procs.eglClientWaitSyncKHR( + renderer->egl->display, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, + EGL_FOREVER_KHR); + if (ret != EGL_CONDITION_SATISFIED_KHR) { + wlr_log(WLR_ERROR, "eglClientWaitSyncKHR failed"); + return false; + } + wlr_log(WLR_DEBUG, "eglClientWaitSyncKHR end"); +#endif + + wlr_egl_destroy_sync(renderer->egl, sync); + + wlr_log(WLR_DEBUG, "glMapBufferRangeEXT"); + + // glMapBufferOES doesn't allow for read access, so we need to use + // glMapBufferRangeEXT instead + glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, pbo); + unsigned char *in = renderer->procs.glMapBufferRangeEXT( + GL_PIXEL_PACK_BUFFER_NV, 0, stride * height, GL_MAP_READ_BIT_EXT); + // TODO: error handling + + wlr_log(WLR_DEBUG, "memcpy"); + memcpy(p, in, stride * height); + + wlr_log(WLR_DEBUG, "glUnmapBufferOES"); + renderer->procs.glUnmapBufferOES(GL_PIXEL_PACK_BUFFER_NV); + glBindBuffer(GL_PIXEL_PACK_BUFFER_NV, 0); + + wlr_log(WLR_DEBUG, "done!"); + } + pop_gles2_debug(renderer); if (flags != NULL) {