_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)
{ __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 }
};
{
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;
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();
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);
}
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);
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;
}
}
}
+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,
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;
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++;
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;
}
.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
&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,
};
*/
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;
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.