/* * YaGL * * Copyright (c) 2012 Samsung Electronics Co., Ltd. All rights reserved. * * Contact : * Vasily Ulyanov * Jinhyung Jo * YeongKyoon Lee * * 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. * * Contributors: * - S-Core Co., Ltd * */ #include "yagl_onscreen_image_tizen_sfc.h" #include "yagl_display.h" #include "yagl_native_display.h" #include "yagl_log.h" #include "yagl_malloc.h" #include "yagl_host_egl_calls.h" #include "yagl_egl_state.h" #include "yagl_state.h" #include "yagl_client_interface.h" #include "yagl_client_image.h" #include "drm_fourcc.h" #include "vigs.h" #include #include #include #include static inline uint32_t rgba2argb(uint32_t rgba) { return ((rgba & 0xff) << 24) | (rgba >> 8); } static inline uint32_t yuv2argb(float y, float u, float v) { int32_t r, g, b; r = y + 1.402 * (v - 128); g = y - 0.344 * (u - 128) - 0.714 * (v - 128); b = y + 1.772 * (u - 128); r = r < 0 ? 0 : (r > 255 ? 255 : r); g = g < 0 ? 0 : (g > 255 ? 255 : g); b = b < 0 ? 0 : (b > 255 ? 255 : b); return (0xff000000 | (r << 16) | (g << 8) | b); } static inline uint32_t argb2xbgr(uint32_t argb) { uint32_t r = (argb & 0xff0000) >> 16; uint32_t g = (argb & 0xff00) >> 8; uint32_t b = (argb & 0xff); return (0xff000000) | (b << 16) | (g << 8) | (r); } static bool yagl_onscreen_image_tizen_sfc_convert(struct yagl_onscreen_image_tizen_sfc *image) { uint32_t *dst; float y, u, v; int i, j; tbm_surface_info_s info; int ret; YAGL_LOG_FUNC_SET(yagl_onscreen_image_tizen_sfc_convert); ret = tbm_surface_map(image->sfc, TBM_SURF_OPTION_READ, &info); if (ret != TBM_SURFACE_ERROR_NONE) { YAGL_LOG_ERROR("tbm_surface_map failed: %d", ret); return false; } ret = vigs_drm_surface_start_access(image->drm_sfc, VIGS_DRM_SAF_WRITE); if (ret) { YAGL_LOG_ERROR("vigs_drm_surface_start_access failed: %s", strerror(-ret)); tbm_surface_unmap(image->sfc); return false; } dst = image->drm_sfc->gem.vaddr; dst += info.width * info.height - info.width; switch (info.format) { case TBM_FORMAT_ARGB8888: for (i = 0; i < info.height; i++) { for (j = 0; j < info.width; j++) { uint32_t argb = ((uint32_t *)info.planes[0].ptr)[i * info.width + j]; *(dst - i * info.width + j) = argb; } } break; case TBM_FORMAT_RGBA8888: for (i = 0; i < info.height; i++) { for (j = 0; j < info.width; j++) { uint32_t rgba = ((uint32_t *)info.planes[0].ptr)[i * info.width + j]; *(dst - i * info.width + j) = rgba2argb(rgba); } } break; case TBM_FORMAT_NV21: for (i = 0; i < info.height; i++) { for (j = 0; j < info.width; j++) { y = info.planes[0].ptr[i * info.width + j]; v = info.planes[1].ptr[i * info.width / 2 + (j & ~1) + 0]; u = info.planes[1].ptr[i * info.width / 2 + (j & ~1) + 1]; *(dst - i * info.width + j) = yuv2argb(y, u, v); } } break; case TBM_FORMAT_YUV420: for (i = 0; i < info.height; i++) { for (j = 0; j < info.width; j++) { y = info.planes[0].ptr[i * info.width + j]; u = info.planes[1].ptr[(i / 2) * (info.width / 2) + (j / 2)]; v = info.planes[2].ptr[(i / 2) * (info.width / 2) + (j / 2)]; *(dst - i * info.width + j) = yuv2argb(y, u, v); } } break; } ret = vigs_drm_surface_end_access(image->drm_sfc, 1); if (ret) { YAGL_LOG_ERROR("vigs_drm_surface_end_access failed: %s", strerror(-ret)); } tbm_surface_unmap(image->sfc); return true; } static bool yagl_onscreen_image_tizen_sfc_convert_back(struct yagl_onscreen_image_tizen_sfc *image) { uint32_t *dst; int i, j; tbm_surface_info_s info; int ret; YAGL_LOG_FUNC_SET(yagl_onscreen_image_tizen_sfc_convert_back); ret = tbm_surface_map(image->sfc, TBM_SURF_OPTION_WRITE, &info); if (ret != TBM_SURFACE_ERROR_NONE) { YAGL_LOG_ERROR("tbm_surface_map failed: %d", ret); return false; } ret = vigs_drm_surface_start_access(image->drm_sfc, VIGS_DRM_SAF_READ); if (ret) { YAGL_LOG_ERROR("vigs_drm_surface_start_access failed: %s", strerror(-ret)); tbm_surface_unmap(image->sfc); return false; } dst = image->drm_sfc->gem.vaddr; dst += info.width * info.height - info.width; switch (info.format) { case TBM_FORMAT_XBGR8888: for (i = 0; i < info.height; i++) { for (j = 0; j < info.width; j++) { uint32_t argb = *(dst - i * info.width + j); ((uint32_t *)info.planes[0].ptr)[i * info.width + j] = argb2xbgr(argb); } } YAGL_LOG_ERROR("cpu converting: %p[0] = 0x%x", info.planes[0].ptr, ((uint32_t *)info.planes[0].ptr)[0]); break; } ret = vigs_drm_surface_end_access(image->drm_sfc, 1); if (ret) { YAGL_LOG_ERROR("vigs_drm_surface_end_access failed: %s", strerror(-ret)); return false; } tbm_surface_unmap(image->sfc); return true; } static void yagl_onscreen_image_tizen_sfc_update(struct yagl_image *image) { struct yagl_onscreen_image_tizen_sfc *tizen_sfc_image = (struct yagl_onscreen_image_tizen_sfc *)image; tbm_bo bo; struct vigs_drm_surface *src; int ret; YAGL_LOG_FUNC_SET(yagl_onscreen_image_tizen_sfc_update); if (!tizen_sfc_image->need_convert) { return; } switch (tizen_sfc_image->format) { case TBM_FORMAT_ARGB8888: bo = tbm_surface_internal_get_bo(tizen_sfc_image->sfc, 0); src = bo ? (struct vigs_drm_surface *)tbm_bo_get_handle(bo, TBM_DEVICE_3D).ptr : NULL; /* * No actual format conversion in ARGB8888 case. We just need to * y-invert the image. */ ret = vigs_drm_surface_convert(src, DRM_FORMAT_ARGB8888, tizen_sfc_image->drm_sfc, DRM_FORMAT_ARGB8888, true); if (ret == 0) { return; } YAGL_LOG_ERROR("vigs_drm_surface_convert failed %s\n", strerror(-ret)); break; case TBM_FORMAT_YUV420: bo = tbm_surface_internal_get_bo(tizen_sfc_image->sfc, 0); src = bo ? (struct vigs_drm_surface *)tbm_bo_get_handle(bo, TBM_DEVICE_3D).ptr : NULL; ret = vigs_drm_surface_convert(src, DRM_FORMAT_YUV420, tizen_sfc_image->drm_sfc, DRM_FORMAT_ARGB8888, false); if (ret == 0) { return; } YAGL_LOG_ERROR("vigs_drm_surface_convert failed %s\n", strerror(-ret)); break; } /* * Fallback to software converson in case of error or unsupported format */ yagl_onscreen_image_tizen_sfc_convert(tizen_sfc_image); } static void yagl_onscreen_image_tizen_sfc_finalize(struct yagl_image *image) { struct yagl_onscreen_image_tizen_sfc *tizen_sfc_image = (struct yagl_onscreen_image_tizen_sfc *)image; tbm_bo bo; struct vigs_drm_surface *dst; int ret; YAGL_LOG_FUNC_SET(yagl_onscreen_image_tizen_sfc_finalize); vigs_drm_surface_set_gpu_dirty(tizen_sfc_image->drm_sfc); if (!tizen_sfc_image->need_convert_back) { return; } switch (tizen_sfc_image->format) { case TBM_FORMAT_XBGR8888: bo = tbm_surface_internal_get_bo(tizen_sfc_image->sfc, 0); dst = bo ? (struct vigs_drm_surface *)tbm_bo_get_handle(bo, TBM_DEVICE_3D).ptr : NULL; ret = vigs_drm_surface_convert(tizen_sfc_image->drm_sfc, DRM_FORMAT_ARGB8888, dst, DRM_FORMAT_XBGR8888, true); if (ret == 0) { return; } YAGL_LOG_ERROR("vigs_drm_surface_convert failed %s\n", strerror(-ret)); break; } yagl_onscreen_image_tizen_sfc_convert_back(tizen_sfc_image); } static void yagl_onscreen_image_tizen_sfc_destroy(struct yagl_ref *ref) { struct yagl_onscreen_image_tizen_sfc *image = (struct yagl_onscreen_image_tizen_sfc *)ref; vigs_drm_gem_unref(&image->drm_sfc->gem); tbm_surface_internal_unref(image->sfc); yagl_image_cleanup(&image->base); yagl_free(image); } struct yagl_onscreen_image_tizen_sfc *yagl_onscreen_image_tizen_sfc_create(struct yagl_display *dpy, EGLClientBuffer buffer, struct yagl_client_interface *iface) { EGLint error = 0; yagl_object_name tex_global_name = yagl_get_global_name(); struct yagl_client_image *client_image; struct yagl_onscreen_image_tizen_sfc *image = NULL; struct vigs_drm_surface *drm_sfc = NULL; tbm_surface_h sfc; tbm_bo bo; tbm_surface_info_s info; bool need_convert; bool need_convert_back; int ret; YAGL_LOG_FUNC_SET(yagl_onscreen_image_tizen_sfc_create); sfc = (tbm_surface_h)buffer; tbm_surface_internal_ref(sfc); ret = tbm_surface_get_info(sfc, &info); if (ret != TBM_SURFACE_ERROR_NONE) { YAGL_LOG_ERROR("tbm_surface_get_info failed: %d", ret); yagl_set_error(EGL_BAD_PARAMETER); goto fail; } switch (info.format) { case TBM_FORMAT_RGB888: case TBM_FORMAT_XRGB8888: need_convert = false; need_convert_back = false; break; case TBM_FORMAT_ARGB8888: case TBM_FORMAT_RGBA8888: case TBM_FORMAT_NV21: case TBM_FORMAT_YUV420: need_convert = true; need_convert_back = false; break; case TBM_FORMAT_XBGR8888: need_convert = true; need_convert_back = true; break; default: YAGL_LOG_ERROR("bad format: 0x%X", info.format); yagl_set_error(EGL_BAD_PARAMETER); goto fail; } if (need_convert) { ret = vigs_drm_surface_create(dpy->native_dpy->drm_dev, info.width, info.height, info.width * 4, vigs_drm_surface_bgra8888, 0, &drm_sfc); if (ret) { YAGL_LOG_ERROR("vigs_drm_surface_create failed: %s", strerror(-ret)); yagl_set_error(EGL_BAD_ALLOC); goto fail; } ret = vigs_drm_gem_map(&drm_sfc->gem, 1); if (ret) { YAGL_LOG_ERROR("vigs_drm_gem_map failed: %s", strerror(-ret)); yagl_set_error(EGL_BAD_ALLOC); goto fail; } } else { if (tbm_surface_internal_get_num_bos(sfc) < 1) { YAGL_LOG_ERROR("tbm_surface_internal_get_num_bos < 1"); yagl_set_error(EGL_BAD_PARAMETER); goto fail; } bo = tbm_surface_internal_get_bo(sfc, 0); drm_sfc = bo ? (struct vigs_drm_surface *)tbm_bo_get_handle(bo, TBM_DEVICE_3D).ptr : NULL; if (!drm_sfc) { YAGL_LOG_ERROR("drm_sfc is NULL"); yagl_set_error(EGL_BAD_PARAMETER); goto fail; } vigs_drm_gem_ref(&drm_sfc->gem); } ret = vigs_drm_gem_get_name(&drm_sfc->gem); if (ret) { YAGL_LOG_ERROR("vigs_drm_gem_get_name failed: %s", strerror(-ret)); yagl_set_error(EGL_BAD_ALLOC); goto fail; } if (!yagl_host_eglCreateImageYAGL(tex_global_name, dpy->host_dpy, drm_sfc->id, &error)) { YAGL_LOG_ERROR("yagl_host_eglCreateImageYAGL failed: %d", error); yagl_set_error(error); goto fail; } client_image = iface->create_image(iface, tex_global_name); image = yagl_malloc0(sizeof(*image)); yagl_image_init(&image->base, &yagl_onscreen_image_tizen_sfc_destroy, dpy, (EGLImageKHR)INT2VOIDP(drm_sfc->gem.name), client_image); yagl_client_image_release(client_image); image->base.update = &yagl_onscreen_image_tizen_sfc_update; image->base.finalize = &yagl_onscreen_image_tizen_sfc_finalize; image->sfc = sfc; image->need_convert = need_convert; image->need_convert_back = need_convert_back; image->drm_sfc = drm_sfc; image->format = info.format; YAGL_LOG_DEBUG("%ux%u/%u, sfc_id = %u, need_convert = %d (0x%X), num_planes = %u, size = %u", info.width, info.height, info.bpp, drm_sfc->id, need_convert, info.format, info.num_planes, info.size); return image; fail: if (drm_sfc) { vigs_drm_gem_unref(&drm_sfc->gem); } yagl_free(image); tbm_surface_internal_unref(sfc); return NULL; }