/* * Copyright © 2010 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Kristian Høgsberg */ #define LOG_TAG "MESA-EGL" #include #include "droid.h" static const __DRIuseInvalidateExtension use_invalidate = { { __DRI_USE_INVALIDATE, 1 } }; static __DRIimage * droid_lookup_egl_image(__DRIscreen *screen, void *image, void *data) { _EGLDisplay *disp = data; struct droid_egl_image *dimg; _EGLImage *img; (void) screen; img = _eglLookupImage(image, disp); if (img == NULL) { _eglError(EGL_BAD_PARAMETER, "droid_lookup_egl_image"); return NULL; } dimg = droid_egl_image(image); return dimg->dri_image; } static const __DRIimageLookupExtension image_lookup_extension = { { __DRI_IMAGE_LOOKUP, 1 }, droid_lookup_egl_image }; static int get_format_bpp(int native) { int bpp; /* see libpixelflinger/format.cpp */ switch (native) { case GGL_PIXEL_FORMAT_RGBA_8888: case GGL_PIXEL_FORMAT_RGBX_8888: case GGL_PIXEL_FORMAT_BGRA_8888: bpp = 4; break; case GGL_PIXEL_FORMAT_RGB_888: bpp = 3; break; case GGL_PIXEL_FORMAT_RGB_565: case GGL_PIXEL_FORMAT_RGBA_5551: case GGL_PIXEL_FORMAT_RGBA_4444: case GGL_PIXEL_FORMAT_LA_88: bpp = 2; break; case GGL_PIXEL_FORMAT_RGB_332: case GGL_PIXEL_FORMAT_A_8: case GGL_PIXEL_FORMAT_L_8: bpp = 1; break; default: bpp = 0; break; } return bpp; } #include int get_native_buffer_name(struct android_native_buffer_t *buf) { struct drm_bo_t *bo; bo = drm_gem_get(buf->handle); return (bo) ? bo->name : 0; } EGLBoolean droid_dequeue_buffer(struct droid_egl_surface *dsurf) { __DRIbuffer *buf = &dsurf->dri_buffer; if (dsurf->window->dequeueBuffer(dsurf->window, &dsurf->buffer)) return EGL_FALSE; dsurf->buffer->common.incRef(&dsurf->buffer->common); dsurf->window->lockBuffer(dsurf->window, dsurf->buffer); buf->attachment = __DRI_BUFFER_FAKE_FRONT_LEFT; buf->name = get_native_buffer_name(dsurf->buffer); buf->cpp = get_format_bpp(dsurf->buffer->format); buf->pitch = dsurf->buffer->stride * buf->cpp; buf->flags = 0; return EGL_TRUE; } EGLBoolean droid_enqueue_buffer(struct droid_egl_surface *dsurf) { dsurf->window->queueBuffer(dsurf->window, dsurf->buffer); dsurf->buffer->common.decRef(&dsurf->buffer->common); dsurf->buffer = NULL; return EGL_TRUE; } static void droid_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate) { } static __DRIbuffer * droid_get_buffers_with_format(__DRIdrawable * driDrawable, int *width, int *height, unsigned int *attachments, int count, int *out_count, void *loaderPrivate) { struct droid_egl_surface *dsurf = loaderPrivate; struct droid_egl_display *ddpy = droid_egl_display(dsurf->base.Resource.Display); if (!dsurf->buffer) { if (!droid_dequeue_buffer(dsurf)) return NULL; } if (width) *width = dsurf->buffer->width; if (height) *height = dsurf->buffer->height; *out_count = 1; return &dsurf->dri_buffer; } static const EGLint droid_to_egl_attribute_map[] = { 0, EGL_BUFFER_SIZE, /* __DRI_ATTRIB_BUFFER_SIZE */ EGL_LEVEL, /* __DRI_ATTRIB_LEVEL */ EGL_RED_SIZE, /* __DRI_ATTRIB_RED_SIZE */ EGL_GREEN_SIZE, /* __DRI_ATTRIB_GREEN_SIZE */ EGL_BLUE_SIZE, /* __DRI_ATTRIB_BLUE_SIZE */ EGL_LUMINANCE_SIZE, /* __DRI_ATTRIB_LUMINANCE_SIZE */ EGL_ALPHA_SIZE, /* __DRI_ATTRIB_ALPHA_SIZE */ 0, /* __DRI_ATTRIB_ALPHA_MASK_SIZE */ EGL_DEPTH_SIZE, /* __DRI_ATTRIB_DEPTH_SIZE */ EGL_STENCIL_SIZE, /* __DRI_ATTRIB_STENCIL_SIZE */ 0, /* __DRI_ATTRIB_ACCUM_RED_SIZE */ 0, /* __DRI_ATTRIB_ACCUM_GREEN_SIZE */ 0, /* __DRI_ATTRIB_ACCUM_BLUE_SIZE */ 0, /* __DRI_ATTRIB_ACCUM_ALPHA_SIZE */ EGL_SAMPLE_BUFFERS, /* __DRI_ATTRIB_SAMPLE_BUFFERS */ EGL_SAMPLES, /* __DRI_ATTRIB_SAMPLES */ 0, /* __DRI_ATTRIB_RENDER_TYPE, */ 0, /* __DRI_ATTRIB_CONFIG_CAVEAT */ 0, /* __DRI_ATTRIB_CONFORMANT */ 0, /* __DRI_ATTRIB_DOUBLE_BUFFER */ 0, /* __DRI_ATTRIB_STEREO */ 0, /* __DRI_ATTRIB_AUX_BUFFERS */ 0, /* __DRI_ATTRIB_TRANSPARENT_TYPE */ 0, /* __DRI_ATTRIB_TRANSPARENT_INDEX_VALUE */ 0, /* __DRI_ATTRIB_TRANSPARENT_RED_VALUE */ 0, /* __DRI_ATTRIB_TRANSPARENT_GREEN_VALUE */ 0, /* __DRI_ATTRIB_TRANSPARENT_BLUE_VALUE */ 0, /* __DRI_ATTRIB_TRANSPARENT_ALPHA_VALUE */ 0, /* __DRI_ATTRIB_FLOAT_MODE */ 0, /* __DRI_ATTRIB_RED_MASK */ 0, /* __DRI_ATTRIB_GREEN_MASK */ 0, /* __DRI_ATTRIB_BLUE_MASK */ 0, /* __DRI_ATTRIB_ALPHA_MASK */ EGL_MAX_PBUFFER_WIDTH, /* __DRI_ATTRIB_MAX_PBUFFER_WIDTH */ EGL_MAX_PBUFFER_HEIGHT, /* __DRI_ATTRIB_MAX_PBUFFER_HEIGHT */ EGL_MAX_PBUFFER_PIXELS, /* __DRI_ATTRIB_MAX_PBUFFER_PIXELS */ 0, /* __DRI_ATTRIB_OPTIMAL_PBUFFER_WIDTH */ 0, /* __DRI_ATTRIB_OPTIMAL_PBUFFER_HEIGHT */ 0, /* __DRI_ATTRIB_VISUAL_SELECT_GROUP */ 0, /* __DRI_ATTRIB_SWAP_METHOD */ EGL_MAX_SWAP_INTERVAL, /* __DRI_ATTRIB_MAX_SWAP_INTERVAL */ EGL_MIN_SWAP_INTERVAL, /* __DRI_ATTRIB_MIN_SWAP_INTERVAL */ 0, /* __DRI_ATTRIB_BIND_TO_TEXTURE_RGB */ 0, /* __DRI_ATTRIB_BIND_TO_TEXTURE_RGBA */ 0, /* __DRI_ATTRIB_BIND_TO_MIPMAP_TEXTURE */ 0, /* __DRI_ATTRIB_BIND_TO_TEXTURE_TARGETS */ EGL_Y_INVERTED_NOK, /* __DRI_ATTRIB_YINVERTED */ 0, /* __DRI_ATTRIB_FRAMEBUFFER_SRGB_CAPABLE */ }; static struct droid_egl_config * droid_add_config(_EGLDisplay *dpy, const __DRIconfig *dri_config, int id, int depth, EGLint surface_type, int rgba_masks[4]) { struct droid_egl_config *conf; struct droid_egl_display *ddpy; _EGLConfig base; unsigned int attrib, value, double_buffer; EGLint key; int dri_masks[4] = { 0, 0, 0, 0 }; int i; ddpy = dpy->DriverData; _eglInitConfig(&base, dpy, id); i = 0; double_buffer = 0; while (ddpy->core->indexConfigAttrib(dri_config, i++, &attrib, &value)) { switch (attrib) { case __DRI_ATTRIB_RENDER_TYPE: if (value & __DRI_ATTRIB_RGBA_BIT) value = EGL_RGB_BUFFER; else if (value & __DRI_ATTRIB_LUMINANCE_BIT) value = EGL_LUMINANCE_BUFFER; else assert(0); _eglSetConfigKey(&base, EGL_COLOR_BUFFER_TYPE, value); break; case __DRI_ATTRIB_CONFIG_CAVEAT: if (value & __DRI_ATTRIB_NON_CONFORMANT_CONFIG) value = EGL_NON_CONFORMANT_CONFIG; else if (value & __DRI_ATTRIB_SLOW_BIT) value = EGL_SLOW_CONFIG; else value = EGL_NONE; _eglSetConfigKey(&base, EGL_CONFIG_CAVEAT, value); break; case __DRI_ATTRIB_DOUBLE_BUFFER: double_buffer = value; break; case __DRI_ATTRIB_RED_MASK: dri_masks[0] = value; break; case __DRI_ATTRIB_GREEN_MASK: dri_masks[1] = value; break; case __DRI_ATTRIB_BLUE_MASK: dri_masks[2] = value; break; case __DRI_ATTRIB_ALPHA_MASK: dri_masks[3] = value; break; default: key = droid_to_egl_attribute_map[attrib]; if (key != 0) _eglSetConfigKey(&base, key, value); break; } } /* In EGL, double buffer or not isn't a config attribute. Pixmaps * surfaces are always single buffered, pbuffer surfaces are always * back buffers and windows can be either, selected by passing an * attribute at window surface construction time. To support this * we ignore all double buffer configs and manipulate the buffer we * return in the getBuffer callback to get the behaviour we want. */ if (double_buffer) return NULL; if (depth > 0 && depth != _eglGetConfigKey(&base, EGL_BUFFER_SIZE)) return NULL; if (memcmp(dri_masks, rgba_masks, sizeof(rgba_masks))) return NULL; _eglSetConfigKey(&base, EGL_NATIVE_RENDERABLE, EGL_TRUE); _eglSetConfigKey(&base, EGL_SURFACE_TYPE, surface_type); _eglSetConfigKey(&base, EGL_RENDERABLE_TYPE, dpy->ClientAPIs); _eglSetConfigKey(&base, EGL_CONFORMANT, dpy->ClientAPIs); if (!_eglValidateConfig(&base, EGL_FALSE)) { _eglLog(_EGL_DEBUG, "DRI2: failed to validate config %d", id); return NULL; } conf = calloc(1, sizeof(*conf)); if (conf != NULL) { memcpy(&conf->base, &base, sizeof(base)); conf->dri_config = dri_config; _eglLinkConfig(&conf->base); } return conf; } static EGLBoolean droid_add_configs_for_visuals(_EGLDriver *drv, _EGLDisplay *dpy) { struct droid_egl_display *ddpy = droid_egl_display(dpy); const struct { int format; int size; int rgba_masks[4]; } visuals[] = { { GGL_PIXEL_FORMAT_RGBA_8888, 32, { 0xff, 0xff00, 0xff0000, 0xff000000 } }, { GGL_PIXEL_FORMAT_RGBX_8888, 32, { 0xff, 0xff00, 0xff0000, 0x0 } }, { GGL_PIXEL_FORMAT_RGB_888, 24, { 0xff, 0xff00, 0xff0000, 0x0 } }, { GGL_PIXEL_FORMAT_RGB_565, 16, { 0xf800, 0x7e0, 0x1f, 0x0 } }, { GGL_PIXEL_FORMAT_BGRA_8888, 32, { 0xff0000, 0xff00, 0xff, 0xff000000 } }, { GGL_PIXEL_FORMAT_A_8, 8, { 0xf800, 0x7e0, 0x1f, 0x0 } }, { 0, 0, { 0, 0, 0, 0 } } }; int count, i, j; count = 0; for (i = 0; visuals[i].format; i++) { int format_count = 0; for (j = 0; ddpy->dri_configs[j]; j++) { struct droid_egl_config *dconf; dconf = droid_add_config(dpy, ddpy->dri_configs[j], count + 1, visuals[i].size, EGL_WINDOW_BIT, visuals[i].rgba_masks); if (dconf) { _eglSetConfigKey(&dconf->base, EGL_NATIVE_VISUAL_TYPE, visuals[i].format); count++; format_count++; } } if (!format_count) { _eglLog(_EGL_DEBUG, "No DRI config supports native format 0x%x", visuals[i].format); } } return (count != 0); } struct droid_extension_match { const char *name; int version; int offset; }; static struct droid_extension_match droid_driver_extensions[] = { { __DRI_CORE, 1, offsetof(struct droid_egl_display, core) }, { __DRI_DRI2, 1, offsetof(struct droid_egl_display, dri2) }, { NULL, 0, 0 } }; static struct droid_extension_match droid_core_extensions[] = { { __DRI2_FLUSH, 1, offsetof(struct droid_egl_display, flush) }, { __DRI_IMAGE, 1, offsetof(struct droid_egl_display, image) }, { NULL, 0, 0 } }; extern const __DRIextension *__driDriverExtensions[]; static EGLBoolean droid_bind_extensions(struct droid_egl_display *ddpy, struct droid_extension_match *matches, const __DRIextension **extensions) { int i, j, ret = EGL_TRUE; void *field; for (i = 0; extensions[i]; i++) { _eglLog(_EGL_DEBUG, "DRI2: found extension `%s'", extensions[i]->name); for (j = 0; matches[j].name; j++) { if (strcmp(extensions[i]->name, matches[j].name) == 0 && extensions[i]->version >= matches[j].version) { field = ((char *) ddpy + matches[j].offset); *(const __DRIextension **) field = extensions[i]; _eglLog(_EGL_INFO, "DRI2: found extension %s version %d", extensions[i]->name, extensions[i]->version); } } } for (j = 0; matches[j].name; j++) { field = ((char *) ddpy + matches[j].offset); if (*(const __DRIextension **) field == NULL) { _eglLog(_EGL_FATAL, "DRI2: did not find extension %s version %d", matches[j].name, matches[j].version); ret = EGL_FALSE; } } return ret; } static EGLBoolean droid_create_screen(_EGLDisplay *dpy) { struct droid_egl_display *ddpy = droid_egl_display(dpy); const __DRIextension **extensions; unsigned int api_mask; ddpy->dri_screen = ddpy->dri2->createNewScreen(0, ddpy->fd, ddpy->extensions, &ddpy->dri_configs, dpy); if (ddpy->dri_screen == NULL) { _eglLog(_EGL_WARNING, "failed to create dri screen"); return EGL_FALSE; } extensions = ddpy->core->getExtensions(ddpy->dri_screen); if (!droid_bind_extensions(ddpy, droid_core_extensions, extensions)) { ddpy->core->destroyScreen(ddpy->dri_screen); return EGL_FALSE; } if (ddpy->dri2->base.version >= 2) api_mask = ddpy->dri2->getAPIMask(ddpy->dri_screen); else api_mask = 1 << __DRI_API_OPENGL; dpy->ClientAPIs = 0; if (api_mask & (1 <<__DRI_API_OPENGL)) dpy->ClientAPIs |= EGL_OPENGL_BIT; if (api_mask & (1 <<__DRI_API_GLES)) dpy->ClientAPIs |= EGL_OPENGL_ES_BIT; if (api_mask & (1 << __DRI_API_GLES2)) dpy->ClientAPIs |= EGL_OPENGL_ES2_BIT; if (ddpy->dri2->base.version >= 2) { dpy->Extensions.KHR_surfaceless_gles1 = EGL_TRUE; dpy->Extensions.KHR_surfaceless_gles2 = EGL_TRUE; dpy->Extensions.KHR_surfaceless_opengl = EGL_TRUE; } return EGL_TRUE; } static EGLBoolean droid_load_driver(_EGLDisplay *disp) { struct droid_egl_display *ddpy = disp->DriverData; const __DRIextension **extensions; extensions = __driDriverExtensions; if (!droid_bind_extensions(ddpy, droid_driver_extensions, extensions)) return EGL_FALSE; return EGL_TRUE; } static EGLBoolean droid_initialize_android(_EGLDriver *drv, _EGLDisplay *dpy) { struct droid_egl_display *ddpy; int fd = -1, err, i; const hw_module_t *mod; err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &mod); if (!err) { const gralloc_module_t *gr = (gralloc_module_t *) mod; err = -EINVAL; if (gr->perform) err = gr->perform(gr, GRALLOC_MODULE_PERFORM_GET_DRM_FD, &fd); } if (err || fd < 0) { _eglLog(_EGL_WARNING, "fail to get drm fd"); return EGL_FALSE; } ddpy = calloc(1, sizeof(*ddpy)); if (!ddpy) return _eglError(EGL_BAD_ALLOC, "eglInitialize"); dpy->DriverData = (void *) ddpy; ddpy->fd = fd; if (!droid_load_driver(dpy)) return EGL_FALSE; ddpy->loader_extension.base.name = __DRI_DRI2_LOADER; ddpy->loader_extension.base.version = 3; ddpy->loader_extension.getBuffers = NULL; ddpy->loader_extension.flushFrontBuffer = droid_flush_front_buffer; ddpy->loader_extension.getBuffersWithFormat = droid_get_buffers_with_format; ddpy->extensions[0] = &ddpy->loader_extension.base; ddpy->extensions[1] = &image_lookup_extension.base; ddpy->extensions[2] = &use_invalidate.base; ddpy->extensions[3] = NULL; if (!droid_create_screen(dpy)) { free(ddpy); return EGL_FALSE; } if (!droid_add_configs_for_visuals(drv, dpy)) { ddpy->core->destroyScreen(ddpy->dri_screen); free(ddpy); } dpy->Extensions.ANDROID_image_native_buffer = EGL_TRUE; dpy->Extensions.KHR_image_base = EGL_TRUE; /* we're supporting EGL 1.4 */ dpy->VersionMajor = 1; dpy->VersionMinor = 4; return EGL_TRUE; } static EGLBoolean droid_terminate(_EGLDriver *drv, _EGLDisplay *dpy) { struct droid_egl_display *ddpy = droid_egl_display(dpy); _eglReleaseDisplayResources(drv, dpy); _eglCleanupDisplay(dpy); ddpy->core->destroyScreen(ddpy->dri_screen); free(ddpy); dpy->DriverData = NULL; return EGL_TRUE; } static EGLBoolean droid_initialize(_EGLDriver *drv, _EGLDisplay *dpy) { /* not until swrast_dri is supported */ if (dpy->Options.UseFallback) return EGL_FALSE; switch (dpy->Platform) { case _EGL_PLATFORM_ANDROID: if (dpy->Options.TestOnly) return EGL_TRUE; return droid_initialize_android(drv, dpy); default: return EGL_FALSE; } } static _EGLProc droid_get_proc_address(_EGLDriver *drv, const char *procname) { struct droid_egl_driver *ddrv = droid_egl_driver(drv); return ddrv->get_proc_address(procname); } static void droid_log(EGLint level, const char *msg) { switch (level) { case _EGL_DEBUG: LOGD(msg); break; case _EGL_INFO: LOGI(msg); break; case _EGL_WARNING: LOGW(msg); break; case _EGL_FATAL: LOG_FATAL(msg); break; default: break; } } static void droid_unload(_EGLDriver *drv) { struct droid_egl_driver *ddrv = droid_egl_driver(drv); free(ddrv); } #include "glapi/glapi.h" /* for _glapi_get_proc_address */ static EGLBoolean droid_load(_EGLDriver *drv) { struct droid_egl_driver *ddrv = droid_egl_driver(drv); ddrv->get_proc_address = (_EGLProc (*)(const char *)) _glapi_get_proc_address; ddrv->glFlush = (void (*)(void)) ddrv->get_proc_address("glFlush"); ddrv->glFinish = (void (*)(void)) ddrv->get_proc_address("glFinish"); return EGL_TRUE; } _EGLDriver * droid_create_driver(void) { struct droid_egl_driver *ddrv; ddrv = calloc(1, sizeof(*ddrv)); if (!ddrv) return NULL; if (!droid_load(&ddrv->base)) return NULL; _eglSetLogProc(droid_log); ddrv->base.Name = "Droid"; ddrv->base.Unload = droid_unload; _eglInitDriverFallbacks(&ddrv->base); ddrv->base.API.Initialize = droid_initialize; ddrv->base.API.Terminate = droid_terminate; ddrv->base.API.GetProcAddress = droid_get_proc_address; return &ddrv->base; }