diff --git a/Android.mk b/Android.mk index a204c96..d7111c0 100644 --- a/Android.mk +++ b/Android.mk @@ -2,5 +2,6 @@ ifeq ($(BOARD_USES_QCOM_HARDWARE),true) display-hals := libhwcomposer liboverlay libgralloc libcopybit + display-hals += libqcomui include $(call all-named-subdir-makefiles,$(display-hals)) endif diff --git a/libcopybit/Android.mk b/libcopybit/Android.mk index 45c4fa2..1d2a0e5 100644 --- a/libcopybit/Android.mk +++ b/libcopybit/Android.mk @@ -21,8 +21,8 @@ ifeq ($(TARGET_USES_C2D_COMPOSITION),true) include $(CLEAR_VARS) LOCAL_PRELINK_MODULE := false LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw - LOCAL_SHARED_LIBRARIES := liblog libdl - LOCAL_SRC_FILES := copybit_c2d.cpp + LOCAL_SHARED_LIBRARIES := liblog libdl libcutils + LOCAL_SRC_FILES := copybit_c2d.cpp software_converter.cpp LOCAL_MODULE := copybit.$(TARGET_BOARD_PLATFORM) LOCAL_C_INCLUDES += hardware/qcom/display/libgralloc LOCAL_CFLAGS += -DCOPYBIT_Z180=1 -DC2D_SUPPORT_DISPLAY=1 diff --git a/libcopybit/copybit.cpp b/libcopybit/copybit.cpp index 9807aef..42378ea 100644 --- a/libcopybit/copybit.cpp +++ b/libcopybit/copybit.cpp @@ -387,9 +387,8 @@ static int stretch_copybit( if(src->format == HAL_PIXEL_FORMAT_YV12) { if(0 == convertYV12toYCrCb420SP(src)){ - //if inplace conversion,just convert and return - if(src->base == dst->base) - return status; + (const_cast(src))->format = + HAL_PIXEL_FORMAT_YCrCb_420_SP; } else{ LOGE("Error copybit conversion from yv12 failed"); diff --git a/libcopybit/copybit.h b/libcopybit/copybit.h index 920f858..e6c55e0 100644 --- a/libcopybit/copybit.h +++ b/libcopybit/copybit.h @@ -65,7 +65,11 @@ enum { COPYBIT_BLUR = 5, /* Informs the copybit that the source and destination contains premultiplied alpha */ - COPYBIT_PREMULTIPLIED_ALPHA = 6 + COPYBIT_PREMULTIPLIED_ALPHA = 6, + /* FB width */ + COPYBIT_FRAMEBUFFER_WIDTH = 7, + /* FB height */ + COPYBIT_FRAMEBUFFER_HEIGHT = 8, }; /* values for copybit_set_parameter(COPYBIT_TRANSFORM) */ diff --git a/libcopybit/copybit_c2d.cpp b/libcopybit/copybit_c2d.cpp index 7680ab0..f05711d 100644 --- a/libcopybit/copybit_c2d.cpp +++ b/libcopybit/copybit_c2d.cpp @@ -36,11 +36,14 @@ #include #include #include +#include +#include #include #include #include "c2d2.h" +#include "software_converter.h" #include C2D_STATUS (*LINK_c2dCreateSurface)( uint32 *surface_id, @@ -83,20 +86,30 @@ C2D_STATUS (*LINK_c2dDestroySurface)( uint32 surface_id ); #define G12_DEVICE_NAME "/dev/kgsl-2d0" -#define COPYBIT_SUCCESS 0 -#define COPYBIT_FAILURE -1 +#define NUM_SURFACES 3 -#define RGB_SURFACE 0 -#define YUV_SURFACE 1 -#define NUM_SRC_SURFACES 2 -#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1)) +enum { + RGB_SURFACE, + YUV_SURFACE_2_PLANES, + YUV_SURFACE_3_PLANES +}; + +enum eConversionType { + CONVERT_TO_ANDROID_FORMAT, + CONVERT_TO_C2D_FORMAT +}; + +enum eC2DFlags { + FLAGS_PREMULTIPLIED_ALPHA = 1<<0, + FLAGS_YUV_DESTINATION = 1<<1 +}; /******************************************************************************/ /** State information for each device instance */ struct copybit_context_t { struct copybit_device_t device; - unsigned int src[NUM_SRC_SURFACES]; - unsigned int dst; /* dst surface */ + unsigned int src[NUM_SURFACES]; /* src surfaces */ + unsigned int dst[NUM_SURFACES]; /* dst surfaces */ unsigned int trg_transform; /* target transform */ C2D_OBJECT blitState; void *libc2d2; @@ -111,6 +124,26 @@ struct blitlist{ C2D_OBJECT blitObjects[12]; }; +struct bufferInfo { + int width; + int height; + int format; +}; + +struct memInfo { + int fd; + size_t size; + int base; +}; + +struct yuvPlaneInfo { + int yStride; //luma stride + int plane1_stride; + int plane2_stride; + int plane1_offset; + int plane2_offset; +}; + /** * Common hardware methods */ @@ -148,9 +181,25 @@ static int get_format(int format) { case HAL_PIXEL_FORMAT_RGBA_5551: return C2D_COLOR_FORMAT_5551_RGBA; case HAL_PIXEL_FORMAT_RGBA_4444: return C2D_COLOR_FORMAT_4444_RGBA; case HAL_PIXEL_FORMAT_YCbCr_420_SP: return C2D_COLOR_FORMAT_420_NV12; + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE:return C2D_COLOR_FORMAT_420_NV12; case HAL_PIXEL_FORMAT_YCrCb_420_SP: return C2D_COLOR_FORMAT_420_NV21; case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: return C2D_COLOR_FORMAT_420_NV12 | C2D_FORMAT_MACROTILED; - default: return -EINVAL; + default: LOGE("%s: invalid format (0x%x", __FUNCTION__, format); return -EINVAL; + } + return -EINVAL; +} + +/* Get the C2D formats needed for conversion to YUV */ +static int get_c2d_format_for_yuv_destination(int halFormat) { + switch (halFormat) { + // We do not swap the RB when the target is YUV + case HAL_PIXEL_FORMAT_RGBX_8888: return C2D_COLOR_FORMAT_8888_ARGB | C2D_FORMAT_DISABLE_ALPHA; + case HAL_PIXEL_FORMAT_RGBA_8888: return C2D_COLOR_FORMAT_8888_ARGB; + // The U and V need to be interchanged when the target is YUV + case HAL_PIXEL_FORMAT_YCbCr_420_SP: return C2D_COLOR_FORMAT_420_NV21; + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE:return C2D_COLOR_FORMAT_420_NV21; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: return C2D_COLOR_FORMAT_420_NV12; + default: return get_format(halFormat); } return -EINVAL; } @@ -256,11 +305,29 @@ static int is_supported_rgb_format(int format) } } +static int get_num_planes(int format) +{ + switch(format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: { + return 2; + } + case HAL_PIXEL_FORMAT_YV12: { + return 3; + } + default: + return COPYBIT_FAILURE; + } +} + static int is_supported_yuv_format(int format) { switch(format) { case HAL_PIXEL_FORMAT_YCbCr_420_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: { return COPYBIT_SUCCESS; } @@ -269,8 +336,21 @@ static int is_supported_yuv_format(int format) } } -static int calculate_yuv_offset_and_stride(int format, int width, int height, int *offset, int *yStride, int *uvStride) +static int is_valid_destination_format(int format) { + if (format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED) { + // C2D does not support NV12Tile as a destination format. + return COPYBIT_FAILURE; + } + return COPYBIT_SUCCESS; +} + +static int calculate_yuv_offset_and_stride(const bufferInfo& info, yuvPlaneInfo& yuvInfo) +{ + int width = info.width; + int height = info.height; + int format = info.format; + int aligned_height = 0; int aligned_width = 0, size = 0; @@ -282,17 +362,23 @@ static int calculate_yuv_offset_and_stride(int format, int width, int height, in aligned_height = ALIGN(height, 32); aligned_width = ALIGN(width, 128); size = aligned_width * aligned_height; - *offset = ALIGN(size,8192); - *yStride = aligned_width; - *uvStride = aligned_width; + yuvInfo.plane1_offset = ALIGN(size,8192); + yuvInfo.yStride = aligned_width; + yuvInfo.plane1_stride = aligned_width; break; } case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: case HAL_PIXEL_FORMAT_YCrCb_420_SP: { aligned_width = ALIGN(width, 32); - *offset = aligned_width * height; - *yStride = aligned_width; - *uvStride = aligned_width; + yuvInfo.yStride = aligned_width; + yuvInfo.plane1_stride = aligned_width; + if (HAL_PIXEL_FORMAT_NV12_ENCODEABLE == format) { + // The encoder requires a 2K aligned chroma offset + yuvInfo.plane1_offset = ALIGN(aligned_width * height, 2048); + } else + yuvInfo.plane1_offset = aligned_width * height; + break; } default: { @@ -304,15 +390,20 @@ static int calculate_yuv_offset_and_stride(int format, int width, int height, in /** create C2D surface from copybit image */ static int set_image(int device_fd, uint32 surfaceId, const struct copybit_image_t *rhs, int *cformat, uint32_t *mapped, - const bool isPremultipliedAlpha) + const eC2DFlags flags) { struct private_handle_t* handle = (struct private_handle_t*)rhs->handle; C2D_SURFACE_TYPE surfaceType; int status = COPYBIT_SUCCESS; - *cformat = get_format(rhs->format); + if (flags & FLAGS_YUV_DESTINATION) { + *cformat = get_c2d_format_for_yuv_destination(rhs->format); + } else { + *cformat = get_format(rhs->format); + } + if(*cformat == -EINVAL) { - LOGE("%s: invalid format", __func__); + LOGE("%s: invalid format", __FUNCTION__); return -EINVAL; } @@ -324,6 +415,7 @@ static int set_image(int device_fd, uint32 surfaceId, const struct copybit_image if (handle->gpuaddr == 0) { handle->gpuaddr = c2d_get_gpuaddr(device_fd, handle); if(!handle->gpuaddr) { + LOGE("%s: c2d_get_gpuaddr failed", __FUNCTION__); return COPYBIT_FAILURE; } *mapped = 1; @@ -339,28 +431,32 @@ static int set_image(int device_fd, uint32 surfaceId, const struct copybit_image surfaceDef.phys = (void*) handle->gpuaddr; surfaceDef.buffer = (void*) (handle->base); - surfaceDef.format = *cformat | (isPremultipliedAlpha ? C2D_FORMAT_PREMULTIPLIED : 0); + surfaceDef.format = *cformat | ((flags & FLAGS_PREMULTIPLIED_ALPHA) ? C2D_FORMAT_PREMULTIPLIED : 0); surfaceDef.width = rhs->w; surfaceDef.height = rhs->h; - surfaceDef.stride = ALIGN(((surfaceDef.width * c2diGetBpp(surfaceDef.format))>>3), 32); + int aligned_width = ALIGN(surfaceDef.width,32); + surfaceDef.stride = (aligned_width * c2diGetBpp(surfaceDef.format))>>3; if(LINK_c2dUpdateSurface( surfaceId,C2D_TARGET | C2D_SOURCE, surfaceType, &surfaceDef)) { - LOGE("%s: RGB Surface c2dUpdateSurface ERROR", __func__); + LOGE("%s: RGB Surface c2dUpdateSurface ERROR", __FUNCTION__); goto error; status = COPYBIT_FAILURE; } } else if (is_supported_yuv_format(rhs->format) == COPYBIT_SUCCESS) { C2D_YUV_SURFACE_DEF surfaceDef; - int offset = 0; - int yStride = 0; - int uvStride = 0; memset(&surfaceDef, 0, sizeof(surfaceDef)); surfaceType = (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS); surfaceDef.format = *cformat; - status = calculate_yuv_offset_and_stride(rhs->format, rhs->w, rhs->h, &offset, &yStride, &uvStride); + bufferInfo info; + info.width = rhs->w; + info.height = rhs->h; + info.format = rhs->format; + + yuvPlaneInfo yuvInfo; + status = calculate_yuv_offset_and_stride(info, yuvInfo); if(status != COPYBIT_SUCCESS) { - LOGE("calculate_yuv_offset_and_stride error"); + LOGE("%s: calculate_yuv_offset_and_stride error", __FUNCTION__); goto error; } @@ -368,19 +464,24 @@ static int set_image(int device_fd, uint32 surfaceId, const struct copybit_image surfaceDef.height = rhs->h; surfaceDef.plane0 = (void*) (handle->base); surfaceDef.phys0 = (void*) (handle->gpuaddr); - surfaceDef.stride0 = yStride; + surfaceDef.stride0 = yuvInfo.yStride; - surfaceDef.plane1 = (void*) (handle->base + offset); - surfaceDef.phys1 = (void*) (handle->gpuaddr + offset); - surfaceDef.stride1 = uvStride; + surfaceDef.plane1 = (void*) (handle->base + yuvInfo.plane1_offset); + surfaceDef.phys1 = (void*) (handle->gpuaddr + yuvInfo.plane1_offset); + surfaceDef.stride1 = yuvInfo.plane1_stride; + if (3 == get_num_planes(rhs->format)) { + surfaceDef.plane2 = (void*) (handle->base + yuvInfo.plane2_offset); + surfaceDef.phys2 = (void*) (handle->gpuaddr + yuvInfo.plane2_offset); + surfaceDef.stride2 = yuvInfo.plane2_stride; + } if(LINK_c2dUpdateSurface( surfaceId,C2D_TARGET | C2D_SOURCE, surfaceType, &surfaceDef)) { - LOGE("%s: YUV Surface c2dUpdateSurface ERROR", __func__); + LOGE("%s: YUV Surface c2dUpdateSurface ERROR", __FUNCTION__); goto error; status = COPYBIT_FAILURE; } } else { - LOGE("%s: invalid format %x", __func__, rhs->format); + LOGE("%s: invalid format 0x%x", __FUNCTION__, rhs->format); goto error; status = COPYBIT_FAILURE; } @@ -429,7 +530,7 @@ static int set_src_image(int device_fd, uint32 *surfaceId, const struct copybit_ surfaceDef.stride = ALIGN(((surfaceDef.width * c2diGetBpp(surfaceDef.format))>>3), 32); if(LINK_c2dCreateSurface( surfaceId, C2D_TARGET, surfaceType,(void*)&surfaceDef)) { - LOGE("%s: LINK_c2dCreateSurface error", __func__); + LOGE("%s: LINK_c2dCreateSurface error", __FUNCTION__); status = COPYBIT_FAILURE; goto error; } @@ -443,9 +544,15 @@ static int set_src_image(int device_fd, uint32 *surfaceId, const struct copybit_ surfaceType = (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS); surfaceDef.format = get_format(rhs->format); - status = calculate_yuv_offset_and_stride(rhs->format, rhs->w, rhs->h, &offset, &yStride, &uvStride); + bufferInfo info; + info.width = rhs->w; + info.height = rhs->h; + info.format = rhs->format; + + yuvPlaneInfo yuvInfo; + status = calculate_yuv_offset_and_stride(info, yuvInfo); if(status != COPYBIT_SUCCESS) { - LOGE("calculate_yuv_offset_and_stride error"); + LOGE("%s: calculate_yuv_offset_and_stride error", __FUNCTION__); goto error; } @@ -453,11 +560,11 @@ static int set_src_image(int device_fd, uint32 *surfaceId, const struct copybit_ surfaceDef.height = rhs->h; surfaceDef.plane0 = (void*) (handle->base); surfaceDef.phys0 = (void*) handle->gpuaddr; - surfaceDef.stride0 = yStride; + surfaceDef.stride0 = yuvInfo.yStride; - surfaceDef.plane1 = (void*) (handle->base + offset); - surfaceDef.phys1 = (void*) (handle->gpuaddr + offset); - surfaceDef.stride1 = uvStride; + surfaceDef.plane1 = (void*) (handle->base + yuvInfo.plane1_offset); + surfaceDef.phys1 = (void*) (handle->gpuaddr + yuvInfo.plane1_offset); + surfaceDef.stride1 = yuvInfo.plane1_stride; if(LINK_c2dCreateSurface( surfaceId, C2D_TARGET | C2D_SOURCE, surfaceType, (void*)&surfaceDef)) { LOGE("%s: YUV surface LINK_c2dCreateSurface error", __func__); @@ -465,7 +572,7 @@ static int set_src_image(int device_fd, uint32 *surfaceId, const struct copybit_ goto error; } } else { - LOGE("%s: Invalid format %x", __func__, rhs->format); + LOGE("%s: Invalid format 0x%x", __FUNCTION__, rhs->format); status = COPYBIT_FAILURE; } @@ -549,22 +656,26 @@ static void set_rects(struct copybit_context_t *ctx, const struct copybit_rect_t *src, const struct copybit_rect_t *scissor) { - + // Set the target rect. if((ctx->trg_transform & C2D_TARGET_ROTATE_90) && (ctx->trg_transform & C2D_TARGET_ROTATE_180)) { /* target rotation is 270 */ c2dObject->target_rect.x = (dst->t)<<16; - c2dObject->target_rect.y = (ALIGN(ctx->fb_width,32) - (dst->r))<<16; + c2dObject->target_rect.y = ctx->fb_width?(ALIGN(ctx->fb_width,32)- dst->r):dst->r; + c2dObject->target_rect.y = c2dObject->target_rect.y<<16; c2dObject->target_rect.height = ((dst->r) - (dst->l))<<16; c2dObject->target_rect.width = ((dst->b) - (dst->t))<<16; } else if(ctx->trg_transform & C2D_TARGET_ROTATE_90) { - c2dObject->target_rect.x = (ctx->fb_height - dst->b)<<16; + c2dObject->target_rect.x = ctx->fb_height?(ctx->fb_height - dst->b):dst->b; + c2dObject->target_rect.x = c2dObject->target_rect.x<<16; c2dObject->target_rect.y = (dst->l)<<16; c2dObject->target_rect.height = ((dst->r) - (dst->l))<<16; c2dObject->target_rect.width = ((dst->b) - (dst->t))<<16; } else if(ctx->trg_transform & C2D_TARGET_ROTATE_180) { - c2dObject->target_rect.y = (ctx->fb_height - dst->b)<<16; - c2dObject->target_rect.x = (ALIGN(ctx->fb_width,32) - dst->r)<<16; + c2dObject->target_rect.y = ctx->fb_height?(ctx->fb_height - dst->b):dst->b; + c2dObject->target_rect.y = c2dObject->target_rect.y<<16; + c2dObject->target_rect.x = ctx->fb_width?(ALIGN(ctx->fb_width,32) - dst->r):dst->r; + c2dObject->target_rect.x = c2dObject->target_rect.x<<16; c2dObject->target_rect.height = ((dst->b) - (dst->t))<<16; c2dObject->target_rect.width = ((dst->r) - (dst->l))<<16; } else { @@ -575,17 +686,18 @@ static void set_rects(struct copybit_context_t *ctx, } c2dObject->config_mask |= C2D_TARGET_RECT_BIT; + // Set the source rect c2dObject->source_rect.x = (src->l)<<16; c2dObject->source_rect.y = (src->t)<<16; c2dObject->source_rect.height = ((src->b) - (src->t))<<16; c2dObject->source_rect.width = ((src->r) - (src->l))<<16; c2dObject->config_mask |= C2D_SOURCE_RECT_BIT; + // Set the scissor rect c2dObject->scissor_rect.x = scissor->l; c2dObject->scissor_rect.y = scissor->t; c2dObject->scissor_rect.height = (scissor->b) - (scissor->t); c2dObject->scissor_rect.width = (scissor->r) - (scissor->l); - c2dObject->config_mask |= C2D_SCISSOR_RECT_BIT; } @@ -600,7 +712,7 @@ static int msm_copybit(struct copybit_context_t *dev, blitlist *list, uint32 tar if(LINK_c2dDraw(target,dev->trg_transform, 0x0, 0, 0, list->blitObjects, list->count)) { - LOGE("%s: LINK_c2dDraw ERROR"); + LOGE("%s: LINK_c2dDraw ERROR", __FUNCTION__); return COPYBIT_FAILURE; } @@ -617,7 +729,7 @@ static int set_parameter_copybit( { struct copybit_context_t* ctx = (struct copybit_context_t*)dev; if (!ctx) { - LOGE("%s: null context", __func__); + LOGE("%s: null context", __FUNCTION__); return -EINVAL; } @@ -668,8 +780,14 @@ static int set_parameter_copybit( (value == COPYBIT_ENABLE) ? ctx->isPremultipliedAlpha = true : ctx->isPremultipliedAlpha = false; break; + case COPYBIT_FRAMEBUFFER_WIDTH: + ctx->fb_width = value; + break; + case COPYBIT_FRAMEBUFFER_HEIGHT: + ctx->fb_height = value; + break; default: - LOGE("%s: default case", __func__); + LOGE("%s: default case param=0x%x", __FUNCTION__, name); return -EINVAL; break; } @@ -684,7 +802,7 @@ static int get(struct copybit_device_t *dev, int name) int value; if (!ctx) { - LOGE("%s: null context error", __func__); + LOGE("%s: null context error", __FUNCTION__); return -EINVAL; } @@ -702,7 +820,7 @@ static int get(struct copybit_device_t *dev, int name) value = 1; break; default: - LOGE("%s: default case", __func__); + LOGE("%s: default case param=0x%x", __FUNCTION__, name); value = -EINVAL; } return value; @@ -729,6 +847,171 @@ static int is_alpha(int cformat) return alpha; } +/* Function to check if we need a temporary buffer for the blit. + * This would happen if the requested destination stride and the + * C2D stride do not match. We ignore RGB buffers, since their + * stride is always aligned to 32. + */ +static bool need_temp_buffer(struct copybit_image_t const *img) +{ + if (COPYBIT_SUCCESS == is_supported_rgb_format(img->format)) + return false; + + struct private_handle_t* handle = (struct private_handle_t*)img->handle; + + // The width parameter in the handle contains the aligned_w. We check if we + // need to convert based on this param. YUV formats have bpp=1, so checking + // if the requested stride is aligned should suffice. + if (0 == (handle->width)%32) { + return false; + } + + return true; +} + +/* Function to extract the information from the copybit image and set the corresponding + * values in the bufferInfo struct. + */ +static void populate_buffer_info(struct copybit_image_t const *img, bufferInfo& info) +{ + info.width = img->w; + info.height = img->h; + info.format = img->format; +} + +/* Function to get the required size for a particular format, inorder for C2D to perform + * the blit operation. + */ +static size_t get_size(const bufferInfo& info) +{ + size_t size = 0; + int w = info.width; + int h = info.height; + int aligned_w = ALIGN(w, 32); + switch(info.format) { + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: + { + // Chroma for this format is aligned to 2K. + size = ALIGN((aligned_w*h), 2048) + + ALIGN(w/2, 32) * h/2 *2; + size = ALIGN(size, 4096); + } break; + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + { + size = aligned_w*h + + ALIGN(w/2, 32) * h/2 *2; + size = ALIGN(size, 4096); + } break; + default: break; + } + return size; +} + +/* Function to allocate memory for the temporary buffer. This memory is + * allocated from Ashmem. It is the caller's responsibility to free this + * memory. + */ +static int get_temp_buffer(const bufferInfo& info, memInfo& mem_info) +{ + // Alloc memory from ashmem + int err = COPYBIT_SUCCESS; + size_t size = get_size(info); + char name[ASHMEM_NAME_LEN]; + snprintf(name, ASHMEM_NAME_LEN, "c2d-buffer-%x",mem_info); + int prot = PROT_READ | PROT_WRITE; + int fd = ashmem_create_region(name, size); + void *base = 0; + if (fd < 0) { + LOGE("%s: couldn't create ashmem (%s)", __FUNCTION__, + strerror(errno)); + return COPYBIT_FAILURE; + } else { + if (ashmem_set_prot_region(fd, prot) < 0) { + LOGE("%s: ashmem_set_prot_region(fd=%d, prot=%x) failed (%s)", + __FUNCTION__, fd, prot, strerror(errno)); + close(fd); + fd = -1; + return COPYBIT_FAILURE; + } else { + base = mmap(0, size, prot, MAP_SHARED|MAP_POPULATE|MAP_LOCKED, fd, 0); + if (base == MAP_FAILED) { + LOGE("%s: alloc mmap(fd=%d, size=%d, prot=%x) failed (%s)", + __FUNCTION__, fd, size, prot, strerror(errno)); + close(fd); + fd = -1; + return COPYBIT_FAILURE; + } + } + } + if (ioctl(fd, ASHMEM_CACHE_INV_RANGE, NULL)) { + LOGE("ASHMEM_CACHE_INV_RANGE failed fd = %d", fd); + } + + // Save the memory info. + mem_info.fd = fd; + mem_info.size = size; + mem_info.base = (int)base; + return err; +} + +/* Function to free the temporary allocated memory.*/ +static void free_temp_image(private_handle_t *hnd) +{ + if (hnd) { + if (0 != hnd->base) { + munmap((void *)hnd->base, hnd->size); + hnd->base = 0; + } + + if (hnd->fd != -1) { + close(hnd->fd); + hnd->fd = -1; + } + } +} + +/* Function to perform the software color conversion. Convert the + * C2D compatible format to the Android compatible format + */ +static int copy_image(private_handle_t *src_handle, + struct copybit_image_t const *rhs, + eConversionType conversionType) +{ + if (src_handle->fd == -1) { + LOGE("%s: src_handle fd is invalid", __FUNCTION__); + return COPYBIT_FAILURE; + } + + // Copy the info. + int ret = COPYBIT_SUCCESS; + switch(rhs->format) { + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + { + if (CONVERT_TO_ANDROID_FORMAT == conversionType) { + return convert_yuv_c2d_to_yuv_android(src_handle, rhs); + } else { + return convert_yuv_android_to_yuv_c2d(src_handle, rhs); + } + + } break; + default: { + LOGE("%s: invalid format 0x%x", __FUNCTION__, rhs->format); + ret = COPYBIT_FAILURE; + } break; + } + return ret; +} + +static void delete_handle(private_handle_t *handle) +{ + if (handle) { + delete handle; + handle = 0; + } +} /** do a stretch blit type operation */ static int stretch_copybit_internal( struct copybit_device_t *dev, @@ -748,20 +1031,20 @@ static int stretch_copybit_internal( memset(&list, 0, sizeof(list)); int cformat; c2d_ts_handle timestamp; - uint32 surface_index = 0; + uint32 src_surface_index = 0, dst_surface_index = 0; if (!ctx) { - LOGE("%s: null context error", __func__); + LOGE("%s: null context error", __FUNCTION__); return -EINVAL; } if (src->w > MAX_DIMENSION || src->h > MAX_DIMENSION) { - LOGE("%s: src dimension error", __func__); + LOGE("%s: src dimension error", __FUNCTION__); return -EINVAL; } if (dst->w > MAX_DIMENSION || dst->h > MAX_DIMENSION) { - LOGE("%s : dst dimension error dst w %d h %d", __func__, dst->w, dst->h); + LOGE("%s : dst dimension error dst w %d h %d", __FUNCTION__, dst->w, dst->h); return -EINVAL; } @@ -770,24 +1053,163 @@ static int stretch_copybit_internal( struct copybit_rect_t clip; list.count = 0; - status = set_image(ctx->g12_device_fd, ctx->dst, dst, &cformat, &trg_mapped, ctx->isPremultipliedAlpha); + if (is_valid_destination_format(dst->format) == COPYBIT_FAILURE) { + LOGE("%s: Invalid destination format format = 0x%x", __FUNCTION__, dst->format); + return COPYBIT_FAILURE; + } + + bool isYUVDestination = false; + if (is_supported_rgb_format(dst->format) == COPYBIT_SUCCESS) { + dst_surface_index = RGB_SURFACE; + } else if (is_supported_yuv_format(dst->format) == COPYBIT_SUCCESS) { + isYUVDestination = true; + int num_planes = get_num_planes(dst->format); + if (num_planes == 2) { + dst_surface_index = YUV_SURFACE_2_PLANES; + } else if (num_planes == 3) { + dst_surface_index = YUV_SURFACE_3_PLANES; + } else { + LOGE("%s: dst number of YUV planes is invalid dst format = 0x%x", + __FUNCTION__, dst->format); + return COPYBIT_FAILURE; + } + } else { + LOGE("%s: Invalid dst surface format 0x%x", __FUNCTION__, dst->format); + return COPYBIT_FAILURE; + } + + copybit_image_t dst_image; + dst_image.w = dst->w; + dst_image.h = dst->h; + dst_image.format = dst->format; + dst_image.handle = dst->handle; + // Check if we need a temp. copy for the destination. We'd need this the destination + // width is not aligned to 32. This case occurs for YUV formats. RGB formats are + // aligned to 32. + bool needTempDestination = need_temp_buffer(dst); + memInfo mem_info; + bufferInfo dst_info; + populate_buffer_info(dst, dst_info); + private_handle_t* dst_hnd = new private_handle_t(-1, 0, 0, 0, dst_info.format, + dst_info.width, dst_info.height); + if (dst_hnd == NULL) { + LOGE("%s: dst_hnd is null", __FUNCTION__); + return COPYBIT_FAILURE; + } + if (needTempDestination) { + // Create a temp buffer and set that as the destination. + if (COPYBIT_SUCCESS == get_temp_buffer(dst_info, mem_info)) { + dst_hnd->fd = mem_info.fd; + dst_hnd->size = mem_info.size; + dst_hnd->flags = private_handle_t::PRIV_FLAGS_USES_ASHMEM; + dst_hnd->base = mem_info.base; + dst_hnd->offset = 0; + dst_hnd->gpuaddr = 0; + dst_image.handle = dst_hnd; + } + } + + int flags = 0; + flags |= (ctx->isPremultipliedAlpha) ? FLAGS_PREMULTIPLIED_ALPHA : 0; + flags |= (isYUVDestination) ? FLAGS_YUV_DESTINATION : 0; + + status = set_image(ctx->g12_device_fd, ctx->dst[dst_surface_index], &dst_image, &cformat, + &trg_mapped, (eC2DFlags)flags); if(status) { - LOGE("%s: set_image error", __func__); + LOGE("%s: dst: set_image error", __FUNCTION__); + if (needTempDestination) { + free_temp_image(dst_hnd); + } + delete_handle(dst_hnd); return COPYBIT_FAILURE; } if(is_supported_rgb_format(src->format) == COPYBIT_SUCCESS) { - surface_index = RGB_SURFACE; + src_surface_index = RGB_SURFACE; } else if (is_supported_yuv_format(src->format) == COPYBIT_SUCCESS) { - surface_index = YUV_SURFACE; + int num_planes = get_num_planes(src->format); + if (num_planes == 2) { + src_surface_index = YUV_SURFACE_2_PLANES; + } else if (num_planes == 3) { + src_surface_index = YUV_SURFACE_3_PLANES; + } else { + LOGE("%s: src number of YUV planes is invalid src format = 0x%x", + __FUNCTION__, src->format); + if (needTempDestination) { + free_temp_image(dst_hnd); + } + delete_handle(dst_hnd); + return -EINVAL; + } } else { - LOGE("%s: Invalid source surface format %x", __func__, src->format); + LOGE("%s: Invalid source surface format 0x%x", __FUNCTION__, src->format); + if (needTempDestination) { + free_temp_image(dst_hnd); + } + delete_handle(dst_hnd); return -EINVAL; } - status = set_image(ctx->g12_device_fd, ctx->src[surface_index], src, &cformat, &src_mapped, ctx->isPremultipliedAlpha); + copybit_image_t src_image; + src_image.w = src->w; + src_image.h = src->h; + src_image.format = src->format; + src_image.handle = src->handle; + + bool needTempSource = need_temp_buffer(src); + bufferInfo src_info; + populate_buffer_info(src, src_info); + private_handle_t* src_hnd = new private_handle_t(-1, 0, 0, 0, src_info.format, + src_info.width, src_info.height); + if (NULL == src_hnd) { + LOGE("%s: src_hnd is null", __FUNCTION__); + if (needTempDestination) { + free_temp_image(dst_hnd); + } + delete_handle(dst_hnd); + return COPYBIT_FAILURE; + } + if (needTempSource) { + // Create a temp buffer and set that as the destination. + if (COPYBIT_SUCCESS == get_temp_buffer(src_info, mem_info)) { + src_hnd->fd = mem_info.fd; + src_hnd->size = mem_info.size; + src_hnd->flags = private_handle_t::PRIV_FLAGS_USES_ASHMEM; + src_hnd->base = mem_info.base; + src_hnd->offset = 0; + src_hnd->gpuaddr = 0; + src_image.handle = src_hnd; + + // Copy the source. + copy_image((private_handle_t *)src->handle, &src_image, CONVERT_TO_C2D_FORMAT); + + // Flush the cache + if (ioctl(src_hnd->fd, ASHMEM_CACHE_FLUSH_RANGE, NULL)) { + LOGE("%s: ASHMEM_CACHE_FLUSH_RANGE failed (error=%s)", + __FUNCTION__, strerror(errno)); + if (needTempDestination) { + free_temp_image(dst_hnd); + } + free_temp_image(src_hnd); + delete_handle(dst_hnd); + delete_handle(src_hnd); + return COPYBIT_FAILURE; + } + } + } + + status = set_image(ctx->g12_device_fd, ctx->src[src_surface_index], &src_image, &cformat, + &src_mapped, (eC2DFlags)flags); if(status) { - LOGE("%s: set_src_image error", __func__); + LOGE("%s: set_src_image error", __FUNCTION__); + if (needTempDestination) { + free_temp_image(dst_hnd); + } + if (needTempSource) { + free_temp_image(src_hnd); + } + delete_handle(dst_hnd); + delete_handle(src_hnd); return COPYBIT_FAILURE; } @@ -796,8 +1218,16 @@ static int stretch_copybit_internal( ctx->blitState.config_mask &= ~C2D_ALPHA_BLEND_NONE; if(!(ctx->blitState.global_alpha)) { // src alpha is zero - unset_image(ctx->g12_device_fd, ctx->src[surface_index], src, src_mapped); - unset_image(ctx->g12_device_fd, ctx->dst, dst, trg_mapped); + unset_image(ctx->g12_device_fd, ctx->src[src_surface_index], &src_image, src_mapped); + unset_image(ctx->g12_device_fd, ctx->dst[dst_surface_index], &dst_image, trg_mapped); + if (needTempDestination) { + free_temp_image(dst_hnd); + } + if (needTempSource) { + free_temp_image(src_hnd); + } + delete_handle(dst_hnd); + delete_handle(src_hnd); return status; } } else { @@ -810,7 +1240,7 @@ static int stretch_copybit_internal( ctx->blitState.config_mask |= C2D_ALPHA_BLEND_NONE; } - ctx->blitState.surface_id = ctx->src[surface_index]; + ctx->blitState.surface_id = ctx->src[src_surface_index]; while ((status == 0) && region->next(region, &clip)) { req = &(list.blitObjects[list.count]); @@ -819,22 +1249,35 @@ static int stretch_copybit_internal( set_rects(ctx, req, dst_rect, src_rect, &clip); if (++list.count == maxCount) { - status = msm_copybit(ctx, &list, ctx->dst); + status = msm_copybit(ctx, &list, ctx->dst[dst_surface_index]); list.count = 0; } } if ((status == 0) && list.count) { - status = msm_copybit(ctx, &list, ctx->dst); + status = msm_copybit(ctx, &list, ctx->dst[dst_surface_index]); } - if(LINK_c2dFinish(ctx->dst)) { - LOGE("%s: LINK_c2dFinish ERROR", __func__); + if(LINK_c2dFinish(ctx->dst[dst_surface_index])) { + LOGE("%s: LINK_c2dFinish ERROR", __FUNCTION__); } - - unset_image(ctx->g12_device_fd, ctx->src[surface_index], src, src_mapped); - unset_image(ctx->g12_device_fd, ctx->dst, dst, trg_mapped); + unset_image(ctx->g12_device_fd, ctx->src[src_surface_index], &src_image, src_mapped); + unset_image(ctx->g12_device_fd, ctx->dst[dst_surface_index], &dst_image, trg_mapped); + if (needTempDestination) { + // copy the temp. destination without the alignment to the actual destination. + copy_image(dst_hnd, dst, CONVERT_TO_ANDROID_FORMAT); + // Free the temp memory. + free_temp_image(dst_hnd); + } + if (needTempSource) { + // Free the temp memory. + free_temp_image(src_hnd); + } + delete_handle(dst_hnd); + delete_handle(src_hnd); ctx->isPremultipliedAlpha = false; + ctx->fb_width = 0; + ctx->fb_height = 0; return status; } @@ -868,8 +1311,8 @@ static int close_copybit(struct hw_device_t *dev) { struct copybit_context_t* ctx = (struct copybit_context_t*)dev; if (ctx) { - LINK_c2dDestroySurface(ctx->dst); - for(int i = 0; i dst[i]); LINK_c2dDestroySurface(ctx->src[i]); } @@ -894,24 +1337,22 @@ static int open_copybit(const struct hw_module_t* module, const char* name, C2D_RGB_SURFACE_DEF surfDefinition = {0}; C2D_YUV_SURFACE_DEF yuvSurfaceDef = {0} ; struct copybit_context_t *ctx; - char const * const device_template[] = { - "/dev/graphics/fb%u", - "/dev/fb%u", - 0 }; - - int fd = -1; - int i=0; char fbName[64]; ctx = (struct copybit_context_t *)malloc(sizeof(struct copybit_context_t)); if(!ctx) { - LOGE("%s: malloc failed", __func__); + LOGE("%s: malloc failed", __FUNCTION__); return COPYBIT_FAILURE; } /* initialize drawstate */ memset(ctx, 0, sizeof(*ctx)); + for (int i=0; i< NUM_SURFACES; i++) { + ctx->dst[i] = -1; + ctx->src[i] = -1; + } + ctx->libc2d2 = ::dlopen("libC2D2.so", RTLD_NOW); if (!ctx->libc2d2) { LOGE("FATAL ERROR: could not dlopen libc2d2.so: %s", dlerror()); @@ -931,11 +1372,11 @@ static int open_copybit(const struct hw_module_t* module, const char* name, *(void **)&LINK_c2dDestroySurface = ::dlsym(ctx->libc2d2, "c2dDestroySurface"); - if(!LINK_c2dCreateSurface || !LINK_c2dUpdateSurface || !LINK_c2dReadSurface + if (!LINK_c2dCreateSurface || !LINK_c2dUpdateSurface || !LINK_c2dReadSurface || !LINK_c2dDraw || !LINK_c2dFlush || !LINK_c2dWaitTimestamp || !LINK_c2dFinish || !LINK_c2dDestroySurface) { - LOGE("%s: dlsym ERROR", __func__); - goto error1; + LOGE("%s: dlsym ERROR", __FUNCTION__); + goto error; } ctx->device.common.tag = HARDWARE_DEVICE_TAG; @@ -949,9 +1390,9 @@ static int open_copybit(const struct hw_module_t* module, const char* name, ctx->blitState.config_mask = C2D_NO_BILINEAR_BIT | C2D_NO_ANTIALIASING_BIT; ctx->trg_transform = C2D_TARGET_ROTATE_0; ctx->g12_device_fd = open(G12_DEVICE_NAME, O_RDWR | O_SYNC); - if(ctx->g12_device_fd < 0) { - LOGE("%s: g12_device_fd open failed", __func__); - goto error1; + if (ctx->g12_device_fd < 0) { + LOGE("%s: g12_device_fd open failed", __FUNCTION__); + goto error; } /* Create RGB Surface */ @@ -961,19 +1402,21 @@ static int open_copybit(const struct hw_module_t* module, const char* name, surfDefinition.width = 1; surfDefinition.height = 1; surfDefinition.format = C2D_COLOR_FORMAT_8888_ARGB; - - if(LINK_c2dCreateSurface(&ctx->dst,C2D_TARGET | C2D_SOURCE, + if (LINK_c2dCreateSurface(&(ctx->dst[RGB_SURFACE]), C2D_TARGET | C2D_SOURCE, (C2D_SURFACE_TYPE)(C2D_SURFACE_RGB_HOST | C2D_SURFACE_WITH_PHYS), &surfDefinition)) { - LOGE("%s: create ctx->dst failed", __func__); - goto error2; + LOGE("%s: create ctx->dst[RGB_SURFACE] failed", __FUNCTION__); + ctx->dst[RGB_SURFACE] = -1; + goto error; } - if(LINK_c2dCreateSurface(&(ctx->src[RGB_SURFACE]), C2D_TARGET | C2D_SOURCE, + + if (LINK_c2dCreateSurface(&(ctx->src[RGB_SURFACE]), C2D_TARGET | C2D_SOURCE, (C2D_SURFACE_TYPE)(C2D_SURFACE_RGB_HOST | C2D_SURFACE_WITH_PHYS), &surfDefinition)) { - LOGE("%s: create ctx->src[RGB_SURFACE] failed", __func__); - goto error3; + LOGE("%s: create ctx->src[RGB_SURFACE] failed", __FUNCTION__); + ctx->src[RGB_SURFACE] = -1; + goto error; } /* Create YUV source surface */ @@ -989,48 +1432,67 @@ static int open_copybit(const struct hw_module_t* module, const char* name, yuvSurfaceDef.phys1 = (void*) 0xaaaaaaaa; yuvSurfaceDef.stride1 = 4; - if(LINK_c2dCreateSurface(&(ctx->src[YUV_SURFACE]),C2D_TARGET | C2D_SOURCE, + if (LINK_c2dCreateSurface(&(ctx->src[YUV_SURFACE_2_PLANES]),C2D_TARGET | C2D_SOURCE, (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS), &yuvSurfaceDef)) { - LOGE("%s: create ctx->src[YUV_SURFACE] failed", __func__); - goto error4; + LOGE("%s: create ctx->src[YUV_SURFACE_2_PLANES] failed", __FUNCTION__); + ctx->src[YUV_SURFACE_2_PLANES] = -1; + goto error; } - *device = &ctx->device.common; - - while ((fd==-1) && device_template[i]) { - snprintf(fbName, 64, device_template[i], 0); - fd = open(fbName, O_RDWR, 0); - i++; + if (LINK_c2dCreateSurface(&(ctx->dst[YUV_SURFACE_2_PLANES]),C2D_TARGET | C2D_SOURCE, + (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS), + &yuvSurfaceDef)) { + LOGE("%s: create ctx->dst[YUV_SURFACE_2_PLANES] failed", __FUNCTION__); + ctx->dst[YUV_SURFACE_2_PLANES] = -1; + goto error; } - if (fd < 0) - goto error5; - struct fb_var_screeninfo info; - if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) - goto error6; + yuvSurfaceDef.format = C2D_COLOR_FORMAT_420_YV12; + yuvSurfaceDef.plane2 = (void*)0xaaaaaaaa; + yuvSurfaceDef.phys2 = (void*) 0xaaaaaaaa; + yuvSurfaceDef.stride2 = 4; - ctx->fb_width = info.xres; - ctx->fb_height = info.yres; - close(fd); + if (LINK_c2dCreateSurface(&(ctx->src[YUV_SURFACE_3_PLANES]),C2D_TARGET | C2D_SOURCE, + (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS), + &yuvSurfaceDef)) { + LOGE("%s: create ctx->src[YUV_SURFACE_3_PLANES] failed", __FUNCTION__); + ctx->src[YUV_SURFACE_3_PLANES] = -1; + goto error; + } + + if (LINK_c2dCreateSurface(&(ctx->dst[YUV_SURFACE_3_PLANES]),C2D_TARGET | C2D_SOURCE, + (C2D_SURFACE_TYPE)(C2D_SURFACE_YUV_HOST | C2D_SURFACE_WITH_PHYS), + &yuvSurfaceDef)) { + LOGE("%s: create ctx->dst[YUV_SURFACE_3_PLANES] failed", __FUNCTION__); + ctx->dst[YUV_SURFACE_3_PLANES] = -1; + goto error; + } + + ctx->fb_width = 0; + ctx->fb_height = 0; ctx->isPremultipliedAlpha = false; + *device = &ctx->device.common; return status; -error6: - close(fd); - fd = -1; -error5: - LINK_c2dDestroySurface(ctx->src[YUV_SURFACE]); -error4: - LINK_c2dDestroySurface(ctx->src[RGB_SURFACE]); -error3: - LINK_c2dDestroySurface(ctx->dst); -error2: - close(ctx->g12_device_fd); -error1: - ::dlclose(ctx->libc2d2); error: - free(ctx); + for (int i = 0; isrc[i])) { + LINK_c2dDestroySurface(ctx->src[i]); + ctx->src[i] = -1; + } + if (-1 != (ctx->dst[i])) { + LINK_c2dDestroySurface(ctx->dst[i]); + ctx->dst[i] = -1; + } + } + if (ctx->g12_device_fd >= 0) { + close(ctx->g12_device_fd); + } + if (ctx->libc2d2) + ::dlclose(ctx->libc2d2); + if (ctx) + free(ctx); status = COPYBIT_FAILURE; *device = NULL; diff --git a/libcopybit/copybit_priv.h b/libcopybit/copybit_priv.h new file mode 100644 index 0000000..7fdd508 --- /dev/null +++ b/libcopybit/copybit_priv.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +struct copybit_iterator : public copybit_region_t { + copybit_iterator(const copybit_rect_t& rect) { + mRect = rect; + mCount = 1; + this->next = iterate; + } +private: + static int iterate(copybit_region_t const * self, copybit_rect_t* rect) { + if (!self || !rect) { + return 0; + } + + copybit_iterator const* me = static_cast(self); + if (me->mCount) { + rect->l = me->mRect.l; + rect->t = me->mRect.t; + rect->r = me->mRect.r; + rect->b = me->mRect.b; + me->mCount--; + return 1; + } + return 0; + } + copybit_rect_t mRect; + mutable int mCount; +}; + diff --git a/libcopybit/software_converter.cpp b/libcopybit/software_converter.cpp index 73c2c88..10e9db2 100644 --- a/libcopybit/software_converter.cpp +++ b/libcopybit/software_converter.cpp @@ -32,7 +32,6 @@ #include #include #include "software_converter.h" -#include "gralloc_priv.h" /** Convert YV12 to YCrCb_420_SP */ int convertYV12toYCrCb420SP(const copybit_image_t *src) @@ -166,3 +165,141 @@ int convertYV12toYCrCb420SP(const copybit_image_t *src) free(tempBuf); return 0; } + +struct copyInfo{ + int width; + int height; + int src_stride; + int dst_stride; + int src_plane1_offset; + int src_plane2_offset; + int dst_plane1_offset; + int dst_plane2_offset; +}; + +/* Internal function to do the actual copy of source to destination */ +static int copy_source_to_destination(const int src_base, const int dst_base, + copyInfo& info) +{ + if (!src_base || !dst_base) { + LOGE("%s: invalid memory src_base = 0x%x dst_base=0x%x", + __FUNCTION__, src_base, dst_base); + return COPYBIT_FAILURE; + } + + int width = info.width; + int height = info.height; + unsigned char *src = (unsigned char*)src_base; + unsigned char *dst = (unsigned char*)dst_base; + + // Copy the luma + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + src += info.src_stride; + dst += info.dst_stride; + } + + // Copy plane 1 + src = (unsigned char*)(src_base + info.src_plane1_offset); + dst = (unsigned char*)(dst_base + info.dst_plane1_offset); + width = width/2; + height = height/2; + for (int i = 0; i < height; i++) { + memcpy(dst, src, info.src_stride); + src += info.src_stride; + dst += info.dst_stride; + } + return 0; +} + + +/* + * Function to convert the c2d format into an equivalent Android format + * + * @param: source buffer handle + * @param: destination image + * + * @return: return status + */ +int convert_yuv_c2d_to_yuv_android(private_handle_t *hnd, + struct copybit_image_t const *rhs) +{ + LOGD("Enter %s", __FUNCTION__); + if (!hnd || !rhs) { + LOGE("%s: invalid inputs hnd=%p rhs=%p", __FUNCTION__, hnd, rhs); + return COPYBIT_FAILURE; + } + + int ret = COPYBIT_SUCCESS; + private_handle_t *dst_hnd = (private_handle_t *)rhs->handle; + + copyInfo info; + info.width = rhs->w; + info.height = rhs->h; + info.src_stride = ALIGN(info.width, 32); + info.dst_stride = ALIGN(info.width, 16); + switch(rhs->format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: { + info.src_plane1_offset = info.src_stride*info.height; + info.dst_plane1_offset = info.dst_stride*info.height; + } break; + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: { + // Chroma is 2K aligned for the NV12 encodeable format. + info.src_plane1_offset = ALIGN(info.src_stride*info.height, 2048); + info.dst_plane1_offset = ALIGN(info.dst_stride*info.height, 2048); + } break; + default: + LOGE("%s: unsupported format (format=0x%x)", __FUNCTION__, + rhs->format); + return COPYBIT_FAILURE; + } + + ret = copy_source_to_destination(hnd->base, dst_hnd->base, info); + return ret; +} + +/* + * Function to convert the Android format into an equivalent C2D format + * + * @param: source buffer handle + * @param: destination image + * + * @return: return status + */ +int convert_yuv_android_to_yuv_c2d(private_handle_t *hnd, + struct copybit_image_t const *rhs) +{ + if (!hnd || !rhs) { + LOGE("%s: invalid inputs hnd=%p rhs=%p", __FUNCTION__, hnd, rhs); + return COPYBIT_FAILURE; + } + + int ret = COPYBIT_SUCCESS; + private_handle_t *dst_hnd = (private_handle_t *)rhs->handle; + + copyInfo info; + info.width = rhs->w; + info.height = rhs->h; + info.src_stride = ALIGN(info.width, 16); + info.dst_stride = ALIGN(info.width, 32); + switch(rhs->format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: { + info.src_plane1_offset = info.src_stride*info.height; + info.dst_plane1_offset = info.dst_stride*info.height; + } break; + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: { + // Chroma is 2K aligned for the NV12 encodeable format. + info.src_plane1_offset = ALIGN(info.src_stride*info.height, 2048); + info.dst_plane1_offset = ALIGN(info.dst_stride*info.height, 2048); + } break; + default: + LOGE("%s: unsupported format (format=0x%x)", __FUNCTION__, + rhs->format); + return -1; + } + + ret = copy_source_to_destination(hnd->base, dst_hnd->base, info); + return ret; +} \ No newline at end of file diff --git a/libcopybit/software_converter.h b/libcopybit/software_converter.h index 345c7c0..8f98a93 100644 --- a/libcopybit/software_converter.h +++ b/libcopybit/software_converter.h @@ -30,9 +30,36 @@ #include +#include "gralloc_priv.h" + +#define COPYBIT_SUCCESS 0 +#define COPYBIT_FAILURE -1 inline unsigned int ALIGN(unsigned int x, unsigned int align) { return (x + align-1) & ~(align-1); } int convertYV12toYCrCb420SP(const copybit_image_t *src); + +/* + * Function to convert the c2d format into an equivalent Android format + * + * @param: source buffer handle + * @param: destination image + * + * @return: return status + */ +int convert_yuv_c2d_to_yuv_android(private_handle_t *hnd, + struct copybit_image_t const *rhs); + + +/* + * Function to convert the Android format into an equivalent C2D format + * + * @param: source buffer handle + * @param: destination image + * + * @return: return status + */ +int convert_yuv_android_to_yuv_c2d(private_handle_t *hnd, + struct copybit_image_t const *rhs); diff --git a/libgenlock/Android.mk b/libgenlock/Android.mk new file mode 100644 index 0000000..d8a3dfe --- /dev/null +++ b/libgenlock/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) +LOCAL_SHARED_LIBRARIES := liblog libcutils +LOCAL_C_INCLUDES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include +LOCAL_C_INCLUDES += hardware/qcom/display/libgralloc +LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +LOCAL_SRC_FILES := genlock.cpp +LOCAL_CFLAGS:= -DLOG_TAG=\"libgenlock\" +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libgenlock +include $(BUILD_SHARED_LIBRARY) + diff --git a/libgenlock/genlock.cpp b/libgenlock/genlock.cpp new file mode 100644 index 0000000..37bcc0e --- /dev/null +++ b/libgenlock/genlock.cpp @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "genlock.h" + +#define GENLOCK_DEVICE "/dev/genlock" + +#ifndef USE_GENLOCK +#define USE_GENLOCK +#endif + +namespace { + /* Internal function to map the userspace locks to the kernel lock types */ + int get_kernel_lock_type(genlock_lock_type lockType) + { + int kLockType = 0; + // If the user sets both a read and write lock, higher preference is + // given to the write lock. + if (lockType & GENLOCK_WRITE_LOCK) { + kLockType = GENLOCK_WRLOCK; + } else if (lockType & GENLOCK_READ_LOCK) { + kLockType = GENLOCK_RDLOCK; + } else { + LOGE("%s: invalid lockType (lockType = %d)", __FUNCTION__, lockType); + return -1; + } + return kLockType; + } + + /* Internal function to perform the actual lock/unlock operations */ + genlock_status_t perform_lock_unlock_operation(native_handle_t *buffer_handle, + int lockType, int timeout) + { + if (private_handle_t::validate(buffer_handle)) { + LOGE("%s: handle is invalid", __FUNCTION__); + return GENLOCK_FAILURE; + } + + private_handle_t *hnd = reinterpret_cast(buffer_handle); + if (hnd->genlockPrivFd < 0) { + LOGE("%s: the lock has not been created, or has not been attached", + __FUNCTION__); + return GENLOCK_FAILURE; + } + + genlock_lock lock; + lock.op = lockType; + lock.flags = 0; + lock.timeout = timeout; + lock.fd = hnd->genlockHandle; + + if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_LOCK, &lock)) { + LOGE("%s: GENLOCK_IOC_LOCK failed (lockType0x%x, err=%s fd=%d)", __FUNCTION__, + lockType, strerror(errno), hnd->fd); + if (ETIMEDOUT == errno) + return GENLOCK_TIMEDOUT; + + return GENLOCK_FAILURE; + } + return GENLOCK_NO_ERROR; + } + + /* Internal function to close the fd and release the handle */ + void close_genlock_fd_and_handle(int& fd, int& handle) + { + if (fd >=0 ) { + close(fd); + fd = -1; + } + + if (handle >= 0) { + close(handle); + handle = -1; + } + } + +} +/* + * Create a genlock lock. The genlock lock file descriptor and the lock + * handle are stored in the buffer_handle. + * + * @param: handle of the buffer + * @return error status. + */ +genlock_status_t genlock_create_lock(native_handle_t *buffer_handle) +{ + genlock_status_t ret = GENLOCK_NO_ERROR; + if (private_handle_t::validate(buffer_handle)) { + LOGE("%s: handle is invalid", __FUNCTION__); + return GENLOCK_FAILURE; + } + + private_handle_t *hnd = reinterpret_cast(buffer_handle); +#ifdef USE_GENLOCK + // Open the genlock device + int fd = open(GENLOCK_DEVICE, O_RDWR); + if (fd < 0) { + LOGE("%s: open genlock device failed (err=%s)", __FUNCTION__, + strerror(errno)); + return GENLOCK_FAILURE; + } + + // Create a new lock + genlock_lock lock; + if (ioctl(fd, GENLOCK_IOC_NEW, NULL)) { + LOGE("%s: GENLOCK_IOC_NEW failed (error=%s)", __FUNCTION__, + strerror(errno)); + close_genlock_fd_and_handle(fd, lock.fd); + ret = GENLOCK_FAILURE; + } + + // Export the lock for other processes to be able to use it. + if (GENLOCK_FAILURE != ret) { + if (ioctl(fd, GENLOCK_IOC_EXPORT, &lock)) { + LOGE("%s: GENLOCK_IOC_EXPORT failed (error=%s)", __FUNCTION__, + strerror(errno)); + close_genlock_fd_and_handle(fd, lock.fd); + ret = GENLOCK_FAILURE; + } + } + + // Store the lock params in the handle. + hnd->genlockPrivFd = fd; + hnd->genlockHandle = lock.fd; +#else + hnd->genlockHandle = 0; +#endif + return ret; +} + + +/* + * Release a genlock lock associated with the handle. + * + * @param: handle of the buffer + * @return error status. + */ +genlock_status_t genlock_release_lock(native_handle_t *buffer_handle) +{ + genlock_status_t ret = GENLOCK_NO_ERROR; +#ifdef USE_GENLOCK + if (private_handle_t::validate(buffer_handle)) { + LOGE("%s: handle is invalid", __FUNCTION__); + return GENLOCK_FAILURE; + } + + private_handle_t *hnd = reinterpret_cast(buffer_handle); + if (hnd->genlockPrivFd < 0) { + LOGE("%s: the lock is invalid", __FUNCTION__); + return GENLOCK_FAILURE; + } + + if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_RELEASE, NULL)) { + LOGE("%s: GENLOCK_IOC_RELEASE failed (err=%s)", __FUNCTION__, + strerror(errno)); + ret = GENLOCK_FAILURE; + } + + // Close the fd and reset the parameters. + close_genlock_fd_and_handle(hnd->genlockPrivFd, hnd->genlockHandle); +#endif + return ret; +} + + +/* + * Attach a lock to the buffer handle passed via an IPC. + * + * @param: handle of the buffer + * @return error status. + */ +genlock_status_t genlock_attach_lock(native_handle_t *buffer_handle) +{ + genlock_status_t ret = GENLOCK_NO_ERROR; +#ifdef USE_GENLOCK + if (private_handle_t::validate(buffer_handle)) { + LOGE("%s: handle is invalid", __FUNCTION__); + return GENLOCK_FAILURE; + } + + // Open the genlock device + int fd = open(GENLOCK_DEVICE, O_RDWR); + if (fd < 0) { + LOGE("%s: open genlock device failed (err=%s)", __FUNCTION__, + strerror(errno)); + return GENLOCK_FAILURE; + } + + // Attach the local handle to an existing lock + private_handle_t *hnd = reinterpret_cast(buffer_handle); + genlock_lock lock; + lock.fd = hnd->genlockHandle; + if (ioctl(fd, GENLOCK_IOC_ATTACH, &lock)) { + LOGE("%s: GENLOCK_IOC_ATTACH failed (err=%s)", __FUNCTION__, + strerror(errno)); + close_genlock_fd_and_handle(fd, lock.fd); + ret = GENLOCK_FAILURE; + } + + // Store the relavant information in the handle + hnd->genlockPrivFd = fd; +#endif + return ret; +} + +/* + * Lock the buffer specified by the buffer handle. The lock held by the buffer + * is specified by the lockType. This function will block if a write lock is + * requested on the buffer which has previously been locked for a read or write + * operation. A buffer can be locked by multiple clients for read. An optional + * timeout value can be specified. By default, there is no timeout. + * + * @param: handle of the buffer + * @param: type of lock to be acquired by the buffer. + * @param: timeout value in ms. GENLOCK_MAX_TIMEOUT is the maximum timeout value. + * @return error status. + */ +genlock_status_t genlock_lock_buffer(native_handle_t *buffer_handle, + genlock_lock_type_t lockType, + int timeout) +{ + genlock_status_t ret = GENLOCK_NO_ERROR; +#ifdef USE_GENLOCK + // Translate the locktype + int kLockType = get_kernel_lock_type(lockType); + if (-1 == kLockType) { + LOGE("%s: invalid lockType", __FUNCTION__); + return GENLOCK_FAILURE; + } + + if (0 == timeout) { + LOGW("%s: trying to lock a buffer with timeout = 0", __FUNCTION__); + } + // Call the private function to perform the lock operation specified. + ret = perform_lock_unlock_operation(buffer_handle, kLockType, timeout); +#endif + return ret; +} + + +/* + * Unlocks a buffer that has previously been locked by the client. + * + * @param: handle of the buffer to be unlocked. + * @return: error status. +*/ +genlock_status_t genlock_unlock_buffer(native_handle_t *buffer_handle) +{ + genlock_status_t ret = GENLOCK_NO_ERROR; +#ifdef USE_GENLOCK + // Do the unlock operation by setting the unlock flag. Timeout is always + // 0 in this case. + ret = perform_lock_unlock_operation(buffer_handle, GENLOCK_UNLOCK, 0); +#endif + return ret; +} + +/* + * Blocks the calling process until the lock held on the handle is unlocked. + * + * @param: handle of the buffer + * @param: timeout value for the wait. + * return: error status. + */ +genlock_status_t genlock_wait(native_handle_t *buffer_handle, int timeout) { +#ifdef USE_GENLOCK + if (private_handle_t::validate(buffer_handle)) { + LOGE("%s: handle is invalid", __FUNCTION__); + return GENLOCK_FAILURE; + } + + private_handle_t *hnd = reinterpret_cast(buffer_handle); + if (hnd->genlockPrivFd < 0) { + LOGE("%s: the lock is invalid", __FUNCTION__); + return GENLOCK_FAILURE; + } + + if (0 == timeout) + LOGW("%s: timeout = 0", __FUNCTION__); + + genlock_lock lock; + lock.fd = hnd->genlockHandle; + lock.timeout = timeout; + if (ioctl(hnd->genlockPrivFd, GENLOCK_IOC_WAIT, &lock)) { + LOGE("%s: GENLOCK_IOC_WAIT failed (err=%s)", __FUNCTION__, strerror(errno)); + return GENLOCK_FAILURE; + } +#endif + return GENLOCK_NO_ERROR; +} diff --git a/libgenlock/genlock.h b/libgenlock/genlock.h new file mode 100644 index 0000000..b394410 --- /dev/null +++ b/libgenlock/genlock.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef INCLUDE_LIBGENLOCK +#define INCLUDE_LIBGENLOCK + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Genlock lock types */ +typedef enum genlock_lock_type{ + GENLOCK_READ_LOCK = 1<<0, // Read lock + GENLOCK_WRITE_LOCK = 1<<1, // Write lock +}genlock_lock_type_t; + +/* Genlock return values */ +typedef enum genlock_status{ + GENLOCK_NO_ERROR = 0, + GENLOCK_TIMEDOUT, + GENLOCK_FAILURE, +} genlock_status_t; + +/* Genlock defines */ +#define GENLOCK_MAX_TIMEOUT 1000 // Max 1s timeout + +/* + * Create a genlock lock. The genlock lock file descriptor and the lock + * handle are stored in the buffer_handle. + * + * @param: handle of the buffer + * @return error status. + */ +genlock_status_t genlock_create_lock(native_handle_t *buffer_handle); + + +/* + * Release a genlock lock associated with the handle. + * + * @param: handle of the buffer + * @return error status. + */ +genlock_status_t genlock_release_lock(native_handle_t *buffer_handle); + +/* + * Attach a lock to the buffer handle passed via an IPC. + * + * @param: handle of the buffer + * @return error status. + */ +genlock_status_t genlock_attach_lock(native_handle_t *buffer_handle); + +/* + * Lock the buffer specified by the buffer handle. The lock held by the buffer + * is specified by the lockType. This function will block if a write lock is + * requested on the buffer which has previously been locked for a read or write + * operation. A buffer can be locked by multiple clients for read. An optional + * timeout value can be specified. By default, there is no timeout. + * + * @param: handle of the buffer + * @param: type of lock to be acquired by the buffer. + * @param: timeout value in ms. GENLOCK_MAX_TIMEOUT is the maximum timeout value. + * @return error status. + */ +genlock_status_t genlock_lock_buffer(native_handle_t *buffer_handle, + genlock_lock_type_t lockType, + int timeout); + +/* + * Unlocks a buffer that has previously been locked by the client. + * + * @param: handle of the buffer to be unlocked. + * @return: error status. +*/ +genlock_status_t genlock_unlock_buffer(native_handle_t *buffer_handle); + +/* + * Blocks the calling process until the lock held on the handle is unlocked. + * + * @param: handle of the buffer + * @param: timeout value for the wait. + * return: error status. + */ +genlock_status_t genlock_wait(native_handle_t *buffer_handle, int timeout); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libgralloc/Android.mk b/libgralloc/Android.mk old mode 100644 new mode 100755 index 77ae6ce..7820499 --- a/libgralloc/Android.mk +++ b/libgralloc/Android.mk @@ -21,7 +21,8 @@ include $(CLEAR_VARS) LOCAL_PRELINK_MODULE := false LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_SHARED_LIBRARIES := liblog libcutils libGLESv1_CM libutils libmemalloc - +LOCAL_SHARED_LIBRARIES += libgenlock +LOCAL_C_INCLUDES += hardware/qcom/display/libgenlock LOCAL_SRC_FILES := framebuffer.cpp \ gpu.cpp \ gralloc.cpp \ @@ -41,8 +42,8 @@ ifeq ($(TARGET_HAVE_HDMI_OUT),true) LOCAL_SHARED_LIBRARIES += liboverlay endif -ifeq ($(TARGET_USES_SF_BYPASS),true) - LOCAL_CFLAGS += -DSF_BYPASS +ifeq ($(TARGET_HAVE_BYPASS),true) + LOCAL_CFLAGS += -DCOMPOSITION_BYPASS endif ifeq ($(TARGET_GRALLOC_USES_ASHMEM),true) diff --git a/libgralloc/alloc_controller.cpp b/libgralloc/alloc_controller.cpp index 8f3b183..b65db7b 100644 --- a/libgralloc/alloc_controller.cpp +++ b/libgralloc/alloc_controller.cpp @@ -89,6 +89,7 @@ int IonController::allocate(alloc_data& data, int usage, { int ionFlags = 0; int ret; + bool noncontig = false; //System heap cannot be uncached if (usage & GRALLOC_USAGE_PRIVATE_UNCACHED && @@ -106,8 +107,10 @@ int IonController::allocate(alloc_data& data, int usage, if(usage & GRALLOC_USAGE_PRIVATE_EBI_HEAP) ionFlags |= 1 << ION_HEAP_EBI_ID; - if(usage & GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP) + if(usage & GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP) { ionFlags |= 1 << ION_HEAP_SYSTEM_ID; + noncontig = true; + } // if no flags are set, default to // EBI heap, so that bypass can work @@ -125,11 +128,15 @@ int IonController::allocate(alloc_data& data, int usage, { LOGW("Falling back to system heap"); data.flags = 1 << ION_HEAP_SYSTEM_ID; + noncontig = true; ret = mIonAlloc->alloc_buffer(data); } - if(ret >= 0 ) + if(ret >= 0 ) { data.allocType = private_handle_t::PRIV_FLAGS_USES_ION; + if(noncontig) + data.allocType |= private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM; + } return ret; @@ -166,6 +173,8 @@ int PmemKernelController::allocate(alloc_data& data, int usage, { int ret = 0; bool adspFallback = false; + if (!(usage & GRALLOC_USAGE_PRIVATE_SMI_HEAP)) + adspFallback = true; // Try SMI first if ((usage & GRALLOC_USAGE_PRIVATE_SMI_HEAP) || @@ -181,8 +190,8 @@ int PmemKernelController::allocate(alloc_data& data, int usage, if(ret >= 0) return ret; else { - adspFallback = true; - LOGW("Allocation from SMI failed, trying ADSP"); + if(adspFallback) + LOGW("Allocation from SMI failed, trying ADSP"); } } } @@ -252,8 +261,10 @@ int PmemAshmemController::allocate(alloc_data& data, int usage, if(usage & GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP) { ret = mAshmemAlloc->alloc_buffer(data); - if(ret >= 0) + if(ret >= 0) { data.allocType = private_handle_t::PRIV_FLAGS_USES_ASHMEM; + data.allocType |= private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM; + } return ret; } @@ -269,8 +280,10 @@ int PmemAshmemController::allocate(alloc_data& data, int usage, } else if(ret < 0 && canFallback(compositionType, usage, false)) { LOGW("Falling back to ashmem"); ret = mAshmemAlloc->alloc_buffer(data); - if(ret >= 0) + if(ret >= 0) { data.allocType = private_handle_t::PRIV_FLAGS_USES_ASHMEM; + data.allocType |= private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM; + } } return ret; diff --git a/libgralloc/ashmemalloc.cpp b/libgralloc/ashmemalloc.cpp index 49926f8..7102090 100644 --- a/libgralloc/ashmemalloc.cpp +++ b/libgralloc/ashmemalloc.cpp @@ -103,7 +103,7 @@ int AshmemAlloc::map_buffer(void **pBase, size_t size, int offset, int fd) return err; base = mmap(0, size, PROT_READ| PROT_WRITE, - MAP_SHARED, fd, 0); + MAP_SHARED|MAP_POPULATE, fd, 0); *pBase = base; if(base == MAP_FAILED) { LOGD("%s: Failed to map memory in the client: %s", diff --git a/libgralloc/framebuffer.cpp b/libgralloc/framebuffer.cpp index 19ee4d1..885c614 100644 --- a/libgralloc/framebuffer.cpp +++ b/libgralloc/framebuffer.cpp @@ -319,7 +319,7 @@ static void *disp_loop(void *ptr) pthread_mutex_lock(&(m->qlock)); // wait (sleep) while display queue is empty; - if (m->disp.isEmpty()) { + while (m->disp.isEmpty()) { pthread_cond_wait(&(m->qpost),&(m->qlock)); } @@ -347,6 +347,14 @@ static void *disp_loop(void *ptr) LOGE("ERROR FBIOPUT_VSCREENINFO failed; frame not displayed"); } +#if defined COMPOSITION_BYPASS + //Signal so that we can close channels if we need to + pthread_mutex_lock(&m->bufferPostLock); + m->bufferPostDone = true; + pthread_cond_signal(&m->bufferPostCond); + pthread_mutex_unlock(&m->bufferPostLock); +#endif + #ifdef DEBUG_CALC_FPS if (debug_fps_level > 0) calc_fps(ns2us(systemTime())); #endif @@ -599,6 +607,34 @@ static int fb_orientationChanged(struct framebuffer_device_t* dev, int orientati } #endif +//Wait until framebuffer content is displayed. +//This is called in the context of threadLoop. +//Display loop wakes this up after display. +static int fb_waitForBufferPost(struct framebuffer_device_t* dev) +{ +#if defined COMPOSITION_BYPASS + private_module_t* m = reinterpret_cast( + dev->common.module); + pthread_mutex_lock(&m->bufferPostLock); + while(m->bufferPostDone == false) { + pthread_cond_wait(&(m->bufferPostCond), &(m->bufferPostLock)); + } + pthread_mutex_unlock(&m->bufferPostLock); +#endif + return 0; +} + +static int fb_resetBufferPostStatus(struct framebuffer_device_t* dev) +{ +#if defined COMPOSITION_BYPASS + private_module_t* m = reinterpret_cast( + dev->common.module); + pthread_mutex_lock(&m->bufferPostLock); + m->bufferPostDone = false; + pthread_mutex_unlock(&m->bufferPostLock); +#endif + return 0; +} static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer) { if (private_handle_t::validate(buffer) < 0) @@ -990,6 +1026,11 @@ int mapFrameBufferLocked(struct private_module_t* module) pthread_t hdmiUIThread; pthread_create(&hdmiUIThread, NULL, &hdmi_ui_loop, (void *) module); #endif +#if defined COMPOSITION_BYPASS + pthread_mutex_init(&(module->bufferPostLock), NULL); + pthread_cond_init(&(module->bufferPostCond), NULL); + module->bufferPostDone = false; +#endif return 0; } @@ -1053,6 +1094,11 @@ int fb_device_open(hw_module_t const* module, const char* name, dev->device.setActionSafeHeightRatio = fb_setActionSafeHeightRatio; #endif +#if defined COMPOSITION_BYPASS + dev->device.waitForBufferPost = fb_waitForBufferPost; + dev->device.resetBufferPostStatus = fb_resetBufferPostStatus; +#endif + private_module_t* m = (private_module_t*)module; status = mapFrameBuffer(m); if (status >= 0) { diff --git a/libgralloc/gpu.cpp b/libgralloc/gpu.cpp old mode 100644 new mode 100755 index e0f3ebb..8909b95 --- a/libgralloc/gpu.cpp +++ b/libgralloc/gpu.cpp @@ -21,16 +21,13 @@ #include #include +#include + #include "gr.h" #include "gpu.h" #include "memalloc.h" #include "alloc_controller.h" -static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00; -static const int QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03; -static const int QOMX_INTERLACE_FLAG = 0x49283654; -static const int QOMX_3D_VIDEO_FLAG = 0x23784238; - using namespace gralloc; using android::sp; @@ -170,7 +167,6 @@ int gpu_context_t::gralloc_alloc_buffer(size_t size, int usage, hnd->offset = data.offset; hnd->base = int(data.base) + data.offset; - hnd->lockState = private_handle_t::LOCK_STATE_MAPPED; *pHandle = hnd; } @@ -247,6 +243,7 @@ int gpu_context_t::alloc_impl(int w, int h, int format, int usage, size = ALIGN( alignedw * alignedh, 8192); size += ALIGN( alignedw * ALIGN(h/2, 32), 8192); break; + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: case HAL_PIXEL_FORMAT_YCbCr_420_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YV12: @@ -256,8 +253,14 @@ int gpu_context_t::alloc_impl(int w, int h, int format, int usage, } alignedw = ALIGN(w, 16); alignedh = h; - size = alignedw*alignedh + + if (HAL_PIXEL_FORMAT_NV12_ENCODEABLE == colorFormat) { + // The encoder requires a 2K aligned chroma offset. + size = ALIGN(alignedw*alignedh, 2048) + + (ALIGN(alignedw/2, 16) * (alignedh/2))*2; + } else { + size = alignedw*alignedh + (ALIGN(alignedw/2, 16) * (alignedh/2))*2; + } size = ALIGN(size, 4096); break; @@ -289,6 +292,13 @@ int gpu_context_t::alloc_impl(int w, int h, int format, int usage, return err; } + // Create a genlock lock for this buffer handle. + err = genlock_create_lock((native_handle_t*)(*pHandle)); + if (err) { + LOGE("%s: genlock_create_lock failed", __FUNCTION__); + free_impl(reinterpret_cast(pHandle)); + return err; + } *pStride = alignedw; return 0; } @@ -308,6 +318,13 @@ int gpu_context_t::free_impl(private_handle_t const* hnd) { return err; terminateBuffer(&m->base, const_cast(hnd)); } + + // Release the genlock + int err = genlock_release_lock((native_handle_t*)hnd); + if (err) { + LOGE("%s: genlock_release_lock failed", __FUNCTION__); + } + delete hnd; return 0; } diff --git a/libgralloc/gralloc_priv.h b/libgralloc/gralloc_priv.h index 0130b6e..00039c4 100644 --- a/libgralloc/gralloc_priv.h +++ b/libgralloc/gralloc_priv.h @@ -52,6 +52,13 @@ enum { GRALLOC_USAGE_PRIVATE_ION = 0x00020000, }; +enum { + /* Gralloc perform enums + */ + GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER = 0x080000001, +}; + + enum { GPU_COMPOSITION, C2D_COMPOSITION, @@ -155,14 +162,7 @@ private: enum { /* OEM specific HAL formats */ - //HAL_PIXEL_FORMAT_YCbCr_422_SP = 0x100, // defined in hardware.h - //HAL_PIXEL_FORMAT_YCrCb_420_SP = 0x101, // defined in hardware.h - HAL_PIXEL_FORMAT_YCbCr_422_P = 0x102, - HAL_PIXEL_FORMAT_YCbCr_420_P = 0x103, - //HAL_PIXEL_FORMAT_YCbCr_422_I = 0x104, // defined in hardware.h - HAL_PIXEL_FORMAT_YCbCr_420_I = 0x105, - HAL_PIXEL_FORMAT_CbYCrY_422_I = 0x106, - HAL_PIXEL_FORMAT_CbYCrY_420_I = 0x107, + HAL_PIXEL_FORMAT_NV12_ENCODEABLE = 0x102, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED = 0x108, HAL_PIXEL_FORMAT_YCbCr_420_SP = 0x109, HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO = 0x10A, @@ -260,6 +260,11 @@ struct private_module_t { pthread_mutex_t overlayLock; pthread_cond_t overlayPost; #endif +#ifdef COMPOSITION_BYPASS + pthread_mutex_t bufferPostLock; + pthread_cond_t bufferPostCond; + bool bufferPostDone; +#endif }; /*****************************************************************************/ @@ -278,16 +283,14 @@ struct private_handle_t { PRIV_FLAGS_USES_ASHMEM = 0x00000010, PRIV_FLAGS_NEEDS_FLUSH = 0x00000020, PRIV_FLAGS_DO_NOT_FLUSH = 0x00000040, - }; - - enum { - LOCK_STATE_WRITE = 1<<31, - LOCK_STATE_MAPPED = 1<<30, - LOCK_STATE_READ_MASK = 0x3FFFFFFF + PRIV_FLAGS_SW_LOCK = 0x00000080, + PRIV_FLAGS_NONCONTIGUOUS_MEM = 0x00000100, + PRIV_FLAGS_HWC_LOCK = 0x00000200, // Set by HWC when storing the handle }; // file-descriptors int fd; + int genlockHandle; // genlock handle to be dup'd by the binder // ints int magic; int flags; @@ -297,23 +300,22 @@ struct private_handle_t { // FIXME: the attributes below should be out-of-line int base; - int lockState; - int writeOwner; int gpuaddr; // The gpu address mapped into the mmu. If using ashmem, set to 0 They don't care int pid; int format; int width; int height; + int genlockPrivFd; // local fd of the genlock device. #ifdef __cplusplus - static const int sNumInts = 13; - static const int sNumFds = 1; + static const int sNumInts = 12; + static const int sNumFds = 2; static const int sMagic = 'gmsm'; private_handle_t(int fd, int size, int flags, int bufferType, int format, int width, int height) : - fd(fd), magic(sMagic), flags(flags), size(size), offset(0), bufferType(bufferType), - base(0), lockState(0), writeOwner(0), gpuaddr(0), pid(getpid()), format(format), width(width), - height(height) + fd(fd), genlockHandle(-1), magic(sMagic), flags(flags), size(size), offset(0), + bufferType(bufferType), base(0), gpuaddr(0), pid(getpid()), format(format), + width(width), height(height), genlockPrivFd(-1) { version = sizeof(native_handle); numInts = sNumInts; diff --git a/libgralloc/ionalloc.cpp b/libgralloc/ionalloc.cpp index d92dedc..e91ee2f 100644 --- a/libgralloc/ionalloc.cpp +++ b/libgralloc/ionalloc.cpp @@ -139,10 +139,9 @@ int IonAlloc::alloc_buffer(alloc_data& data) close(ionSyncFd); ionSyncFd = FD_INIT; - // Not doing memset for ION, uncomment if needed - // memset(base, 0, ionAllocData.len); + memset(base, 0, ionAllocData.len); // Clean cache after memset - // clean_buffer(base, data.size, data.offset, fd_data.fd); + clean_buffer(base, data.size, data.offset, fd_data.fd); data.base = base; data.fd = fd_data.fd; ioctl(mIonFd, ION_IOC_FREE, &handle_data); diff --git a/libgralloc/mapper.cpp b/libgralloc/mapper.cpp old mode 100644 new mode 100755 index d6024b0..2cecbd5 --- a/libgralloc/mapper.cpp +++ b/libgralloc/mapper.cpp @@ -34,6 +34,7 @@ #include #include +#include #include @@ -132,8 +133,31 @@ int gralloc_register_buffer(gralloc_module_t const* module, private_handle_t* hnd = (private_handle_t*)handle; if (hnd->pid != getpid()) { hnd->base = 0; - hnd->lockState = 0; - hnd->writeOwner = 0; + void *vaddr; + int err = gralloc_map(module, handle, &vaddr); + if (err) { + LOGE("%s: gralloc_map failed", __FUNCTION__); + return err; + } + + // Reset the genlock private fd flag in the handle + hnd->genlockPrivFd = -1; + + // Check if there is a valid lock attached to the handle. + if (-1 == hnd->genlockHandle) { + LOGE("%s: the lock is invalid.", __FUNCTION__); + gralloc_unmap(module, handle); + hnd->base = 0; + return -EINVAL; + } + + // Attach the genlock handle + if (GENLOCK_FAILURE == genlock_attach_lock((native_handle_t *)handle)) { + LOGE("%s: genlock_attach_lock failed", __FUNCTION__); + gralloc_unmap(module, handle); + hnd->base = 0; + return -EINVAL; + } } return 0; } @@ -152,18 +176,19 @@ int gralloc_unregister_buffer(gralloc_module_t const* module, private_handle_t* hnd = (private_handle_t*)handle; - LOGE_IF(hnd->lockState & private_handle_t::LOCK_STATE_READ_MASK, - "[unregister] handle %p still locked (state=%08x)", - hnd, hnd->lockState); - // never unmap buffers that were created in this process if (hnd->pid != getpid()) { - if (hnd->lockState & private_handle_t::LOCK_STATE_MAPPED) { + if (hnd->base != 0) { gralloc_unmap(module, handle); } hnd->base = 0; - hnd->lockState = 0; - hnd->writeOwner = 0; + // Release the genlock + if (-1 != hnd->genlockHandle) { + return genlock_release_lock((native_handle_t *)handle); + } else { + LOGE("%s: there was no genlock attached to this buffer", __FUNCTION__); + return -EINVAL; + } } return 0; } @@ -176,11 +201,7 @@ int terminateBuffer(gralloc_module_t const* module, * to un-map it. It's an error to be here with a locked buffer. */ - LOGE_IF(hnd->lockState & private_handle_t::LOCK_STATE_READ_MASK, - "[terminate] handle %p still locked (state=%08x)", - hnd, hnd->lockState); - - if (hnd->lockState & private_handle_t::LOCK_STATE_MAPPED) { + if (hnd->base != 0) { // this buffer was mapped, unmap it now if (hnd->flags & (private_handle_t::PRIV_FLAGS_USES_PMEM | private_handle_t::PRIV_FLAGS_USES_PMEM_ADSP | @@ -211,70 +232,43 @@ int gralloc_lock(gralloc_module_t const* module, int err = 0; private_handle_t* hnd = (private_handle_t*)handle; - int32_t current_value, new_value; - int retry; - - do { - current_value = hnd->lockState; - new_value = current_value; - - if (current_value & private_handle_t::LOCK_STATE_WRITE) { - // already locked for write - LOGE("handle %p already locked for write", handle); - return -EBUSY; - } else if (current_value & private_handle_t::LOCK_STATE_READ_MASK) { - // already locked for read - if (usage & (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_HW_RENDER)) { - LOGE("handle %p already locked for read", handle); - return -EBUSY; - } else { - // this is not an error - //LOGD("%p already locked for read... count = %d", - // handle, (current_value & ~(1<<31))); - } - } - - // not currently locked - if (usage & (GRALLOC_USAGE_SW_WRITE_MASK | GRALLOC_USAGE_HW_RENDER)) { - // locking for write - new_value |= private_handle_t::LOCK_STATE_WRITE; - } - new_value++; - - retry = android_atomic_cmpxchg(current_value, new_value, - (volatile int32_t*)&hnd->lockState); - } while (retry); - - if (new_value & private_handle_t::LOCK_STATE_WRITE) { - // locking for write, store the tid - hnd->writeOwner = gettid(); - } - - // if requesting sw write for non-framebuffer handles, flag for - // flushing at unlock - - if ((usage & GRALLOC_USAGE_SW_WRITE_MASK) && - !(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { - hnd->flags |= private_handle_t::PRIV_FLAGS_NEEDS_FLUSH; - } - if (usage & (GRALLOC_USAGE_SW_READ_MASK | GRALLOC_USAGE_SW_WRITE_MASK)) { - if (!(current_value & private_handle_t::LOCK_STATE_MAPPED)) { + if (hnd->base == 0) { // we need to map for real pthread_mutex_t* const lock = &sMapLock; pthread_mutex_lock(lock); - if (!(hnd->lockState & private_handle_t::LOCK_STATE_MAPPED)) { - err = gralloc_map(module, handle, vaddr); - if (err == 0) { - android_atomic_or(private_handle_t::LOCK_STATE_MAPPED, - (volatile int32_t*)&(hnd->lockState)); - } - } + err = gralloc_map(module, handle, vaddr); pthread_mutex_unlock(lock); } *vaddr = (void*)hnd->base; - } + // Lock the buffer for read/write operation as specified. Write lock + // has a higher priority over read lock. + int lockType = 0; + if (usage & GRALLOC_USAGE_SW_WRITE_MASK) { + lockType = GENLOCK_WRITE_LOCK; + } else if (usage & GRALLOC_USAGE_SW_READ_MASK) { + lockType = GENLOCK_READ_LOCK; + } + + int timeout = GENLOCK_MAX_TIMEOUT; + if (GENLOCK_FAILURE == genlock_lock_buffer((native_handle_t *)handle, + (genlock_lock_type)lockType, + timeout)) { + LOGE("%s: genlock_lock_buffer (lockType=0x%x) failed", __FUNCTION__, + lockType); + return -EINVAL; + } else { + // Mark this buffer as locked for SW read/write operation. + hnd->flags |= private_handle_t::PRIV_FLAGS_SW_LOCK; + } + + if ((usage & GRALLOC_USAGE_SW_WRITE_MASK) && + !(hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER)) { + // Mark the buffer to be flushed after cpu read/write + hnd->flags |= private_handle_t::PRIV_FLAGS_NEEDS_FLUSH; + } + } return err; } @@ -285,7 +279,6 @@ int gralloc_unlock(gralloc_module_t const* module, return -EINVAL; private_handle_t* hnd = (private_handle_t*)handle; - int32_t current_value, new_value; if (hnd->flags & private_handle_t::PRIV_FLAGS_NEEDS_FLUSH) { int err; @@ -297,28 +290,14 @@ int gralloc_unlock(gralloc_module_t const* module, hnd->flags &= ~private_handle_t::PRIV_FLAGS_NEEDS_FLUSH; } - do { - current_value = hnd->lockState; - new_value = current_value; - - if (current_value & private_handle_t::LOCK_STATE_WRITE) { - // locked for write - if (hnd->writeOwner == gettid()) { - hnd->writeOwner = 0; - new_value &= ~private_handle_t::LOCK_STATE_WRITE; - } - } - - if ((new_value & private_handle_t::LOCK_STATE_READ_MASK) == 0) { - LOGE("handle %p not locked", handle); + if ((hnd->flags & private_handle_t::PRIV_FLAGS_SW_LOCK)) { + // Unlock the buffer. + if (GENLOCK_FAILURE == genlock_unlock_buffer((native_handle_t *)handle)) { + LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); return -EINVAL; - } - - new_value--; - - } while (android_atomic_cmpxchg(current_value, new_value, - (volatile int32_t*)&hnd->lockState)); - + } else + hnd->flags &= ~private_handle_t::PRIV_FLAGS_SW_LOCK; + } return 0; } @@ -330,7 +309,6 @@ int gralloc_perform(struct gralloc_module_t const* module, int res = -EINVAL; va_list args; va_start(args, operation); -#if 0 switch (operation) { case GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER: { @@ -338,6 +316,9 @@ int gralloc_perform(struct gralloc_module_t const* module, size_t size = va_arg(args, size_t); size_t offset = va_arg(args, size_t); void* base = va_arg(args, void*); + int width = va_arg(args, int); + int height = va_arg(args, int); + int format = va_arg(args, int); native_handle_t** handle = va_arg(args, native_handle_t**); int memoryFlags = va_arg(args, int); @@ -367,31 +348,18 @@ int gralloc_perform(struct gralloc_module_t const* module, hnd->size = size; hnd->offset = offset; hnd->base = intptr_t(base) + offset; - hnd->lockState = private_handle_t::LOCK_STATE_MAPPED; hnd->gpuaddr = 0; + hnd->width = width; + hnd->height = height; + hnd->format = format; *handle = (native_handle_t *)hnd; res = 0; break; } - case GRALLOC_MODULE_PERFORM_UPDATE_BUFFER_HANDLE: - { - native_handle_t* handle = va_arg(args, native_handle_t*); - int w = va_arg(args, int); - int h = va_arg(args, int); - int f = va_arg(args, int); - private_handle_t* hnd = (private_handle_t*)handle; - hnd->width = w; - hnd->height = h; - if (hnd->format != f) { - hnd->format = f; - } - break; - } default: break; } -#endif va_end(args); return res; } diff --git a/libhwcomposer/Android.mk b/libhwcomposer/Android.mk old mode 100644 new mode 100755 index 9a84146..fd1a761 --- a/libhwcomposer/Android.mk +++ b/libhwcomposer/Android.mk @@ -6,18 +6,26 @@ include $(CLEAR_VARS) LOCAL_PRELINK_MODULE := false LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_SHARED_LIBRARIES := liblog libcutils libEGL libhardware libutils liboverlay +LOCAL_SHARED_LIBRARIES += libgenlock LOCAL_SRC_FILES := \ hwcomposer.cpp LOCAL_MODULE := hwcomposer.$(TARGET_BOARD_PLATFORM) LOCAL_CFLAGS:= -DLOG_TAG=\"$(TARGET_BOARD_PLATFORM).hwcomposer\" + LOCAL_C_INCLUDES += hardware/qcom/display/libgralloc LOCAL_C_INCLUDES += hardware/qcom/display/liboverlay LOCAL_C_INCLUDES += hardware/qcom/display/libcopybit +LOCAL_C_INCLUDES += hardware/qcom/display/libgenlock +LOCAL_C_INCLUDES += hardware/qcom/display/libqcomui + ifeq ($(TARGET_HAVE_HDMI_OUT),true) LOCAL_CFLAGS += -DHDMI_DUAL_DISPLAY endif +ifeq ($(TARGET_USES_OVERLAY),true) +LOCAL_CFLAGS += -DUSE_OVERLAY +endif ifeq ($(TARGET_HAVE_BYPASS),true) LOCAL_CFLAGS += -DCOMPOSITION_BYPASS endif diff --git a/libhwcomposer/hwcomposer.cpp b/libhwcomposer/hwcomposer.cpp old mode 100644 new mode 100755 index c43d5c1..517ae82 --- a/libhwcomposer/hwcomposer.cpp +++ b/libhwcomposer/hwcomposer.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include /*****************************************************************************/ #define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1)) @@ -58,12 +60,6 @@ enum HWCCompositionType { HWC_USE_COPYBIT // This layer is to be handled by copybit }; -enum HWCPrivateFlags { - HWC_USE_ORIGINAL_RESOLUTION = HWC_FLAGS_PRIVATE_0, // This layer is to be drawn using overlays - HWC_DO_NOT_USE_OVERLAY = HWC_FLAGS_PRIVATE_1, // Do not use overlays to draw this layer - HWC_COMP_BYPASS = HWC_FLAGS_PRIVATE_3, // Layer "might" use or have used bypass -}; - enum HWCLayerType{ HWC_SINGLE_VIDEO = 0x1, HWC_ORIG_RESOLUTION = 0x2, @@ -82,6 +78,11 @@ enum { MAX_BYPASS_LAYERS = 2, ANIM_FRAME_COUNT = 30, }; + +enum BypassBufferLockState { + BYPASS_BUFFER_UNLOCKED, + BYPASS_BUFFER_LOCKED, +}; #endif enum eHWCOverlayStatus { @@ -94,8 +95,11 @@ struct hwc_context_t { hwc_composer_device_t device; /* our private state goes below here */ overlay::Overlay* mOverlayLibObject; + native_handle_t *previousOverlayHandle; #ifdef COMPOSITION_BYPASS overlay::OverlayUI* mOvUI[MAX_BYPASS_LAYERS]; + native_handle_t* previousBypassHandle[MAX_BYPASS_LAYERS]; + BypassBufferLockState bypassBufferLockState[MAX_BYPASS_LAYERS]; int animCount; BypassState bypassState; #endif @@ -227,14 +231,39 @@ static int hwc_closeOverlayChannels(hwc_context_t* ctx) { #ifdef COMPOSITION_BYPASS // To-do: Merge this with other blocks & move them to a separate file. +void unlockPreviousBypassBuffers(hwc_context_t* ctx) { + // Unlock the previous bypass buffers. We can blindly unlock the buffers here, + // because buffers will be in this list only if the lock was successfully acquired. + for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { + if (ctx->previousBypassHandle[i]) { + private_handle_t *hnd = (private_handle_t*) ctx->previousBypassHandle[i]; + // Validate the handle to make sure it hasn't been deallocated. + if (private_handle_t::validate(ctx->previousBypassHandle[i])) { + continue; + } + // Check if the handle was locked previously + if (private_handle_t::PRIV_FLAGS_HWC_LOCK & hnd->flags) { + if (GENLOCK_FAILURE == genlock_unlock_buffer(ctx->previousBypassHandle[i])) { + LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); + } else { + ctx->previousBypassHandle[i] = NULL; + // Reset the lock flag + hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK; + } + } + } + } +} + void closeBypass(hwc_context_t* ctx) { - for (int index = 0 ; index < MAX_BYPASS_LAYERS; index++) { - ctx->mOvUI[index]->closeChannel(); + unlockPreviousBypassBuffers(ctx); + for (int index = 0 ; index < MAX_BYPASS_LAYERS; index++) { + ctx->mOvUI[index]->closeChannel(); + } #ifdef DEBUG LOGE("%s", __FUNCTION__); #endif } -} #endif /* @@ -307,6 +336,25 @@ static int prepareOverlay(hwc_context_t *ctx, hwc_layer_t *layer, const bool wai return 0; } +void unlockPreviousOverlayBuffer(hwc_context_t* ctx) +{ + if (ctx->previousOverlayHandle) { + // Validate the handle before attempting to use it. + if (!private_handle_t::validate(ctx->previousOverlayHandle)) { + private_handle_t *hnd = (private_handle_t*)ctx->previousOverlayHandle; + // Unlock any previously locked buffers + if (private_handle_t::PRIV_FLAGS_HWC_LOCK & hnd->flags) { + if (GENLOCK_NO_ERROR == genlock_unlock_buffer(ctx->previousOverlayHandle)) { + ctx->previousOverlayHandle = NULL; + hnd->flags &= ~private_handle_t::PRIV_FLAGS_HWC_LOCK; + } else { + LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); + } + } + } + } +} + bool canSkipComposition(hwc_context_t* ctx, int yuvBufferCount, int currentLayerCount, int numLayersNotUpdating) { @@ -459,9 +507,21 @@ static int drawLayerUsingBypass(hwc_context_t *ctx, hwc_layer_t *layer, overlay::OverlayUI *ovUI = ctx->mOvUI[index]; int ret = 0; private_handle_t *hnd = (private_handle_t *)layer->handle; + ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED; + if (GENLOCK_FAILURE == genlock_lock_buffer(hnd, GENLOCK_READ_LOCK, + GENLOCK_MAX_TIMEOUT)) { + LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__); + return -1; + } + ctx->bypassBufferLockState[index] = BYPASS_BUFFER_LOCKED; ret = ovUI->queueBuffer(hnd); if (ret) { LOGE("drawLayerUsingBypass queueBuffer failed"); + // Unlock the locked buffer + if (GENLOCK_FAILURE == genlock_unlock_buffer(hnd)) { + LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); + } + ctx->bypassBufferLockState[index] = BYPASS_BUFFER_UNLOCKED; return -1; } } @@ -498,9 +558,26 @@ static bool isDisjoint(const hwc_layer_list_t* list) { return true; } +static bool usesContiguousMemory(const hwc_layer_list_t* list) { + for(int i = 0; i < list->numHwLayers; i++) { + const private_handle_t *hnd = + reinterpret_cast(list->hwLayers[i].handle); + if(hnd != NULL && (hnd->flags & + private_handle_t::PRIV_FLAGS_NONCONTIGUOUS_MEM + )) { + // Bypass cannot work for non contiguous buffers + return false; + } + } + return true; +} + /* - * Checks if doing comp. bypass is possible. If video is not on and there - * are 2 layers then its doable. + * Checks if doing comp. bypass is possible. + * It is possible if + * 1. If video is not on + * 2. There are 2 layers + * 3. The memory type is contiguous */ inline static bool isBypassDoable(hwc_composer_device_t *dev, const int yuvCount, const hwc_layer_list_t* list) { @@ -511,6 +588,9 @@ inline static bool isBypassDoable(hwc_composer_device_t *dev, const int yuvCount if(hwcModule->isBypassEnabled == false) { return false; } + // Check if memory type is contiguous + if(!usesContiguousMemory(list)) + return false; //Disable bypass during animation if(UNLIKELY(ctx->animCount)) { --(ctx->animCount); @@ -570,6 +650,25 @@ void unsetBypassLayerFlags(hwc_layer_list_t* list) { } } +void unsetBypassBufferLockState(hwc_context_t* ctx) { + for (int i=0; i< MAX_BYPASS_LAYERS; i++) { + ctx->bypassBufferLockState[i] = BYPASS_BUFFER_UNLOCKED; + } +} + +void storeLockedBypassHandle(hwc_layer_list_t* list, hwc_context_t* ctx) { + for (int index = 0 ; index < list->numHwLayers; index++) { + // Store the current bypass handle. + if (list->hwLayers[index].flags == HWC_COMP_BYPASS) { + private_handle_t *hnd = (private_handle_t*)list->hwLayers[index].handle; + if (ctx->bypassBufferLockState[index] == BYPASS_BUFFER_LOCKED) { + ctx->previousBypassHandle[index] = (native_handle_t*)list->hwLayers[index].handle; + hnd->flags |= private_handle_t::PRIV_FLAGS_HWC_LOCK; + } else + ctx->previousBypassHandle[index] = NULL; + } + } +} #endif //COMPOSITION_BYPASS @@ -708,15 +807,20 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) { hwc_context_t* ctx = (hwc_context_t*)(dev); - if(!ctx || !list) { - LOGE("hwc_prepare invalid context or list"); - return -1; + if(!ctx) { + LOGE("hwc_prepare invalid context"); + return -1; } private_hwc_module_t* hwcModule = reinterpret_cast( dev->common.module); - if(!hwcModule) { - LOGE("hwc_prepare null module "); + if (!list || !hwcModule) { + LOGE("hwc_prepare invalid list or module"); +#ifdef COMPOSITION_BYPASS + unlockPreviousBypassBuffers(ctx); + unsetBypassBufferLockState(ctx); +#endif + unlockPreviousOverlayBuffer(ctx); return -1; } @@ -739,6 +843,8 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) { s3dVideoFormat = getS3DVideoFormat(list); if (s3dVideoFormat) isS3DCompositionNeeded = isS3DCompositionRequired(); + } else { + unlockPreviousOverlayBuffer(ctx); } if (list->flags & HWC_GEOMETRY_CHANGED) { @@ -757,29 +863,55 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) { // need to still mark the layer for S3D composition if (isS3DCompositionNeeded) markUILayerForS3DComposition(list->hwLayers[i], s3dVideoFormat); + + if (hwcModule->compositionType + & (COMPOSITION_TYPE_C2D | COMPOSITION_TYPE_MDP)) { + // Ensure that HWC_OVERLAY layers below skip layers do not + // overwrite GPU composed skip layers. + ssize_t layer_countdown = ((ssize_t)i) - 1; + while (layer_countdown >= 0) + { + // Mark every non-mdp overlay layer below the + // skip-layer for GPU composition. + switch(list->hwLayers[layer_countdown].compositionType) { + case HWC_FRAMEBUFFER: + case HWC_USE_OVERLAY: + break; + case HWC_USE_COPYBIT: + default: + list->hwLayers[layer_countdown].compositionType = HWC_FRAMEBUFFER; + break; + } + layer_countdown--; + } + } continue; } if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) && (yuvBufferCount == 1)) { - bool waitForVsync = skipComposition ? true:false; + bool waitForVsync = true; if (!isValidDestination(hwcModule->fbDevice, list->hwLayers[i].displayFrame)) { list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; - skipComposition = false; +#ifdef USE_OVERLAY } else if(prepareOverlay(ctx, &(list->hwLayers[i]), waitForVsync) == 0) { list->hwLayers[i].compositionType = HWC_USE_OVERLAY; list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB; // We've opened the channel. Set the state to open. ctx->hwcOverlayStatus = HWC_OVERLAY_OPEN; +#endif } - else if (hwcModule->compositionType & (COMPOSITION_TYPE_C2D)) { + else if (hwcModule->compositionType & (COMPOSITION_TYPE_C2D| + COMPOSITION_TYPE_MDP)) { //Fail safe path: If drawing with overlay fails, //Use C2D if available. list->hwLayers[i].compositionType = HWC_USE_COPYBIT; - skipComposition = false; } else { //If C2D is not enabled fall back to GPU. list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; + } + if (HWC_USE_OVERLAY != list->hwLayers[i].compositionType) { + unlockPreviousOverlayBuffer(ctx); skipComposition = false; } } else if (isS3DCompositionNeeded) { @@ -819,7 +951,9 @@ static int hwc_prepare(hwc_composer_device_t *dev, hwc_layer_list_t* list) { ctx->bypassState = BYPASS_ON; } } else { + unlockPreviousBypassBuffers(ctx); unsetBypassLayerFlags(list); + unsetBypassBufferLockState(ctx); if(ctx->bypassState == BYPASS_ON) { ctx->bypassState = BYPASS_OFF_PENDING; } @@ -887,6 +1021,14 @@ static int drawLayerUsingCopybit(hwc_composer_device_t *dev, hwc_layer_t *layer, return -1; } + // Lock this buffer for read. + genlock_lock_type lockType = GENLOCK_READ_LOCK; + int err = genlock_lock_buffer(hnd, lockType, GENLOCK_MAX_TIMEOUT); + if (GENLOCK_FAILURE == err) { + LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__); + return -1; + } + // Set the copybit source: copybit_image_t src; src.w = ALIGN(hnd->width, 32); @@ -894,6 +1036,11 @@ static int drawLayerUsingCopybit(hwc_composer_device_t *dev, hwc_layer_t *layer, src.format = hnd->format; src.base = (void *)hnd->base; src.handle = (native_handle_t *)layer->handle; + src.horiz_padding = src.w - hnd->width; + // Initialize vertical padding to zero for now, + // this needs to change to accomodate vertical stride + // if needed in the future + src.vert_padding = 0; // Copybit source rect hwc_rect_t sourceCrop = layer->sourceCrop; @@ -912,11 +1059,13 @@ static int drawLayerUsingCopybit(hwc_composer_device_t *dev, hwc_layer_t *layer, android_native_buffer_t *renderBuffer = (android_native_buffer_t *)eglGetRenderBufferANDROID(dpy, surface); if (!renderBuffer) { LOGE("eglGetRenderBufferANDROID returned NULL buffer"); + genlock_unlock_buffer(hnd); return -1; } private_handle_t *fbHandle = (private_handle_t *)renderBuffer->handle; if(!fbHandle) { LOGE("Framebuffer handle is NULL"); + genlock_unlock_buffer(hnd); return -1; } dst.w = ALIGN(fbHandle->width,32); @@ -930,16 +1079,24 @@ static int drawLayerUsingCopybit(hwc_composer_device_t *dev, hwc_layer_t *layer, region_iterator copybitRegion(region); copybit_device_t *copybit = hwcModule->copybitEngine; + copybit->set_parameter(copybit, COPYBIT_FRAMEBUFFER_WIDTH, renderBuffer->width); + copybit->set_parameter(copybit, COPYBIT_FRAMEBUFFER_HEIGHT, renderBuffer->height); copybit->set_parameter(copybit, COPYBIT_TRANSFORM, layer->transform); copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, (layer->blending == HWC_BLENDING_NONE) ? 0xFF : layer->alpha); copybit->set_parameter(copybit, COPYBIT_PREMULTIPLIED_ALPHA, (layer->blending == HWC_BLENDING_PREMULT)? COPYBIT_ENABLE : COPYBIT_DISABLE); - int err = copybit->stretch(copybit, &dst, &src, &dstRect, &srcRect, ©bitRegion); + err = copybit->stretch(copybit, &dst, &src, &dstRect, &srcRect, ©bitRegion); if(err < 0) LOGE("copybit stretch failed"); + // Unlock this buffer since copybit is done with it. + err = genlock_unlock_buffer(hnd); + if (GENLOCK_FAILURE == err) { + LOGE("%s: genlock_unlock_buffer failed", __FUNCTION__); + } + return err; } @@ -955,13 +1112,33 @@ static int drawLayerUsingOverlay(hwc_context_t *ctx, hwc_layer_t *layer) overlay::Overlay *ovLibObject = ctx->mOverlayLibObject; int ret = 0; - ret = ovLibObject->queueBuffer(hnd); - if (!ret) { - LOGE("drawLayerUsingOverlay queueBuffer failed"); + // Lock this buffer for read. + if (GENLOCK_NO_ERROR != genlock_lock_buffer(hnd, GENLOCK_READ_LOCK, + GENLOCK_MAX_TIMEOUT)) { + LOGE("%s: genlock_lock_buffer(READ) failed", __FUNCTION__); return -1; } + + ret = ovLibObject->queueBuffer(hnd); + + // Unlock the previously locked buffer, since the overlay has completed reading the buffer + unlockPreviousOverlayBuffer(ctx); + + if (!ret) { + LOGE("drawLayerUsingOverlay queueBuffer failed"); + // Unlock the buffer handle + genlock_unlock_buffer(hnd); + ctx->previousOverlayHandle = NULL; + } else { + // Store the current buffer handle as the one that is to be unlocked after + // the next overlay play call. + ctx->previousOverlayHandle = hnd; + hnd->flags |= private_handle_t::PRIV_FLAGS_HWC_LOCK; + } + + return ret; } - return 0; + return -1; } static int hwc_set(hwc_composer_device_t *dev, @@ -970,15 +1147,23 @@ static int hwc_set(hwc_composer_device_t *dev, hwc_layer_list_t* list) { hwc_context_t* ctx = (hwc_context_t*)(dev); - if(!ctx || !list) { - LOGE("hwc_set invalid context or list"); - return -1; + if(!ctx) { + LOGE("hwc_set invalid context"); + return -1; } private_hwc_module_t* hwcModule = reinterpret_cast( dev->common.module); - if(!hwcModule) { - LOGE("hwc_set null module "); + + framebuffer_device_t *fbDev = hwcModule->fbDevice; + + if (!list || !hwcModule) { + LOGE("hwc_set invalid list or module"); +#ifdef COMPOSITION_BYPASS + unlockPreviousBypassBuffers(ctx); + unsetBypassBufferLockState(ctx); +#endif + unlockPreviousOverlayBuffer(ctx); return -1; } @@ -1000,15 +1185,34 @@ static int hwc_set(hwc_composer_device_t *dev, } } +#ifdef COMPOSITION_BYPASS + unlockPreviousBypassBuffers(ctx); + storeLockedBypassHandle(list, ctx); + // We have stored the handles, unset the current lock states in the context. + unsetBypassBufferLockState(ctx); + + //Setup for waiting until 1 FB post is done before closing bypass mode. + if (ctx->bypassState == BYPASS_OFF_PENDING) { + fbDev->resetBufferPostStatus(fbDev); + } +#endif + // Do not call eglSwapBuffers if we the skip composition flag is set on the list. if (!(list->flags & HWC_SKIP_COMPOSITION)) { EGLBoolean sucess = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur); if (!sucess) { ret = HWC_EGL_ERROR; + LOGE("eglSwapBuffers() failed in %s", __FUNCTION__); } } #ifdef COMPOSITION_BYPASS if(ctx->bypassState == BYPASS_OFF_PENDING) { + //Close channels only after fb content is displayed. + //We have already reset status before eglSwapBuffers. + if (!(list->flags & HWC_SKIP_COMPOSITION)) { + fbDev->waitForBufferPost(fbDev); + } + closeBypass(ctx); ctx->bypassState = BYPASS_OFF; } @@ -1047,13 +1251,17 @@ static int hwc_device_close(struct hw_device_t *dev) hwcModule->fbDevice = NULL; } + unlockPreviousOverlayBuffer(ctx); + if (ctx) { delete ctx->mOverlayLibObject; ctx->mOverlayLibObject = NULL; #ifdef COMPOSITION_BYPASS - for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { - delete ctx->mOvUI[i]; - } + for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { + delete ctx->mOvUI[i]; + } + unlockPreviousBypassBuffers(ctx); + unsetBypassBufferLockState(ctx); #endif free(ctx); } @@ -1130,7 +1338,9 @@ static int hwc_device_open(const struct hw_module_t* module, const char* name, #ifdef COMPOSITION_BYPASS for(int i = 0; i < MAX_BYPASS_LAYERS; i++) { dev->mOvUI[i] = new overlay::OverlayUI(); + dev->previousBypassHandle[i] = NULL; } + unsetBypassBufferLockState(dev); dev->animCount = 0; dev->bypassState = BYPASS_OFF; #endif @@ -1139,6 +1349,7 @@ static int hwc_device_open(const struct hw_module_t* module, const char* name, dev->mHDMIEnabled = false; dev->pendingHDMI = false; #endif + dev->previousOverlayHandle = NULL; dev->hwcOverlayStatus = HWC_OVERLAY_CLOSED; /* initialize the procs */ dev->device.common.tag = HARDWARE_DEVICE_TAG; diff --git a/liboverlay/overlayLib.cpp b/liboverlay/overlayLib.cpp index c468c4e..6f2c604 100755 --- a/liboverlay/overlayLib.cpp +++ b/liboverlay/overlayLib.cpp @@ -256,8 +256,21 @@ bool overlay::enableBarrier (unsigned int orientation) { int overlay::getColorFormat(int format) { - return (format == HAL_PIXEL_FORMAT_YV12) ? - format : COLOR_FORMAT(format); + if (format == HAL_PIXEL_FORMAT_YV12) + return format; + else if (format & INTERLACE_MASK) + return format ^ HAL_PIXEL_FORMAT_INTERLACE; + else + return COLOR_FORMAT(format); +} + +bool overlay::isInterlacedContent(int format) +{ + if ((format != HAL_PIXEL_FORMAT_YV12) && + (format & INTERLACE_MASK)) + return true; + + return false; } unsigned int overlay::getOverlayConfig (unsigned int format3D, bool poll, @@ -466,8 +479,8 @@ bool Overlay::updateOverlaySource(const overlay_buffer_info& info, int orientati int orientHdmi = 0; int orientPrimary = sHDMIAsPrimary ? 0 : orientation; int orient[2] = {orientPrimary, orientHdmi}; - // enable waitForVsync on HDMI - bool waitForHDMI = true; + // disable waitForVsync on HDMI, since we call the wait ioctl + bool waitForHDMI = false; bool waitForPrimary = sHDMIAsPrimary ? true : waitForVsync; bool waitCond[2] = {waitForPrimary, waitForHDMI}; @@ -582,7 +595,7 @@ bool Overlay::setSource(const overlay_buffer_info& info, int orientation, if (FRAMEBUFFER_1 == i) { // Disable rotation for HDMI noRot = true; - waitForVsync = true; + waitForVsync = false; } if(!startChannel(info, i, noRot, false, mS3DFormat, i, waitForVsync, num_buffers)) { @@ -715,6 +728,10 @@ bool Overlay::queueBuffer(uint32_t offset, int channel) { return objOvDataChannel[channel].queueBuffer(offset); } +bool Overlay::waitForHdmiVsync(int channel) { + return objOvDataChannel[channel].waitForHdmiVsync(); +} + bool Overlay::queueBuffer(buffer_handle_t buffer) { private_handle_t const* hnd = reinterpret_cast (buffer); @@ -737,12 +754,17 @@ bool Overlay::queueBuffer(buffer_handle_t buffer) { case OV_3D_VIDEO_3D_PANEL: case OV_3D_VIDEO_2D_TV: case OV_3D_VIDEO_3D_TV: - for (int i=0; i=0; i--) { if(!queueBuffer(fd, offset, i)) { LOGE("%s:failed for channel %d", __FUNCTION__, i); return false; } } + //Wait for HDMI done.. + if(!waitForHdmiVsync(VG1_PIPE)) { + LOGE("%s: waitforHdmiVsync failed", __FUNCTION__); + return false; + } break; default: LOGE("%s:Unknown state %d", __FUNCTION__, mState); @@ -961,8 +983,8 @@ bool OverlayControlChannel::setOverlayInformation(const overlay_buffer_info& inf mOVInfo.z_order = zorder; mOVInfo.alpha = 0xff; mOVInfo.transp_mask = 0xffffffff; - mOVInfo.flags = flags; } + mOVInfo.flags = flags; if (!ignoreFB) mOVInfo.flags |= MDP_OV_PLAY_NOWAIT; else @@ -1042,7 +1064,9 @@ bool OverlayControlChannel::updateOverlaySource(const overlay_buffer_info& info, ovBufInfo.height = info.height; ovBufInfo.format = hw_format; - if (!setOverlayInformation(ovBufInfo, 0, orientation, 0, waitForVsync, UPDATE_REQUEST)) + int flags = isInterlacedContent(info.format) ? MDP_DEINTERLACE : 0; + if (!setOverlayInformation(ovBufInfo, flags, orientation, 0, waitForVsync, + UPDATE_REQUEST)) return false; return startOVRotatorSessions(ovBufInfo, orientation, UPDATE_REQUEST); @@ -1063,7 +1087,7 @@ bool OverlayControlChannel::startControlChannel(int w, int h, int colorFormat = format; // The interlace mask is part of the HAL_PIXEL_FORMAT_YV12 value. Add // an explicit check for the format - if ((format != HAL_PIXEL_FORMAT_YV12) && (format & INTERLACE_MASK)) { + if (isInterlacedContent(format)) { flags |= MDP_DEINTERLACE; // Get the actual format @@ -1608,6 +1632,19 @@ bool OverlayDataChannel::queue(uint32_t offset) { reportError("overlay play failed."); return false; } + + return true; +} + +bool OverlayDataChannel::waitForHdmiVsync() { + if (!isChannelUP()) { + reportError("waitForHdmiVsync: channel not up"); + return false; + } + if (ioctl(mFD, MSMFB_OVERLAY_PLAY_WAIT, &mOvData)) { + reportError("waitForHdmiVsync: MSMFB_OVERLAY_PLAY_WAIT failed"); + return false; + } return true; } diff --git a/liboverlay/overlayLib.h b/liboverlay/overlayLib.h index aebb075..1448f83 100755 --- a/liboverlay/overlayLib.h +++ b/liboverlay/overlayLib.h @@ -144,6 +144,7 @@ bool enableBarrier(unsigned int orientation); unsigned int getOverlayConfig (unsigned int format3D, bool poll = true, bool isHDMI = false); int getColorFormat(int format); +bool isInterlacedContent(int format); int get_mdp_format(int format); int get_size(int format, int w, int h); int get_rot_output_format(int format); @@ -250,6 +251,7 @@ public: bool closeDataChannel(); bool setFd(int fd); bool queueBuffer(uint32_t offset); + bool waitForHdmiVsync(); bool setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h); bool getCropS3D(overlay_rect *inRect, int channel, int format, overlay_rect *rect); bool isChannelUP() const { return (mFD > 0); } @@ -297,6 +299,7 @@ public: bool setSource(const overlay_buffer_info& info, int orientation, bool hdmiConnected, bool ignoreFB = false, int numBuffers = 2); bool setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h); + bool waitForHdmiVsync(int channel); int getChannelStatus() const { return (mChannelUP ? OVERLAY_CHANNEL_UP: OVERLAY_CHANNEL_DOWN); } void setHDMIStatus (bool isHDMIConnected) { mHDMIConnected = isHDMIConnected; mState = -1; } int getHDMIStatus() const {return (mHDMIConnected ? HDMI_ON : HDMI_OFF); } diff --git a/libqcomui/Android.mk b/libqcomui/Android.mk new file mode 100644 index 0000000..2b786d9 --- /dev/null +++ b/libqcomui/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + qcom_ui.cpp + +LOCAL_SHARED_LIBRARIES := \ + libutils \ + libcutils \ + libmemalloc \ + libui + +LOCAL_C_INCLUDES := $(TOP)/hardware/qcom/display/libgralloc \ +LOCAL_CFLAGS := -DLOG_TAG=\"libQcomUI\" +LOCAL_MODULE := libQcomUI +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) diff --git a/libqcomui/qcom_ui.cpp b/libqcomui/qcom_ui.cpp new file mode 100644 index 0000000..9de00e7 --- /dev/null +++ b/libqcomui/qcom_ui.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +using gralloc::IMemAlloc; +using gralloc::IonController; +using gralloc::alloc_data; +using android::sp; + +namespace { + + static android::sp sAlloc = 0; + + int reallocate_memory(native_handle_t *buffer_handle, int mReqSize, int usage) + { + int ret = 0; + if (sAlloc == 0) { + sAlloc = gralloc::IAllocController::getInstance(true); + } + if (sAlloc == 0) { + LOGE("sAlloc is still NULL"); + return -EINVAL; + } + + // Dealloc the old memory + private_handle_t *hnd = (private_handle_t *)buffer_handle; + sp memalloc = sAlloc->getAllocator(hnd->flags); + ret = memalloc->free_buffer((void*)hnd->base, hnd->size, hnd->offset, hnd->fd); + + if (ret) { + LOGE("%s: free_buffer failed", __FUNCTION__); + return -1; + } + + // Realloc new memory + alloc_data data; + data.base = 0; + data.fd = -1; + data.offset = 0; + data.size = mReqSize; + data.align = getpagesize(); + data.uncached = true; + int allocFlags = usage; + + switch (hnd->format) { + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: + case (HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED^HAL_PIXEL_FORMAT_INTERLACE): { + data.align = 8192; + } break; + default: break; + } + ret = sAlloc->allocate(data, allocFlags, 0); + if (ret == 0) { + hnd->fd = data.fd; + hnd->base = (int)data.base; + hnd->offset = data.offset; + hnd->size = data.size; + } else { + LOGE("%s: allocate failed", __FUNCTION__); + return -EINVAL; + } + return ret; + } +}; // ANONYNMOUS NAMESPACE + +/* + * Gets the number of arguments required for this operation. + * + * @param: operation whose argument count is required. + * + * @return -EINVAL if the operation is invalid. + */ +int getNumberOfArgsForOperation(int operation) { + int num_args = -EINVAL; + switch(operation) { + case NATIVE_WINDOW_SET_BUFFERS_SIZE: + num_args = 1; + break; + case NATIVE_WINDOW_UPDATE_BUFFERS_GEOMETRY: + num_args = 3; + break; + default: LOGE("%s: invalid operation(0x%x)", __FUNCTION__, operation); + break; + }; + return num_args; +} + +/* + * Checks if the format is supported by the GPU. + * + * @param: format to check + * + * @return true if the format is supported by the GPU. + */ +bool isGPUSupportedFormat(int format) { + bool isSupportedFormat = true; + switch(format) { + case (HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED^HAL_PIXEL_FORMAT_INTERLACE): + case (HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED|HAL_3D_OUT_SIDE_BY_SIDE + |HAL_3D_IN_SIDE_BY_SIDE_R_L): + case (HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED|HAL_3D_OUT_SIDE_BY_SIDE + |HAL_3D_IN_SIDE_BY_SIDE_L_R): + isSupportedFormat = false; + break; + default: break; + } + return isSupportedFormat; +} + +/* + * Function to check if the allocated buffer is of the correct size. + * Reallocate the buffer with the correct size, if the size doesn't + * match + * + * @param: handle of the allocated buffer + * @param: requested size for the buffer + * @param: usage flags + * + * return 0 on success + */ +int checkBuffer(native_handle_t *buffer_handle, int size, int usage) +{ + // If the client hasn't set a size, return + if (0 == size) { + return 0; + } + + // Validate the handle + if (private_handle_t::validate(buffer_handle)) { + LOGE("%s: handle is invalid", __FUNCTION__); + return -EINVAL; + } + + // Obtain the private_handle from the native handle + private_handle_t *hnd = reinterpret_cast(buffer_handle); + if (hnd->size < size) { + return reallocate_memory(hnd, size, usage); + } + + return 0; +} + +/* + * Checks if memory needs to be reallocated for this buffer. + * + * @param: Geometry of the current buffer. + * @param: Required Geometry. + * @param: Geometry of the updated buffer. + * + * @return True if a memory reallocation is required. + */ +bool needNewBuffer(const qBufGeometry currentGeometry, + const qBufGeometry requiredGeometry, + const qBufGeometry updatedGeometry) +{ + // If the current buffer info matches the updated info, + // we do not require any memory allocation. + if (updatedGeometry.width && updatedGeometry.height && + updatedGeometry.format) { + return false; + } + if (currentGeometry.width != requiredGeometry.width || + currentGeometry.height != requiredGeometry.height || + currentGeometry.format != requiredGeometry.format) { + // Current and required geometry do not match. Allocation + // required. + return true; + } + return false; +} + +/* + * Update the geometry of this buffer without reallocation. + * + * @param: buffer whose geometry needs to be updated. + * @param: Updated width + * @param: Updated height + * @param: Updated format + */ +int updateBufferGeometry(sp buffer, const qBufGeometry updatedGeometry) +{ + if (buffer == 0) { + LOGE("%s: graphic buffer is NULL", __FUNCTION__); + return -EINVAL; + } + + if (!updatedGeometry.width || !updatedGeometry.height || + !updatedGeometry.format) { + // No update required. Return. + return 0; + } + if (buffer->width == updatedGeometry.width && + buffer->height == updatedGeometry.height && + buffer->format == updatedGeometry.format) { + // The buffer has already been updated. Return. + return 0; + } + + // Validate the handle + if (private_handle_t::validate(buffer->handle)) { + LOGE("%s: handle is invalid", __FUNCTION__); + return -EINVAL; + } + buffer->width = updatedGeometry.width; + buffer->height = updatedGeometry.height; + buffer->format = updatedGeometry.format; + private_handle_t *hnd = (private_handle_t*)(buffer->handle); + if (hnd) { + hnd->width = updatedGeometry.width; + hnd->height = updatedGeometry.height; + hnd->format = updatedGeometry.format; + } else { + LOGE("%s: hnd is NULL", __FUNCTION__); + return -EINVAL; + } + + return 0; +} + +/* + * Updates the flags for the layer + * + * @param: Attribute + * @param: Identifies if the attribute was enabled or disabled. + * + * @return: -EINVAL if the attribute is invalid + */ +int updateLayerQcomFlags(eLayerAttrib attribute, bool enable, int& currentFlags) +{ + int ret = 0; + switch (attribute) { + case LAYER_UPDATE_STATUS: { + if (enable) + currentFlags |= LAYER_UPDATING; + else + currentFlags &= ~LAYER_UPDATING; + } break; + default: LOGE("%s: invalid attribute(0x%x)", __FUNCTION__, attribute); + break; + } + return ret; +} + +/* + * Gets the per frame HWC flags for this layer. + * + * @param: current hwcl flags + * @param: current layerFlags + * + * @return: the per frame flags. + */ +int getPerFrameFlags(int hwclFlags, int layerFlags) { + int flags = hwclFlags; + if (layerFlags & LAYER_UPDATING) + flags &= ~HWC_LAYER_NOT_UPDATING; + else + flags |= HWC_LAYER_NOT_UPDATING; + + return flags; +} + diff --git a/libqcomui/qcom_ui.h b/libqcomui/qcom_ui.h new file mode 100644 index 0000000..6744edf --- /dev/null +++ b/libqcomui/qcom_ui.h @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef INCLUDE_LIBQCOM_UI +#define INCLUDE_LIBQCOM_UI + +#include +#include + +using android::sp; +using android::GraphicBuffer; + +/* + * Qcom specific Native Window perform operations + */ +enum { + NATIVE_WINDOW_SET_BUFFERS_SIZE = 0x10000000, + NATIVE_WINDOW_UPDATE_BUFFERS_GEOMETRY = 0x20000000, +}; + +/* + * Layer Attributes + */ +enum eLayerAttrib { + LAYER_UPDATE_STATUS, +}; + +/* + * Layer Flags + */ +enum { + LAYER_UPDATING = 1<<0, +}; + +/* + * Flags set by the layer and sent to HWC + */ +enum { + HWC_LAYER_NOT_UPDATING = 0x00000002, + HWC_USE_ORIGINAL_RESOLUTION = 0x10000000, + HWC_DO_NOT_USE_OVERLAY = 0x20000000, + HWC_COMP_BYPASS = 0x40000000, +}; + +/* + * Structure to hold the buffer geometry + */ +struct qBufGeometry { + int width; + int height; + int format; + void set(int w, int h, int f) { + width = w; + height = h; + format = f; + } +}; + +/* + * Function to check if the allocated buffer is of the correct size. + * Reallocate the buffer with the correct size, if the size doesn't + * match + * + * @param: handle of the allocated buffer + * @param: requested size for the buffer + * @param: usage flags + * + * return 0 on success + */ +int checkBuffer(native_handle_t *buffer_handle, int size, int usage); + +/* + * Checks if the format is supported by the GPU. + * + * @param: format to check + * + * @return true if the format is supported by the GPU. + */ +bool isGPUSupportedFormat(int format); + +/* + * Gets the number of arguments required for this operation. + * + * @param: operation whose argument count is required. + * + * @return -EINVAL if the operation is invalid. + */ +int getNumberOfArgsForOperation(int operation); + +/* + * Checks if memory needs to be reallocated for this buffer. + * + * @param: Geometry of the current buffer. + * @param: Required Geometry. + * @param: Geometry of the updated buffer. + * + * @return True if a memory reallocation is required. + */ +bool needNewBuffer(const qBufGeometry currentGeometry, + const qBufGeometry requiredGeometry, + const qBufGeometry updatedGeometry); + +/* + * Update the geometry of this buffer without reallocation. + * + * @param: buffer whose geometry needs to be updated. + * @param: Updated buffer geometry + */ +int updateBufferGeometry(sp buffer, const qBufGeometry bufGeometry); + +/* + * Updates the flags for the layer + * + * @param: Attribute + * @param: Identifies if the attribute was enabled or disabled. + * @param: current Layer flags. + * + * @return: Flags for the layer + */ +int updateLayerQcomFlags(eLayerAttrib attribute, bool enable, int& currentFlags); + +/* + * Gets the per frame HWC flags for this layer. + * + * @param: current hwcl flags + * @param: current layerFlags + * + * @return: the per frame flags. + */ +int getPerFrameFlags(int hwclFlags, int layerFlags); + +#endif // INCLUDE_LIBQCOM_UI diff --git a/libtilerenderer/Android.mk b/libtilerenderer/Android.mk new file mode 100644 index 0000000..e0bf342 --- /dev/null +++ b/libtilerenderer/Android.mk @@ -0,0 +1,26 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +ifeq ($(USE_OPENGL_RENDERER),true) +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) +LOCAL_SHARED_LIBRARIES := libutils libcutils libGLESv2 libhwui + +LOCAL_C_INCLUDES += \ + frameworks/base/include/utils \ + frameworks/base/libs/hwui \ + external/skia/include/core \ + external/skia/include/effects \ + external/skia/include/images \ + external/skia/src/ports \ + external/skia/include/utils \ + hardware/libhardware/include/hardware \ + frameworks/base/opengl/include/GLES2 + +LOCAL_SRC_FILES := \ + tilerenderer.cpp + +LOCAL_MODULE := libtilerenderer +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) +endif diff --git a/libtilerenderer/tilerenderer.cpp b/libtilerenderer/tilerenderer.cpp new file mode 100644 index 0000000..ae12ecf --- /dev/null +++ b/libtilerenderer/tilerenderer.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2011 Code Aurora Forum. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include "tilerenderer.h" + +namespace android { +ANDROID_SINGLETON_STATIC_INSTANCE(uirenderer::TileRenderer) ; +namespace uirenderer { + +TileRenderer::TileRenderer() { + mIsTiled = false; +} + +TileRenderer::~TileRenderer() { +} + +void TileRenderer::startTileRendering(OpenGLRenderer* renderer, + int left, int top, + int right, int bottom) { + int width = 0; + int height = 0; + GLenum status = GL_NO_ERROR; + + if (renderer != NULL) { + renderer->getViewport(width, height); + } + + if (!left && !right && !top && !bottom) { + left = 0; + top = 0; + right = width; + bottom = height; + } + + if (!left && !right && !top && !bottom) { + //can't do tile rendering + LOGE("can't tile render; drity region, width, height not available"); + return; + } + + int l = left, t = (height - bottom), w = (right - left), h = (bottom - top), preserve = 0; + + if (l < 0 || t < 0) { + l = (l < 0) ? 0 : l; + t = (t < 0) ? 0 : t; + preserve = 1; + } + + if (w > width || h > height) { + w = (w > width) ? width : w; + h = (h > height) ? height : h; + preserve = 1; + } + + //clear off all errors before tiling, if any + while ((status = glGetError()) != GL_NO_ERROR) { + LOGE("glStartTilingQCOM: 0x%x", status); + } + + if (preserve) + glStartTilingQCOM(l, t, w, h, GL_COLOR_BUFFER_BIT0_QCOM); + else + glStartTilingQCOM(l, t, w, h, GL_NONE); + + status = glGetError(); + if (status == GL_NO_ERROR) + mIsTiled = true; + else + LOGE("glStartTilingQCOM: 0x%x", status); +} + +void TileRenderer::endTileRendering(OpenGLRenderer*) { + if (!mIsTiled) { + return; + } + glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM); + mIsTiled = false; + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + LOGE("glEndTilingQCOM: 0x%x", status); + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libtilerenderer/tilerenderer.h b/libtilerenderer/tilerenderer.h new file mode 100644 index 0000000..bec225d --- /dev/null +++ b/libtilerenderer/tilerenderer.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ANDROID_TILE_RENDERER_H +#define ANDROID_TILE_RENDERER_H + +#include + +namespace android { +namespace uirenderer { + +class OpenGLRenderer; + +class TileRenderer: public Singleton { +public: + TileRenderer(); + ~TileRenderer(); + + void startTileRendering(OpenGLRenderer* renderer, int left, int top, int right, int bottom); + void endTileRendering(OpenGLRenderer*); + +private: + bool mIsTiled; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif