diff --git a/libhwcomposer/hwcomposer.cpp b/libhwcomposer/hwcomposer.cpp index dc12a21..952c9de 100644 --- a/libhwcomposer/hwcomposer.cpp +++ b/libhwcomposer/hwcomposer.cpp @@ -1495,7 +1495,10 @@ static int hwc_set(hwc_composer_device_t *dev, int ret = 0; if (list) { + bool bDumpLayers = needToDumpLayers(); // Check need for debugging dumps for (size_t i=0; inumHwLayers; i++) { + if (bDumpLayers) + dumpLayer(hwcModule->compositionType, list->flags, i, list->hwLayers); if (list->hwLayers[i].flags & HWC_SKIP_LAYER) { continue; #ifdef COMPOSITION_BYPASS diff --git a/libqcomui/Android.mk b/libqcomui/Android.mk index 666ed9b..7630733 100644 --- a/libqcomui/Android.mk +++ b/libqcomui/Android.mk @@ -14,9 +14,14 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libmemalloc \ libui \ - libEGL + libEGL \ + libskia LOCAL_C_INCLUDES := $(TOP)/hardware/qcom/display/libgralloc \ + $(TOP)/frameworks/base/services/surfaceflinger \ + $(TOP)/external/skia/include/core \ + $(TOP)/external/skia/include/images + LOCAL_CFLAGS := -DLOG_TAG=\"libQcomUI\" LOCAL_CFLAGS += -DQCOM_HARDWARE LOCAL_CFLAGS += -DDEBUG_CALC_FPS diff --git a/libqcomui/qcom_ui.cpp b/libqcomui/qcom_ui.cpp index ae203e1..f1a7e6d 100644 --- a/libqcomui/qcom_ui.cpp +++ b/libqcomui/qcom_ui.cpp @@ -34,8 +34,11 @@ #include #include #include -#include #include +#include +#include +#include +#include #include #include @@ -536,6 +539,281 @@ external_display handleEventHDMI(external_display newState, external_display } return retState; } + +// Using global variables for layer dumping since "property_set("debug.sf.dump", +// property)" does not work. +int sfdump_countlimit_raw = 0; +int sfdump_counter_raw = 1; +char sfdump_propstr_persist_raw[PROPERTY_VALUE_MAX] = ""; +char sfdumpdir_raw[256] = ""; +int sfdump_countlimit_png = 0; +int sfdump_counter_png = 1; +char sfdump_propstr_persist_png[PROPERTY_VALUE_MAX] = ""; +char sfdumpdir_png[256] = ""; + +bool needToDumpLayers() +{ + bool bDumpLayer = false; + char sfdump_propstr[PROPERTY_VALUE_MAX]; + time_t timenow; + tm sfdump_time; + + time(&timenow); + localtime_r(&timenow, &sfdump_time); + + if ((property_get("debug.sf.dump.png", sfdump_propstr, NULL) > 0) && + (strncmp(sfdump_propstr, sfdump_propstr_persist_png, + PROPERTY_VALUE_MAX - 1))) { + // Strings exist & not equal implies it has changed, so trigger a dump + strncpy(sfdump_propstr_persist_png, sfdump_propstr, + PROPERTY_VALUE_MAX - 1); + sfdump_countlimit_png = atoi(sfdump_propstr); + sfdump_countlimit_png = (sfdump_countlimit_png < 0) ? 0: + (sfdump_countlimit_png >= LONG_MAX) ? (LONG_MAX - 1): + sfdump_countlimit_png; + if (sfdump_countlimit_png) { + sprintf(sfdumpdir_png,"/data/sfdump.png%04d%02d%02d.%02d%02d%02d", + sfdump_time.tm_year + 1900, sfdump_time.tm_mon + 1, + sfdump_time.tm_mday, sfdump_time.tm_hour, + sfdump_time.tm_min, sfdump_time.tm_sec); + if (0 == mkdir(sfdumpdir_png, 0777)) + sfdump_counter_png = 0; + else + LOGE("sfdump: Error: %s. Failed to create sfdump directory" + ": %s", strerror(errno), sfdumpdir_png); + } + } + + if (sfdump_counter_png <= sfdump_countlimit_png) + sfdump_counter_png++; + + if ((property_get("debug.sf.dump", sfdump_propstr, NULL) > 0) && + (strncmp(sfdump_propstr, sfdump_propstr_persist_raw, + PROPERTY_VALUE_MAX - 1))) { + // Strings exist & not equal implies it has changed, so trigger a dump + strncpy(sfdump_propstr_persist_raw, sfdump_propstr, + PROPERTY_VALUE_MAX - 1); + sfdump_countlimit_raw = atoi(sfdump_propstr); + sfdump_countlimit_raw = (sfdump_countlimit_raw < 0) ? 0: + (sfdump_countlimit_raw >= LONG_MAX) ? (LONG_MAX - 1): + sfdump_countlimit_raw; + if (sfdump_countlimit_raw) { + sprintf(sfdumpdir_raw,"/data/sfdump.raw%04d%02d%02d.%02d%02d%02d", + sfdump_time.tm_year + 1900, sfdump_time.tm_mon + 1, + sfdump_time.tm_mday, sfdump_time.tm_hour, + sfdump_time.tm_min, sfdump_time.tm_sec); + if (0 == mkdir(sfdumpdir_raw, 0777)) + sfdump_counter_raw = 0; + else + LOGE("sfdump: Error: %s. Failed to create sfdump directory" + ": %s", strerror(errno), sfdumpdir_raw); + } + } + + if (sfdump_counter_raw <= sfdump_countlimit_raw) + sfdump_counter_raw++; + + bDumpLayer = (sfdump_countlimit_png || sfdump_countlimit_raw)? true : false; + return bDumpLayer; +} + +inline void getHalPixelFormatStr(int format, char pixelformatstr[]) +{ + if (!pixelformatstr) + return; + + switch(format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + strcpy(pixelformatstr, "RGBA_8888"); + break; + case HAL_PIXEL_FORMAT_RGBX_8888: + strcpy(pixelformatstr, "RGBX_8888"); + break; + case HAL_PIXEL_FORMAT_RGB_888: + strcpy(pixelformatstr, "RGB_888"); + break; + case HAL_PIXEL_FORMAT_RGB_565: + strcpy(pixelformatstr, "RGB_565"); + break; + case HAL_PIXEL_FORMAT_BGRA_8888: + strcpy(pixelformatstr, "BGRA_8888"); + break; + case HAL_PIXEL_FORMAT_RGBA_5551: + strcpy(pixelformatstr, "RGBA_5551"); + break; + case HAL_PIXEL_FORMAT_RGBA_4444: + strcpy(pixelformatstr, "RGBA_4444"); + break; + case HAL_PIXEL_FORMAT_YV12: + strcpy(pixelformatstr, "YV12"); + break; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + strcpy(pixelformatstr, "YCbCr_422_SP_NV16"); + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + strcpy(pixelformatstr, "YCrCb_420_SP_NV21"); + break; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + strcpy(pixelformatstr, "YCbCr_422_I_YUY2"); + break; + case HAL_PIXEL_FORMAT_NV12_ENCODEABLE: + strcpy(pixelformatstr, "NV12_ENCODEABLE"); + break; + case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED: + strcpy(pixelformatstr, "YCbCr_420_SP_TILED_TILE_4x2"); + break; + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + strcpy(pixelformatstr, "YCbCr_420_SP"); + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP_ADRENO: + strcpy(pixelformatstr, "YCrCb_420_SP_ADRENO"); + break; + case HAL_PIXEL_FORMAT_YCrCb_422_SP: + strcpy(pixelformatstr, "YCrCb_422_SP"); + break; + case HAL_PIXEL_FORMAT_R_8: + strcpy(pixelformatstr, "R_8"); + break; + case HAL_PIXEL_FORMAT_RG_88: + strcpy(pixelformatstr, "RG_88"); + break; + case HAL_PIXEL_FORMAT_INTERLACE: + strcpy(pixelformatstr, "INTERLACE"); + break; + default: + sprintf(pixelformatstr, "Unknown0x%X", format); + break; + } +} + +void dumpLayer(int moduleCompositionType, int listFlags, size_t layerIndex, + hwc_layer_t hwLayers[]) +{ + char dumplogstr_png[128] = ""; + char dumplogstr_raw[128] = ""; + if (sfdump_counter_png <= sfdump_countlimit_png) { + sprintf(dumplogstr_png, "[png-dump-frame: %03d of %03d] ", + sfdump_counter_png, sfdump_countlimit_png); + } + if (sfdump_counter_raw <= sfdump_countlimit_raw) { + sprintf(dumplogstr_raw, "[raw-dump-frame: %03d of %03d]", + sfdump_counter_raw, sfdump_countlimit_raw); + } + if (NULL == hwLayers) { + LOGE("sfdump: Error.%s%sLayer[%d] No hwLayers to dump.", + dumplogstr_raw, dumplogstr_png, layerIndex); + return; + } + hwc_layer *layer = &hwLayers[layerIndex]; + hwc_rect_t sourceCrop = layer->sourceCrop; + hwc_rect_t displayFrame = layer->displayFrame; + private_handle_t *hnd = (private_handle_t *)layer->handle; + char pixelformatstr[32] = "None"; + + if (hnd) + getHalPixelFormatStr(hnd->format, pixelformatstr); + + LOGE("sfdump: %s%s[%s]-Composition, Layer[%d] SrcBuff[%dx%d] " + "SrcCrop[%dl, %dt, %dr, %db] " + "DispFrame[%dl, %dt, %dr, %db] Composition-type = %s, Format = %s, " + "Orientation = %s, Flags = %s%s%s%s%s%s%s%s%s%s", + dumplogstr_raw, dumplogstr_png, + (moduleCompositionType == COMPOSITION_TYPE_GPU)? "GPU": + (moduleCompositionType == COMPOSITION_TYPE_MDP)? "MDP": + (moduleCompositionType == COMPOSITION_TYPE_C2D)? "C2D": + (moduleCompositionType == COMPOSITION_TYPE_CPU)? "CPU": + (moduleCompositionType == COMPOSITION_TYPE_DYN)? "DYN": "???", + layerIndex, + (hnd)? hnd->width : -1, (hnd)? hnd->height : -1, + sourceCrop.left, sourceCrop.top, + sourceCrop.right, sourceCrop.bottom, + displayFrame.left, displayFrame.top, + displayFrame.right, displayFrame.bottom, + (layer->compositionType == HWC_FRAMEBUFFER)? "Framebuffer (OpenGL ES)": + (layer->compositionType == HWC_OVERLAY)? "Overlay": + (layer->compositionType == HWC_USE_COPYBIT)? "Copybit": "???", + pixelformatstr, + (layer->transform == Transform::ROT_0)? "ROT_0": + (layer->transform == Transform::FLIP_H)? "FLIP_H": + (layer->transform == Transform::FLIP_V)? "FLIP_V": + (layer->transform == Transform::ROT_90)? "ROT_90": + (layer->transform == Transform::ROT_180)? "ROT_180": + (layer->transform == Transform::ROT_270)? "ROT_270": + (layer->transform == Transform::ROT_INVALID)? "ROT_INVALID":"???", + (layer->flags == 0)? "[None]":"", + (layer->flags & HWC_SKIP_LAYER)? "[Skip layer]":"", + (layer->flags & HWC_LAYER_NOT_UPDATING)? "[Layer not updating]":"", + (layer->flags & HWC_USE_ORIGINAL_RESOLUTION)? "[Original Resolution]":"", + (layer->flags & HWC_DO_NOT_USE_OVERLAY)? "[Do not use Overlay]":"", + (layer->flags & HWC_COMP_BYPASS)? "[Bypass]":"", + (layer->flags & HWC_BYPASS_RESERVE_0)? "[Bypass Reserve 0]":"", + (layer->flags & HWC_BYPASS_RESERVE_1)? "[Bypass Reserve 1]":"", + (listFlags & HWC_GEOMETRY_CHANGED)? "[List: Geometry Changed]":"", + (listFlags & HWC_SKIP_COMPOSITION)? "[List: Skip Composition]":""); + + if (NULL == hnd) { + LOGE("sfdump: %s%sLayer[%d] private-handle is invalid.", + dumplogstr_raw, dumplogstr_png, layerIndex); + return; + } + + if ((sfdump_counter_png <= sfdump_countlimit_png) && hnd->base) { + bool bResult = false; + char sfdumpfile_name[256]; + SkBitmap *tempSkBmp = new SkBitmap(); + SkBitmap::Config tempSkBmpConfig = SkBitmap::kNo_Config; + sprintf(sfdumpfile_name, "%s/sfdump%03d_layer%d.png", sfdumpdir_png, + sfdump_counter_png, layerIndex); + + switch (hnd->format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + tempSkBmpConfig = SkBitmap::kARGB_8888_Config; + break; + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBA_5551: + case HAL_PIXEL_FORMAT_RGBA_4444: + tempSkBmpConfig = SkBitmap::kRGB_565_Config; + break; + case HAL_PIXEL_FORMAT_RGB_888: + default: + tempSkBmpConfig = SkBitmap::kNo_Config; + break; + } + if (SkBitmap::kNo_Config != tempSkBmpConfig) { + tempSkBmp->setConfig(tempSkBmpConfig, hnd->width, hnd->height); + tempSkBmp->setPixels((void*)hnd->base); + bResult = SkImageEncoder::EncodeFile(sfdumpfile_name, + *tempSkBmp, SkImageEncoder::kPNG_Type, 100); + LOGE("sfdump: %sDumped Layer[%d] to %s: %s", dumplogstr_png, + layerIndex, sfdumpfile_name, bResult ? "Success" : "Fail"); + } + else { + LOGE("sfdump: %sSkipping Layer[%d] dump: Unsupported layer " + "format %s for png encoder.", dumplogstr_png, layerIndex, + pixelformatstr); + } + delete tempSkBmp; // Calls SkBitmap::freePixels() internally. + } + + if ((sfdump_counter_raw <= sfdump_countlimit_raw) && hnd->base) { + char sfdumpfile_name[256]; + bool bResult = false; + sprintf(sfdumpfile_name, "%s/sfdump%03d_layer%d_%dx%d_%s.raw", + sfdumpdir_raw, + sfdump_counter_raw, layerIndex, hnd->width, hnd->height, + pixelformatstr); + FILE* fp = fopen(sfdumpfile_name, "w+"); + if (fp != NULL) { + bResult = (bool) fwrite((void*)hnd->base, hnd->size, 1, fp); + fclose(fp); + } + LOGE("sfdump: %s Dumped Layer[%d] to %s: %s", dumplogstr_raw, + layerIndex, sfdumpfile_name, bResult ? "Success" : "Fail"); + } +} + #ifdef DEBUG_CALC_FPS ANDROID_SINGLETON_STATIC_INSTANCE(CalcFps) ; diff --git a/libqcomui/qcom_ui.h b/libqcomui/qcom_ui.h index b1fe853..258ff27 100644 --- a/libqcomui/qcom_ui.h +++ b/libqcomui/qcom_ui.h @@ -36,6 +36,7 @@ #include #include #include +#include #include "../libgralloc/gralloc_priv.h" using namespace android; @@ -364,4 +365,38 @@ int qcomuiClearRegion(Region region, EGLDisplay dpy, EGLSurface sur); */ external_display handleEventHDMI(external_display newEvent, external_display currEvent); + +/* + * Checks if layers need to be dumped based on system property "debug.sf.dump" + * for raw dumps and "debug.sf.dump.png" for png dumps. + * + * For example, to dump 25 frames in raw format, do, + * adb shell setprop debug.sf.dump 25 + * Layers are dumped in a time-stamped location: /data/sfdump*. + * + * To dump 10 frames in png format, do, + * adb shell setprop debug.sf.dump.png 10 + * To dump another 25 or so frames in raw format, do, + * adb shell setprop debug.sf.dump 26 + * + * To turn off logcat logging of layer-info, set both properties to 0, + * adb shell setprop debug.sf.dump.png 0 + * adb shell setprop debug.sf.dump 0 + * + * @return: true if layers need to be dumped (or logcat-ed). + */ +bool needToDumpLayers(); + +/* + * Dumps a layer's info into logcat and its buffer into raw/png files. + * + * @param: moduleCompositionType - Composition type set in hwcomposer module. + * @param: listFlags - Flags used in hwcomposer's list. + * @param: layerIndex - Index of layer being dumped. + * @param: hwLayers - Address of hwc_layer_t to log and dump. + * + */ +void dumpLayer(int moduleCompositionType, int listFlags, size_t layerIndex, + hwc_layer_t hwLayers[]); + #endif // INCLUDE_LIBQCOM_UI