From ed7c694688a527708ef2e0c34a9a106fe59c1ada Mon Sep 17 00:00:00 2001 From: Chad Versace Date: Mon, 30 Apr 2018 22:35:17 -0700 Subject: [PATCH] egl/android: Implement EGL_KHR_mutable_render_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Specifically, implement the extension DRI_MutableRenderBufferLoader. However, the loader enables EGL_KHR_mutable_render_buffer only if the DRI driver implements its half of the extension, DRI_MutableRenderBufferDriver. Testing: - No change in dEQP-EGL.functional.* on Fedora 27, Wayland, Skylake GT2. Used deqp at tag android-p-preview-5. - No change in dEQP-EGL.functional.*, ran on Android on Chrome OS, Kabylake GT2. - Manually inspected Android apps on same Chrome OS device. Reviewed-by: Tapani Pälli --- src/egl/drivers/dri2/egl_dri2.c | 38 +++++- src/egl/drivers/dri2/egl_dri2.h | 7 + src/egl/drivers/dri2/platform_android.c | 168 +++++++++++++++++++++++- 3 files changed, 206 insertions(+), 7 deletions(-) diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c index ab4f134b7de..7fc7cb49703 100644 --- a/src/egl/drivers/dri2/egl_dri2.c +++ b/src/egl/drivers/dri2/egl_dri2.c @@ -272,7 +272,10 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id, _eglSetConfigKey(&base, EGL_MAX_PBUFFER_HEIGHT, _EGL_MAX_PBUFFER_HEIGHT); break; - + case __DRI_ATTRIB_MUTABLE_RENDER_BUFFER: + if (disp->Extensions.KHR_mutable_render_buffer) + surface_type |= EGL_MUTABLE_RENDER_BUFFER_BIT_KHR; + break; default: key = dri2_to_egl_attribute_map[attrib]; if (key != 0) @@ -432,6 +435,7 @@ static const struct dri2_extension_match optional_core_extensions[] = { { __DRI_IMAGE, 1, offsetof(struct dri2_egl_display, image) }, { __DRI2_FLUSH_CONTROL, 1, offsetof(struct dri2_egl_display, flush_control) }, { __DRI2_BLOB, 1, offsetof(struct dri2_egl_display, blob) }, + { __DRI_MUTABLE_RENDER_BUFFER_DRIVER, 1, offsetof(struct dri2_egl_display, mutable_render_buffer) }, { NULL, 0, 0 } }; @@ -1459,6 +1463,8 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, { struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx); + _EGLDisplay *old_disp = NULL; + struct dri2_egl_display *old_dri2_dpy = NULL; _EGLContext *old_ctx; _EGLSurface *old_dsurf, *old_rsurf; _EGLSurface *tmp_dsurf, *tmp_rsurf; @@ -1475,6 +1481,11 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, return EGL_FALSE; } + if (old_ctx) { + old_disp = old_ctx->Resource.Display; + old_dri2_dpy = dri2_egl_display(old_disp); + } + /* flush before context switch */ if (old_ctx) dri2_gl_flush(); @@ -1488,6 +1499,13 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, if (old_dsurf) dri2_surf_update_fence_fd(old_ctx, disp, old_dsurf); + + /* Disable shared buffer mode */ + if (old_dsurf && _eglSurfaceInSharedBufferMode(old_dsurf) && + old_dri2_dpy->vtbl->set_shared_buffer_mode) { + old_dri2_dpy->vtbl->set_shared_buffer_mode(old_disp, old_dsurf, false); + } + dri2_dpy->core->unbindContext(old_cctx); } @@ -1500,6 +1518,11 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, tmp_dsurf == dsurf && tmp_rsurf == rsurf); + if (old_dsurf && _eglSurfaceInSharedBufferMode(old_dsurf) && + old_dri2_dpy->vtbl->set_shared_buffer_mode) { + old_dri2_dpy->vtbl->set_shared_buffer_mode(old_disp, old_dsurf, true); + } + _eglPutSurface(dsurf); _eglPutSurface(rsurf); _eglPutContext(ctx); @@ -1522,11 +1545,22 @@ dri2_make_current(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *dsurf, dri2_dpy->ref_count++; if (old_ctx) { - EGLDisplay old_disp = _eglGetDisplayHandle(old_ctx->Resource.Display); dri2_destroy_context(drv, disp, old_ctx); dri2_display_release(old_disp); } + if (dsurf && _eglSurfaceHasMutableRenderBuffer(dsurf) && + dri2_dpy->vtbl->set_shared_buffer_mode) { + /* Always update the shared buffer mode. This is obviously needed when + * the active EGL_RENDER_BUFFER is EGL_SINGLE_BUFFER. When + * EGL_RENDER_BUFFER is EGL_BACK_BUFFER, the update protects us in the + * case where external non-EGL API may have changed window's shared + * buffer mode since we last saw it. + */ + bool mode = (dsurf->ActiveRenderBuffer == EGL_SINGLE_BUFFER); + dri2_dpy->vtbl->set_shared_buffer_mode(disp, dsurf, mode); + } + return EGL_TRUE; } diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h index 95572565823..d1e4e8dcf22 100644 --- a/src/egl/drivers/dri2/egl_dri2.h +++ b/src/egl/drivers/dri2/egl_dri2.h @@ -151,6 +151,12 @@ struct dri2_egl_display_vtbl { __DRIdrawable *(*get_dri_drawable)(_EGLSurface *surf); void (*close_screen_notify)(_EGLDisplay *dpy); + + /* Used in EGL_KHR_mutable_render_buffer to update the native window's + * shared buffer mode. + */ + bool (*set_shared_buffer_mode)(_EGLDisplay *dpy, _EGLSurface *surf, + bool mode); }; struct dri2_egl_display @@ -178,6 +184,7 @@ struct dri2_egl_display const __DRI2blobExtension *blob; const __DRI2rendererQueryExtension *rendererQuery; const __DRI2interopExtension *interop; + const __DRImutableRenderBufferDriverExtension *mutable_render_buffer; int fd; /* dri2_initialize/dri2_terminate increment/decrement this count, so does diff --git a/src/egl/drivers/dri2/platform_android.c b/src/egl/drivers/dri2/platform_android.c index ac4fc56bd7b..a683d05d8d8 100644 --- a/src/egl/drivers/dri2/platform_android.c +++ b/src/egl/drivers/dri2/platform_android.c @@ -303,6 +303,32 @@ droid_window_cancel_buffer(struct dri2_egl_surface *dri2_surf) } } +static bool +droid_set_shared_buffer_mode(_EGLDisplay *disp, _EGLSurface *surf, bool mode) +{ +#if ANDROID_API_LEVEL >= 24 + struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp); + struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf); + struct ANativeWindow *window = dri2_surf->window; + + assert(surf->Type == EGL_WINDOW_BIT); + assert(_eglSurfaceHasMutableRenderBuffer(&dri2_surf->base)); + + _eglLog(_EGL_DEBUG, "%s: mode=%d", __func__, mode); + + if (native_window_set_shared_buffer_mode(window, mode)) { + _eglLog(_EGL_WARNING, "failed native_window_set_shared_buffer_mode" + "(window=%p, mode=%d)", window, mode); + return false; + } + + return true; +#else + _eglLog(_EGL_FATAL, "%s:%d: internal error: unreachable", __FILE__, __LINE__); + return false; +#endif +} + static _EGLSurface * droid_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type, _EGLConfig *conf, void *native_window, @@ -593,6 +619,21 @@ droid_image_get_buffers(__DRIdrawable *driDrawable, if (update_buffers(dri2_surf) < 0) return 0; + if (_eglSurfaceInSharedBufferMode(&dri2_surf->base)) { + if (get_back_bo(dri2_surf) < 0) + return 0; + + /* We have dri_image_back because this is a window surface and + * get_back_bo() succeeded. + */ + assert(dri2_surf->dri_image_back); + images->back = dri2_surf->dri_image_back; + images->image_mask |= __DRI_IMAGE_BUFFER_SHARED; + + /* There exists no accompanying back nor front buffer. */ + return 1; + } + if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) { if (get_front_bo(dri2_surf, format) < 0) return 0; @@ -639,6 +680,21 @@ droid_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) if (dri2_surf->base.Type != EGL_WINDOW_BIT) return EGL_TRUE; + const bool has_mutable_rb = _eglSurfaceHasMutableRenderBuffer(draw); + + /* From the EGL_KHR_mutable_render_buffer spec (v12): + * + * If surface is a single-buffered window, pixmap, or pbuffer surface + * for which there is no pending change to the EGL_RENDER_BUFFER + * attribute, eglSwapBuffers has no effect. + */ + if (has_mutable_rb && + draw->RequestedRenderBuffer == EGL_SINGLE_BUFFER && + draw->ActiveRenderBuffer == EGL_SINGLE_BUFFER) { + _eglLog(_EGL_DEBUG, "%s: remain in shared buffer mode", __func__); + return EGL_TRUE; + } + for (int i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) { if (dri2_surf->color_buffers[i].age > 0) dri2_surf->color_buffers[i].age++; @@ -663,6 +719,18 @@ droid_swap_buffers(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *draw) dri2_dpy->flush->invalidate(dri2_surf->dri_drawable); + /* Update the shared buffer mode */ + if (has_mutable_rb && + draw->ActiveRenderBuffer != draw->RequestedRenderBuffer) { + bool mode = (draw->RequestedRenderBuffer == EGL_SINGLE_BUFFER); + _eglLog(_EGL_DEBUG, "%s: change to shared buffer mode %d", + __func__, mode); + + if (!droid_set_shared_buffer_mode(disp, draw, mode)) + return EGL_FALSE; + draw->ActiveRenderBuffer = draw->RequestedRenderBuffer; + } + return EGL_TRUE; } @@ -1176,6 +1244,7 @@ static const struct dri2_egl_display_vtbl droid_display_vtbl = { .create_wayland_buffer_from_image = dri2_fallback_create_wayland_buffer_from_image, .get_sync_values = dri2_fallback_get_sync_values, .get_dri_drawable = dri2_surface_get_dri_drawable, + .set_shared_buffer_mode = droid_set_shared_buffer_mode, }; #ifdef HAVE_DRM_GRALLOC @@ -1202,14 +1271,94 @@ static const __DRIextension *droid_dri2_loader_extensions[] = { &droid_dri2_loader_extension.base, &image_lookup_extension.base, &use_invalidate.base, + /* No __DRI_MUTABLE_RENDER_BUFFER_LOADER because it requires + * __DRI_IMAGE_LOADER. + */ NULL, }; #endif /* HAVE_DRM_GRALLOC */ +static void +droid_display_shared_buffer(__DRIdrawable *driDrawable, int fence_fd, + void *loaderPrivate) +{ + struct dri2_egl_surface *dri2_surf = loaderPrivate; + struct ANativeWindowBuffer *old_buffer UNUSED = dri2_surf->buffer; + + if (!_eglSurfaceInSharedBufferMode(&dri2_surf->base)) { + _eglLog(_EGL_WARNING, "%s: internal error: buffer is not shared", + __func__); + return; + } + + if (fence_fd >= 0) { + /* The driver's fence is more recent than the surface's out fence, if it + * exists at all. So use the driver's fence. + */ + if (dri2_surf->out_fence_fd >= 0) { + close(dri2_surf->out_fence_fd); + dri2_surf->out_fence_fd = -1; + } + } else if (dri2_surf->out_fence_fd >= 0) { + fence_fd = dri2_surf->out_fence_fd; + dri2_surf->out_fence_fd = -1; + } + + if (dri2_surf->window->queueBuffer(dri2_surf->window, dri2_surf->buffer, + fence_fd)) { + _eglLog(_EGL_WARNING, "%s: ANativeWindow::queueBuffer failed", __func__); + close(fence_fd); + return; + } + + fence_fd = -1; + + if (dri2_surf->window->dequeueBuffer(dri2_surf->window, &dri2_surf->buffer, + &fence_fd)) { + /* Tear down the surface because it no longer has a back buffer. */ + struct dri2_egl_display *dri2_dpy = + dri2_egl_display(dri2_surf->base.Resource.Display); + + _eglLog(_EGL_WARNING, "%s: ANativeWindow::dequeueBuffer failed", __func__); + + dri2_surf->base.Lost = true; + dri2_surf->buffer = NULL; + dri2_surf->back = NULL; + + if (dri2_surf->dri_image_back) { + dri2_dpy->image->destroyImage(dri2_surf->dri_image_back); + dri2_surf->dri_image_back = NULL; + } + + dri2_dpy->flush->invalidate(dri2_surf->dri_drawable); + return; + } + + if (fence_fd < 0) + return; + + /* Access to the buffer is controlled by a sync fence. Block on it. + * + * Ideally, we would submit the fence to the driver, and the driver would + * postpone command execution until it signalled. But DRI lacks API for + * that (as of 2018-04-11). + * + * SYNC_IOC_WAIT waits forever if timeout < 0 + */ + sync_wait(fence_fd, -1); + close(fence_fd); +} + +static const __DRImutableRenderBufferLoaderExtension droid_mutable_render_buffer_extension = { + .base = { __DRI_MUTABLE_RENDER_BUFFER_LOADER, 1 }, + .displaySharedBuffer = droid_display_shared_buffer, +}; + static const __DRIextension *droid_image_loader_extensions[] = { &droid_image_loader_extension.base, &image_lookup_extension.base, &use_invalidate.base, + &droid_mutable_render_buffer_extension.base, NULL, }; @@ -1436,11 +1585,6 @@ dri2_initialize_android(_EGLDriver *drv, _EGLDisplay *disp) */ dri2_setup_swap_interval(disp, 1); - if (!droid_add_configs_for_visuals(drv, disp)) { - err = "DRI2: failed to add configs"; - goto cleanup; - } - disp->Extensions.ANDROID_framebuffer_target = EGL_TRUE; disp->Extensions.ANDROID_image_native_buffer = EGL_TRUE; disp->Extensions.ANDROID_recordable = EGL_TRUE; @@ -1449,6 +1593,20 @@ dri2_initialize_android(_EGLDriver *drv, _EGLDisplay *disp) disp->Extensions.KHR_partial_update = EGL_TRUE; #endif disp->Extensions.KHR_image = EGL_TRUE; +#if ANDROID_API_LEVEL >= 24 + if (dri2_dpy->mutable_render_buffer && + dri2_dpy->loader_extensions == droid_image_loader_extensions) { + disp->Extensions.KHR_mutable_render_buffer = EGL_TRUE; + } +#endif + + /* Create configs *after* enabling extensions because presence of DRI + * driver extensions can affect the capabilities of EGLConfigs. + */ + if (!droid_add_configs_for_visuals(drv, disp)) { + err = "DRI2: failed to add configs"; + goto cleanup; + } /* Fill vtbl last to prevent accidentally calling virtual function during * initialization. -- 2.30.2