/* * Mesa 3-D graphics library * * Copyright (C) 2010-2011 Chia-I Wu * Copyright (C) 2010-2011 LunarG Inc. * * 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 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. */ #define LOG_TAG "EGL-GALLIUM" #if ANDROID_VERSION >= 0x0400 #if ANDROID_VERSION >= 0x0402 #include #endif #include #include #else /* ANDROID_VERSION >= 0x0400 */ #define android_native_buffer_t ANativeWindowBuffer #include #include #endif /* ANDROID_VERSION >= 0x0400 */ #if ANDROID_VERSION < 0x0402 /* rename to old logging macros */ #define ALOGE(...) LOGE(__VA_ARGS__) #define ALOGW(...) LOGW(__VA_ARGS__) #define ALOGI(...) LOGI(__VA_ARGS__) #define ALOGD(...) LOGD(__VA_ARGS__) #endif #include #include #include #include extern "C" { #include "egllog.h" } #include "util/u_memory.h" #include "util/u_inlines.h" #include "util/u_format.h" #include "util/u_box.h" #include "common/native.h" #include "common/native_helper.h" #include "android/android_sw_winsys.h" #include "state_tracker/drm_driver.h" struct android_config; struct android_display { struct native_display base; boolean use_drm; const struct native_event_handler *event_handler; struct android_config *configs; int num_configs; }; struct android_surface { struct native_surface base; struct android_display *adpy; ANativeWindow *win; /* staging color buffer for when buffer preserving is enabled */ struct pipe_resource *color_res; uint stamp; ANativeWindowBuffer *buf; struct pipe_resource *buf_res; /* cache the current back buffers */ struct { int width; int height; int format; } cache_key; void *cache_handles[2]; struct pipe_resource *cache_resources[2]; }; struct android_config { struct native_config base; }; static INLINE struct android_display * android_display(const struct native_display *ndpy) { return (struct android_display *) ndpy; } static INLINE struct android_surface * android_surface(const struct native_surface *nsurf) { return (struct android_surface *) nsurf; } static INLINE struct android_config * android_config(const struct native_config *nconf) { return (struct android_config *) nconf; } namespace android { static enum pipe_format get_pipe_format(int native) { enum pipe_format fmt; switch (native) { case HAL_PIXEL_FORMAT_RGBA_8888: fmt = PIPE_FORMAT_R8G8B8A8_UNORM; break; case HAL_PIXEL_FORMAT_RGBX_8888: fmt = PIPE_FORMAT_R8G8B8X8_UNORM; break; case HAL_PIXEL_FORMAT_RGB_888: fmt = PIPE_FORMAT_R8G8B8_UNORM; break; case HAL_PIXEL_FORMAT_RGB_565: fmt = PIPE_FORMAT_B5G6R5_UNORM; break; case HAL_PIXEL_FORMAT_BGRA_8888: fmt = PIPE_FORMAT_B8G8R8A8_UNORM; break; default: ALOGE("unsupported native format 0x%x", native); fmt = PIPE_FORMAT_NONE; break; } return fmt; } #ifndef ANDROID_BACKEND_NO_DRM extern "C" { #include } static int get_handle_name(buffer_handle_t handle) { return gralloc_drm_get_gem_handle(handle); } #else static int get_handle_name(buffer_handle_t handle) { return 0; } #endif /* ANDROID_BACKEND_NO_DRM */ /** * Import an ANativeWindowBuffer allocated by the server. */ static struct pipe_resource * import_buffer(struct android_display *adpy, const struct pipe_resource *templ, ANativeWindowBuffer *abuf) { struct pipe_screen *screen = adpy->base.screen; struct pipe_resource *res; if (templ->bind & PIPE_BIND_RENDER_TARGET) { if (!screen->is_format_supported(screen, templ->format, templ->target, 0, PIPE_BIND_RENDER_TARGET)) ALOGW("importing unsupported buffer as render target"); } if (templ->bind & PIPE_BIND_SAMPLER_VIEW) { if (!screen->is_format_supported(screen, templ->format, templ->target, 0, PIPE_BIND_SAMPLER_VIEW)) ALOGW("importing unsupported buffer as sampler view"); } if (adpy->use_drm) { struct winsys_handle handle; memset(&handle, 0, sizeof(handle)); handle.type = DRM_API_HANDLE_TYPE_SHARED; /* for DRM, we need the GEM name */ handle.handle = get_handle_name(abuf->handle); if (!handle.handle) { ALOGE("unable to import invalid buffer %p", abuf); return NULL; } handle.stride = abuf->stride * util_format_get_blocksize(templ->format); res = screen->resource_from_handle(screen, templ, &handle); } else { struct android_winsys_handle handle; memset(&handle, 0, sizeof(handle)); handle.handle = abuf->handle; handle.stride = abuf->stride * util_format_get_blocksize(templ->format); res = screen->resource_from_handle(screen, templ, (struct winsys_handle *) &handle); } if (!res) ALOGE("failed to import buffer %p", abuf); return res; } static void android_surface_clear_cache(struct native_surface *nsurf) { struct android_surface *asurf = android_surface(nsurf); int i; for (i = 0; i < Elements(asurf->cache_handles); i++) { asurf->cache_handles[i] = NULL; pipe_resource_reference(&asurf->cache_resources[i], NULL); } memset(&asurf->cache_key, 0, sizeof(asurf->cache_key)); } static struct pipe_resource * android_surface_add_cache(struct native_surface *nsurf, ANativeWindowBuffer *abuf) { struct android_surface *asurf = android_surface(nsurf); void *handle; int idx; /* how about abuf->usage? */ if (asurf->cache_key.width != abuf->width || asurf->cache_key.height != abuf->height || asurf->cache_key.format != abuf->format) android_surface_clear_cache(&asurf->base); if (asurf->adpy->use_drm) handle = (void *) get_handle_name(abuf->handle); else handle = (void *) abuf->handle; /* NULL is invalid */ if (!handle) { ALOGE("invalid buffer native buffer %p", abuf); return NULL; } /* find the slot to use */ for (idx = 0; idx < Elements(asurf->cache_handles); idx++) { if (asurf->cache_handles[idx] == handle || !asurf->cache_handles[idx]) break; } if (idx == Elements(asurf->cache_handles)) { ALOGW("cache full: buf %p, width %d, height %d, format %d, usage 0x%x", abuf, abuf->width, abuf->height, abuf->format, abuf->usage); android_surface_clear_cache(&asurf->base); idx = 0; } if (idx == 0) { asurf->cache_key.width = abuf->width; asurf->cache_key.height = abuf->height; asurf->cache_key.format = abuf->format; } if (!asurf->cache_handles[idx]) { struct pipe_resource templ; assert(!asurf->cache_resources[idx]); memset(&templ, 0, sizeof(templ)); templ.target = PIPE_TEXTURE_2D; templ.format = get_pipe_format(asurf->buf->format); templ.bind = PIPE_BIND_RENDER_TARGET; if (!asurf->adpy->use_drm) { templ.bind |= PIPE_BIND_TRANSFER_WRITE | PIPE_BIND_TRANSFER_READ; } templ.width0 = asurf->buf->width; templ.height0 = asurf->buf->height; templ.depth0 = 1; templ.array_size = 1; if (templ.format != PIPE_FORMAT_NONE) { asurf->cache_resources[idx] = import_buffer(asurf->adpy, &templ, asurf->buf); } else { asurf->cache_resources[idx] = NULL; } asurf->cache_handles[idx] = handle; } return asurf->cache_resources[idx]; } /** * Dequeue the next back buffer for rendering. */ static boolean android_surface_dequeue_buffer(struct native_surface *nsurf) { struct android_surface *asurf = android_surface(nsurf); struct pipe_resource *res; #if ANDROID_VERSION >= 0x0402 int fence_fd; if (asurf->win->dequeueBuffer(asurf->win, &asurf->buf, &fence_fd) != NO_ERROR) { ALOGE("failed to dequeue window %p", asurf->win); return FALSE; } if (fence_fd >= 0) { sync_wait(fence_fd, -1); close(fence_fd); } asurf->buf->common.incRef(&asurf->buf->common); #else if (asurf->win->dequeueBuffer(asurf->win, &asurf->buf) != NO_ERROR) { ALOGE("failed to dequeue window %p", asurf->win); return FALSE; } asurf->buf->common.incRef(&asurf->buf->common); asurf->win->lockBuffer(asurf->win, asurf->buf); #endif res = android_surface_add_cache(&asurf->base, asurf->buf); if (!res) return FALSE; pipe_resource_reference(&asurf->buf_res, res); return TRUE; } /** * Enqueue the back buffer. This will make it the next front buffer. */ static boolean android_surface_enqueue_buffer(struct native_surface *nsurf) { struct android_surface *asurf = android_surface(nsurf); pipe_resource_reference(&asurf->buf_res, NULL); #if ANDROID_VERSION >= 0x0402 asurf->win->queueBuffer(asurf->win, asurf->buf, -1); #else asurf->win->queueBuffer(asurf->win, asurf->buf); #endif asurf->buf->common.decRef(&asurf->buf->common); asurf->buf = NULL; return TRUE; } static boolean android_surface_swap_buffers(struct native_surface *nsurf) { struct android_surface *asurf = android_surface(nsurf); struct android_display *adpy = asurf->adpy; struct native_display *ndpy = &adpy->base; struct pipe_context *pipe = ndpy_get_copy_context(ndpy); /* flush buffer */ pipe->flush_resource(pipe, asurf->buf_res); pipe->flush(pipe, NULL, 0); android_surface_enqueue_buffer(&asurf->base); asurf->stamp++; adpy->event_handler->invalid_surface(&adpy->base, &asurf->base, asurf->stamp); return TRUE; } static void copy_resources(struct native_display *ndpy, struct pipe_resource *src, struct pipe_resource *dst) { struct pipe_context *pipe; struct pipe_box box; pipe = ndpy_get_copy_context(ndpy); if (!pipe) return; u_box_origin_2d(src->width0, src->height0, &box); pipe->resource_copy_region(pipe, dst, 0, 0, 0, 0, src, 0, &box); pipe->flush(pipe, NULL, 0); } static boolean android_surface_present(struct native_surface *nsurf, const native_present_control *ctrl) { struct android_surface *asurf = android_surface(nsurf); struct android_display *adpy = asurf->adpy; boolean ret; if (ctrl->swap_interval || ctrl->natt != NATIVE_ATTACHMENT_BACK_LEFT) return FALSE; /* this happens when eglSwapBuffers is called more than once in a row */ if (!asurf->buf) return TRUE; /* we always render to color_res first when it exists */ if (asurf->color_res) { copy_resources(&adpy->base, asurf->color_res, asurf->buf_res); if (!ctrl->preserve) pipe_resource_reference(&asurf->color_res, NULL); } else if (ctrl->preserve) { struct pipe_resource templ; memset(&templ, 0, sizeof(templ)); templ.target = asurf->buf_res->target; templ.format = asurf->buf_res->format; templ.bind = PIPE_BIND_RENDER_TARGET; templ.width0 = asurf->buf_res->width0; templ.height0 = asurf->buf_res->height0; templ.depth0 = asurf->buf_res->depth0; templ.array_size = asurf->buf_res->array_size; asurf->color_res = adpy->base.screen->resource_create(adpy->base.screen, &templ); if (!asurf->color_res) return FALSE; /* preserve the contents */ copy_resources(&adpy->base, asurf->buf_res, asurf->color_res); } return android_surface_swap_buffers(nsurf); } static boolean android_surface_validate(struct native_surface *nsurf, uint attachment_mask, unsigned int *seq_num, struct pipe_resource **textures, int *width, int *height) { struct android_surface *asurf = android_surface(nsurf); struct winsys_handle handle; if (!asurf->buf) { if (!android_surface_dequeue_buffer(&asurf->base)) return FALSE; /* color_res must be compatible with buf_res */ if (asurf->color_res && (asurf->color_res->format != asurf->buf_res->format || asurf->color_res->width0 != asurf->buf_res->width0 || asurf->color_res->height0 != asurf->buf_res->height0)) pipe_resource_reference(&asurf->color_res, NULL); } if (textures) { /* we have access to only the back buffer */ const enum native_attachment att = NATIVE_ATTACHMENT_BACK_LEFT; if (native_attachment_mask_test(attachment_mask, att)) { textures[att] = NULL; pipe_resource_reference(&textures[att], (asurf->color_res) ? asurf->color_res : asurf->buf_res); } } if (seq_num) *seq_num = asurf->stamp; if (width) *width = asurf->buf->width; if (height) *height = asurf->buf->height; return TRUE; } static void android_surface_wait(struct native_surface *nsurf) { } static void android_surface_destroy(struct native_surface *nsurf) { struct android_surface *asurf = android_surface(nsurf); int i; pipe_resource_reference(&asurf->color_res, NULL); if (asurf->buf) android_surface_enqueue_buffer(&asurf->base); android_surface_clear_cache(&asurf->base); asurf->win->common.decRef(&asurf->win->common); FREE(asurf); } static struct native_surface * android_display_create_window_surface(struct native_display *ndpy, EGLNativeWindowType win, const struct native_config *nconf) { struct android_display *adpy = android_display(ndpy); struct android_config *aconf = android_config(nconf); struct android_surface *asurf; enum pipe_format format; int val; if (win->common.magic != ANDROID_NATIVE_WINDOW_MAGIC) { ALOGE("invalid native window with magic 0x%x", win->common.magic); return NULL; } if (win->query(win, NATIVE_WINDOW_FORMAT, &val)) { ALOGE("failed to query native window format"); return NULL; } format = get_pipe_format(val); if (format != nconf->color_format) { ALOGW("native window format 0x%x != config format 0x%x", format, nconf->color_format); if (!adpy->base.screen->is_format_supported(adpy->base.screen, format, PIPE_TEXTURE_2D, 0, PIPE_BIND_RENDER_TARGET)) { ALOGE("and the native window cannot be used as a render target"); return NULL; } } asurf = CALLOC_STRUCT(android_surface); if (!asurf) return NULL; asurf->adpy = adpy; asurf->win = win; asurf->win->common.incRef(&asurf->win->common); /* request buffers that are for CPU access */ if (!adpy->use_drm) { native_window_set_usage(asurf->win, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); } asurf->base.destroy = android_surface_destroy; asurf->base.present = android_surface_present; asurf->base.validate = android_surface_validate; asurf->base.wait = android_surface_wait; return &asurf->base; } static boolean android_display_init_configs(struct native_display *ndpy) { struct android_display *adpy = android_display(ndpy); const int native_formats[] = { HAL_PIXEL_FORMAT_RGBA_8888, HAL_PIXEL_FORMAT_RGBX_8888, HAL_PIXEL_FORMAT_RGB_888, HAL_PIXEL_FORMAT_RGB_565, HAL_PIXEL_FORMAT_BGRA_8888, }; int i; adpy->configs = (struct android_config *) CALLOC(Elements(native_formats), sizeof(*adpy->configs)); if (!adpy->configs) return FALSE; for (i = 0; i < Elements(native_formats); i++) { enum pipe_format color_format; struct android_config *aconf; color_format = get_pipe_format(native_formats[i]); if (color_format == PIPE_FORMAT_NONE || !adpy->base.screen->is_format_supported(adpy->base.screen, color_format, PIPE_TEXTURE_2D, 0, PIPE_BIND_RENDER_TARGET)) { ALOGI("skip unsupported native format 0x%x", native_formats[i]); continue; } aconf = &adpy->configs[adpy->num_configs++]; /* only the back buffer */ aconf->base.buffer_mask = 1 << NATIVE_ATTACHMENT_BACK_LEFT; aconf->base.color_format = color_format; aconf->base.window_bit = TRUE; aconf->base.native_visual_id = native_formats[i]; aconf->base.native_visual_type = native_formats[i]; } return TRUE; } static boolean android_display_init_drm(struct native_display *ndpy) { struct android_display *adpy = android_display(ndpy); const hw_module_t *mod; int fd, err; #ifndef ANDROID_BACKEND_NO_DRM /* get the authorized fd from gralloc */ 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) { adpy->base.screen = adpy->event_handler->new_drm_screen(&adpy->base, NULL, fd); } #endif if (adpy->base.screen) { ALOGI("using DRM screen"); return TRUE; } else { ALOGW("failed to create DRM screen"); ALOGW("will fall back to other EGL drivers if any"); return FALSE; } } static boolean android_display_init_sw(struct native_display *ndpy) { struct android_display *adpy = android_display(ndpy); struct sw_winsys *ws; ws = android_create_sw_winsys(); if (ws) { adpy->base.screen = adpy->event_handler->new_sw_screen(&adpy->base, ws); } if (adpy->base.screen) { ALOGI("using SW screen"); return TRUE; } else { ALOGE("failed to create SW screen"); return FALSE; } } static boolean android_display_init_screen(struct native_display *ndpy) { struct android_display *adpy = android_display(ndpy); if (adpy->use_drm) android_display_init_drm(&adpy->base); else android_display_init_sw(&adpy->base); if (!adpy->base.screen) return FALSE; if (!android_display_init_configs(&adpy->base)) { adpy->base.screen->destroy(adpy->base.screen); adpy->base.screen = NULL; return FALSE; } return TRUE; } static void android_display_destroy(struct native_display *ndpy) { struct android_display *adpy = android_display(ndpy); FREE(adpy->configs); if (adpy->base.screen) adpy->base.screen->destroy(adpy->base.screen); FREE(adpy); } static const struct native_config ** android_display_get_configs(struct native_display *ndpy, int *num_configs) { struct android_display *adpy = android_display(ndpy); const struct native_config **configs; int i; configs = (const struct native_config **) MALLOC(adpy->num_configs * sizeof(*configs)); if (configs) { for (i = 0; i < adpy->num_configs; i++) configs[i] = (const struct native_config *) &adpy->configs[i]; if (num_configs) *num_configs = adpy->num_configs; } return configs; } static int android_display_get_param(struct native_display *ndpy, enum native_param_type param) { int val; switch (param) { case NATIVE_PARAM_PRESERVE_BUFFER: val = 1; break; default: val = 0; break; } return val; } static struct pipe_resource * android_display_import_buffer(struct native_display *ndpy, struct native_buffer *nbuf) { struct android_display *adpy = android_display(ndpy); ANativeWindowBuffer *abuf; enum pipe_format format; struct pipe_resource templ; if (nbuf->type != NATIVE_BUFFER_ANDROID) return NULL; abuf = nbuf->u.android; if (!abuf || abuf->common.magic != ANDROID_NATIVE_BUFFER_MAGIC || abuf->common.version != sizeof(*abuf)) { ALOGE("invalid android native buffer"); return NULL; } format = get_pipe_format(abuf->format); if (format == PIPE_FORMAT_NONE) return NULL; memset(&templ, 0, sizeof(templ)); templ.target = PIPE_TEXTURE_2D; templ.format = format; /* assume for texturing only */ templ.bind = PIPE_BIND_SAMPLER_VIEW; templ.width0 = abuf->width; templ.height0 = abuf->height; templ.depth0 = 1; templ.array_size = 1; return import_buffer(adpy, &templ, abuf); } static boolean android_display_export_buffer(struct native_display *ndpy, struct pipe_resource *res, struct native_buffer *nbuf) { return FALSE; } static struct native_display_buffer android_display_buffer = { android_display_import_buffer, android_display_export_buffer }; static struct android_display * android_display_create(const struct native_event_handler *event_handler, boolean use_sw) { struct android_display *adpy; char value[PROPERTY_VALUE_MAX]; boolean force_sw; /* check if SW renderer is forced */ if (property_get("debug.mesa.software", value, NULL)) force_sw = (atoi(value) != 0); else force_sw = debug_get_bool_option("EGL_SOFTWARE", FALSE); if (force_sw) use_sw = TRUE; adpy = CALLOC_STRUCT(android_display); if (!adpy) return NULL; adpy->event_handler = event_handler; adpy->use_drm = !use_sw; adpy->base.init_screen = android_display_init_screen; adpy->base.destroy = android_display_destroy; adpy->base.get_param = android_display_get_param; adpy->base.get_configs = android_display_get_configs; adpy->base.create_window_surface = android_display_create_window_surface; adpy->base.buffer = &android_display_buffer; return adpy; } static const struct native_event_handler *android_event_handler; static struct native_display * native_create_display(void *dpy, boolean use_sw) { struct android_display *adpy; adpy = android_display_create(android_event_handler, use_sw); return (adpy) ? &adpy->base : NULL; } static const struct native_platform android_platform = { "Android", /* name */ native_create_display }; }; /* namespace android */ using namespace android; static void android_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; } } const struct native_platform * native_get_android_platform(const struct native_event_handler *event_handler) { android_event_handler = event_handler; /* use Android logger */ _eglSetLogProc(android_log); return &android_platform; }