static int
get_fourcc_yuv(int native, int is_ycrcb, int chroma_step)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(droid_yuv_formats); ++i)
+ for (int i = 0; i < ARRAY_SIZE(droid_yuv_formats); ++i)
if (droid_yuv_formats[i].native == native &&
droid_yuv_formats[i].is_ycrcb == is_ycrcb &&
droid_yuv_formats[i].chroma_step == chroma_step)
static bool
is_yuv(int native)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(droid_yuv_formats); ++i)
+ for (int i = 0; i < ARRAY_SIZE(droid_yuv_formats); ++i)
if (droid_yuv_formats[i].native == native)
return true;
}
static void
-droid_window_cancel_buffer(_EGLDisplay *disp, struct dri2_egl_surface *dri2_surf)
+droid_window_cancel_buffer(struct dri2_egl_surface *dri2_surf)
{
- /* no cancel buffer? */
- droid_window_enqueue_buffer(disp, dri2_surf);
+ int ret;
+
+ ret = dri2_surf->window->cancelBuffer(dri2_surf->window, dri2_surf->buffer, -1);
+ if (ret < 0) {
+ _eglLog(_EGL_WARNING, "ANativeWindow::cancelBuffer failed");
+ dri2_surf->base.Lost = EGL_TRUE;
+ }
}
static __DRIbuffer *
{
struct dri2_egl_display *dri2_dpy =
dri2_egl_display(dri2_surf->base.Resource.Display);
- int i;
- for (i = 0; i < ARRAY_SIZE(dri2_surf->local_buffers); i++) {
+ for (int i = 0; i < ARRAY_SIZE(dri2_surf->local_buffers); i++) {
if (dri2_surf->local_buffers[i]) {
dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
dri2_surf->local_buffers[i]);
_EGLConfig *conf, void *native_window,
const EGLint *attrib_list)
{
+ __DRIcreateNewDrawableFunc createNewDrawable;
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
struct dri2_egl_surface *dri2_surf;
if (!config)
goto cleanup_surface;
- dri2_surf->dri_drawable =
- dri2_dpy->dri2->createNewDrawable(dri2_dpy->dri_screen, config,
- dri2_surf);
+ if (dri2_dpy->image_driver)
+ createNewDrawable = dri2_dpy->image_driver->createNewDrawable;
+ else
+ createNewDrawable = dri2_dpy->dri2->createNewDrawable;
+
+ dri2_surf->dri_drawable = (*createNewDrawable)(dri2_dpy->dri_screen, config,
+ dri2_surf);
if (dri2_surf->dri_drawable == NULL) {
- _eglError(EGL_BAD_ALLOC, "dri2->createNewDrawable");
+ _eglError(EGL_BAD_ALLOC, "createNewDrawable");
goto cleanup_surface;
}
if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
if (dri2_surf->buffer)
- droid_window_cancel_buffer(disp, dri2_surf);
+ droid_window_cancel_buffer(dri2_surf);
dri2_surf->window->common.decRef(&dri2_surf->window->common);
}
static int
update_buffers(struct dri2_egl_surface *dri2_surf)
{
+ if (dri2_surf->base.Lost)
+ return -1;
+
if (dri2_surf->base.Type != EGL_WINDOW_BIT)
return 0;
/* try to dequeue the next back buffer */
if (!dri2_surf->buffer && !droid_window_dequeue_buffer(dri2_surf)) {
_eglLog(_EGL_WARNING, "Could not dequeue buffer from native window");
+ dri2_surf->base.Lost = EGL_TRUE;
return -1;
}
}
static int
-get_back_bo(struct dri2_egl_surface *dri2_surf, unsigned int format)
+get_back_bo(struct dri2_egl_surface *dri2_surf)
{
struct dri2_egl_display *dri2_dpy =
dri2_egl_display(dri2_surf->base.Resource.Display);
}
if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
- if (get_back_bo(dri2_surf, format) < 0)
+ if (get_back_bo(dri2_surf) < 0)
return 0;
if (dri2_surf->dri_image_back) {
if (update_buffers(dri2_surf) < 0) {
_eglError(EGL_BAD_ALLOC, "droid_query_buffer_age");
- return 0;
+ return -1;
}
- return dri2_surf->back->age;
+ return dri2_surf->back ? dri2_surf->back->age : 0;
}
static EGLBoolean
if (dri2_surf->color_buffers[i].age > 0)
dri2_surf->color_buffers[i].age++;
}
- dri2_surf->back->age = 1;
+
+ /* "XXX: we don't use get_back_bo() since it causes regressions in
+ * several dEQP tests.
+ */
+ if (dri2_surf->back)
+ dri2_surf->back->age = 1;
dri2_flush_drawable_for_swapbuffers(disp, draw);
+ /* dri2_surf->buffer can be null even when no error has occured. For
+ * example, if the user has called no GL rendering commands since the
+ * previous eglSwapBuffers, then the driver may have not triggered
+ * a callback to ANativeWindow::dequeueBuffer, in which case
+ * dri2_surf->buffer remains null.
+ */
if (dri2_surf->buffer)
droid_window_enqueue_buffer(disp, dri2_surf);
return EGL_TRUE;
}
+#if ANDROID_API_LEVEL >= 23
+static EGLBoolean
+droid_set_damage_region(_EGLDriver *drv,
+ _EGLDisplay *disp,
+ _EGLSurface *draw, const EGLint* rects, EGLint n_rects)
+{
+ struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
+ android_native_rect_t* droid_rects = NULL;
+ int ret;
+
+ if (n_rects == 0)
+ return EGL_TRUE;
+
+ droid_rects = malloc(n_rects * sizeof(android_native_rect_t));
+ if (droid_rects == NULL) {
+ _eglError(EGL_BAD_ALLOC, "eglSetDamageRegionKHR");
+ return EGL_FALSE;
+ }
+
+ for (EGLint num_drects = 0; num_drects < n_rects; num_drects++) {
+ EGLint i = num_drects * 4;
+ droid_rects[num_drects].left = rects[i];
+ droid_rects[num_drects].bottom = rects[i + 1];
+ droid_rects[num_drects].right = rects[i] + rects[i + 2];
+ droid_rects[num_drects].top = rects[i + 1] + rects[i + 3];
+ }
+
+ /*
+ * XXX/TODO: Need to check for other return values
+ */
+
+ ret = native_window_set_surface_damage(dri2_surf->window, droid_rects, n_rects);
+ free(droid_rects);
+
+ return ret == 0 ? EGL_TRUE : EGL_FALSE;
+}
+#endif
+
static _EGLImage *
droid_create_image_from_prime_fd_yuv(_EGLDisplay *disp, _EGLContext *ctx,
struct ANativeWindowBuffer *buf, int fd)
}
if (ycbcr.chroma_step == 2) {
- /* Semi-planar Y + CbCr or Y + CbCr format. */
+ /* Semi-planar Y + CbCr or Y + CrCb format. */
const EGLint attr_list_2plane[] = {
EGL_WIDTH, buf->width,
EGL_HEIGHT, buf->height,
droid_get_buffers_parse_attachments(struct dri2_egl_surface *dri2_surf,
unsigned int *attachments, int count)
{
- int num_buffers = 0, i;
+ int num_buffers = 0;
/* fill dri2_surf->buffers */
- for (i = 0; i < count * 2; i += 2) {
+ for (int i = 0; i < count * 2; i += 2) {
__DRIbuffer *buf, *local;
assert(num_buffers < ARRAY_SIZE(dri2_surf->buffers));
int format;
unsigned int rgba_masks[4];
} visuals[] = {
- { HAL_PIXEL_FORMAT_RGBA_8888, { 0xff, 0xff00, 0xff0000, 0xff000000 } },
- { HAL_PIXEL_FORMAT_RGBX_8888, { 0xff, 0xff00, 0xff0000, 0x0 } },
- { HAL_PIXEL_FORMAT_RGB_565, { 0xf800, 0x7e0, 0x1f, 0x0 } },
- { HAL_PIXEL_FORMAT_BGRA_8888, { 0xff0000, 0xff00, 0xff, 0xff000000 } },
+ { HAL_PIXEL_FORMAT_RGBA_8888, { 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 } },
+ { HAL_PIXEL_FORMAT_RGBX_8888, { 0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000 } },
+ { HAL_PIXEL_FORMAT_RGB_565, { 0x0000f800, 0x000007e0, 0x0000001f, 0x00000000 } },
+ { HAL_PIXEL_FORMAT_BGRA_8888, { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 } },
};
EGLint config_attrs[] = {
EGL_NATIVE_VISUAL_ID, 0,
EGL_RECORDABLE_ANDROID, EGL_TRUE,
EGL_NONE
};
- unsigned int format_count[ARRAY_SIZE(visuals)] = { 0 };
- int count, i, j;
- count = 0;
- for (i = 0; dri2_dpy->driver_configs[i]; i++) {
- const EGLint surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
- struct dri2_egl_config *dri2_conf;
+ unsigned int format_count[ARRAY_SIZE(visuals)] = { 0 };
+ int count = 0;
+
+ /* The nesting of loops is significant here. Also significant is the order
+ * of the HAL pixel formats. Many Android apps (such as Google's official
+ * NDK GLES2 example app), and even portions the core framework code (such
+ * as SystemServiceManager in Nougat), incorrectly choose their EGLConfig.
+ * They neglect to match the EGLConfig's EGL_NATIVE_VISUAL_ID against the
+ * window's native format, and instead choose the first EGLConfig whose
+ * channel sizes match those of the native window format while ignoring the
+ * channel *ordering*.
+ *
+ * We can detect such buggy clients in logcat when they call
+ * eglCreateSurface, by detecting the mismatch between the EGLConfig's
+ * format and the window's format.
+ *
+ * As a workaround, we generate EGLConfigs such that all EGLConfigs for HAL
+ * pixel format i precede those for HAL pixel format i+1. In my
+ * (chadversary) testing on Android Nougat, this was good enough to pacify
+ * the buggy clients.
+ */
+ for (int i = 0; i < ARRAY_SIZE(visuals); i++) {
+ for (int j = 0; dri2_dpy->driver_configs[j]; j++) {
+ const EGLint surface_type = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
- for (j = 0; j < ARRAY_SIZE(visuals); j++) {
- config_attrs[1] = visuals[j].format;
- config_attrs[3] = visuals[j].format;
+ config_attrs[1] = visuals[i].format;
+ config_attrs[3] = visuals[i].format;
- dri2_conf = dri2_add_config(dpy, dri2_dpy->driver_configs[i],
- count + 1, surface_type, config_attrs, visuals[j].rgba_masks);
+ struct dri2_egl_config *dri2_conf =
+ dri2_add_config(dpy, dri2_dpy->driver_configs[j],
+ count + 1, surface_type, config_attrs,
+ visuals[i].rgba_masks);
if (dri2_conf) {
- count++;
- format_count[j]++;
+ if (dri2_conf->base.ConfigID == count + 1)
+ count++;
+ format_count[i]++;
}
}
}
- for (i = 0; i < ARRAY_SIZE(format_count); i++) {
+ for (int i = 0; i < ARRAY_SIZE(format_count); i++) {
if (!format_count[i]) {
_eglLog(_EGL_DEBUG, "No DRI config supports native format 0x%x",
visuals[i].format);
return (fd >= 0) ? fcntl(fd, F_DUPFD_CLOEXEC, 3) : -1;
}
-/* support versions < JellyBean */
-#ifndef ALOGW
-#define ALOGW LOGW
-#endif
-#ifndef ALOGD
-#define ALOGD LOGD
-#endif
-#ifndef ALOGI
-#define ALOGI LOGI
-#endif
-
-static void
-droid_log(EGLint level, const char *msg)
-{
- switch (level) {
- case _EGL_DEBUG:
- ALOGD("%s", msg);
- break;
- case _EGL_INFO:
- ALOGI("%s", msg);
- break;
- case _EGL_WARNING:
- ALOGW("%s", msg);
- break;
- case _EGL_FATAL:
- LOG_FATAL("%s", msg);
- break;
- default:
- break;
- }
-}
-
-static struct dri2_egl_display_vtbl droid_display_vtbl = {
+static const struct dri2_egl_display_vtbl droid_display_vtbl = {
.authenticate = NULL,
.create_window_surface = droid_create_window_surface,
.create_pixmap_surface = dri2_fallback_create_pixmap_surface,
.swap_buffers = droid_swap_buffers,
.swap_buffers_with_damage = dri2_fallback_swap_buffers_with_damage,
.swap_buffers_region = dri2_fallback_swap_buffers_region,
+#if ANDROID_API_LEVEL >= 23
+ .set_damage_region = droid_set_damage_region,
+#else
+ .set_damage_region = dri2_fallback_set_damage_region,
+#endif
.post_sub_buffer = dri2_fallback_post_sub_buffer,
.copy_buffers = dri2_fallback_copy_buffers,
.query_buffer_age = droid_query_buffer_age,
const char *err;
int ret;
- _eglSetLogProc(droid_log);
-
loader_set_logger(_eglLog);
dri2_dpy = calloc(1, sizeof(*dri2_dpy));
if (!dri2_dpy)
return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+ dri2_dpy->fd = -1;
ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
(const hw_module_t **)&dri2_dpy->gralloc);
if (ret) {
err = "DRI2: failed to get gralloc module";
- goto cleanup_display;
+ goto cleanup;
}
dpy->DriverData = (void *) dri2_dpy;
dri2_dpy->fd = droid_open_device(dri2_dpy);
if (dri2_dpy->fd < 0) {
err = "DRI2: failed to open device";
- goto cleanup_display;
+ goto cleanup;
}
dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
if (dri2_dpy->driver_name == NULL) {
err = "DRI2: failed to get driver name";
- goto cleanup_device;
- }
-
- if (!dri2_load_driver(dpy)) {
- err = "DRI2: failed to load driver";
- goto cleanup_driver_name;
+ goto cleanup;
}
dri2_dpy->is_render_node = drmGetNodeTypeFromFd(dri2_dpy->fd) == DRM_NODE_RENDER;
/* render nodes cannot use Gem names, and thus do not support
* the __DRI_DRI2_LOADER extension */
- if (!dri2_dpy->is_render_node)
+ if (!dri2_dpy->is_render_node) {
dri2_dpy->loader_extensions = droid_dri2_loader_extensions;
- else
+ if (!dri2_load_driver(dpy)) {
+ err = "DRI2: failed to load driver";
+ goto cleanup;
+ }
+ } else {
dri2_dpy->loader_extensions = droid_image_loader_extensions;
+ if (!dri2_load_driver_dri3(dpy)) {
+ err = "DRI3: failed to load driver";
+ goto cleanup;
+ }
+ }
if (!dri2_create_screen(dpy)) {
err = "DRI2: failed to create screen";
- goto cleanup_driver;
+ goto cleanup;
}
+ if (!dri2_setup_extensions(dpy))
+ goto cleanup;
+
+ dri2_setup_screen(dpy);
+
if (!droid_add_configs_for_visuals(drv, dpy)) {
err = "DRI2: failed to add configs";
- goto cleanup_screen;
+ goto cleanup;
}
dpy->Extensions.ANDROID_framebuffer_target = EGL_TRUE;
dpy->Extensions.ANDROID_image_native_buffer = EGL_TRUE;
dpy->Extensions.ANDROID_recordable = EGL_TRUE;
dpy->Extensions.EXT_buffer_age = EGL_TRUE;
+#if ANDROID_API_LEVEL >= 23
+ dpy->Extensions.KHR_partial_update = EGL_TRUE;
+#endif
/* Fill vtbl last to prevent accidentally calling virtual function during
* initialization.
return EGL_TRUE;
-cleanup_screen:
- dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
-cleanup_driver:
- dlclose(dri2_dpy->driver);
-cleanup_driver_name:
- free(dri2_dpy->driver_name);
-cleanup_device:
- close(dri2_dpy->fd);
-cleanup_display:
- free(dri2_dpy);
- dpy->DriverData = NULL;
-
+cleanup:
+ dri2_display_destroy(dpy);
return _eglError(EGL_NOT_INITIALIZED, err);
}