egl/android: Implement EGL_KHR_mutable_render_buffer
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 <tapani.palli@intel.com>
This commit is contained in:
parent
317c460a4d
commit
ed7c694688
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue