Update the overlay when we have a channel up. Previously, we did updates only when the width and height changed. Now, we always update the overlay when the channel is open, and not close the channel to update parameters such as orientation change, flags etc. Change-Id: I0b2ef8b19bd860d5361c391e040ff497f1023ae3
967 lines
31 KiB
C++
Executable File
967 lines
31 KiB
C++
Executable File
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <hardware/hardware.h>
|
|
#include <hardware/overlay.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
|
|
#include <cutils/log.h>
|
|
#include <cutils/atomic.h>
|
|
#include <cutils/properties.h>
|
|
|
|
#include <hardware/hwcomposer.h>
|
|
#include <hardware/overlay.h>
|
|
#include <hardware/copybit.h>
|
|
#include <overlayLib.h>
|
|
#include <overlayLibUI.h>
|
|
|
|
#include <EGL/egl.h>
|
|
#include <EGL/eglext.h>
|
|
#include <ui/android_native_buffer.h>
|
|
#include <gralloc_priv.h>
|
|
|
|
/*****************************************************************************/
|
|
#define ALIGN(x, align) (((x) + ((align)-1)) & ~((align)-1))
|
|
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
|
|
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
|
|
|
|
// Enum containing the supported composition types
|
|
enum {
|
|
COMPOSITION_TYPE_GPU = 0,
|
|
COMPOSITION_TYPE_MDP = 0x1,
|
|
COMPOSITION_TYPE_C2D = 0x2,
|
|
COMPOSITION_TYPE_CPU = 0x4,
|
|
COMPOSITION_TYPE_DYN = 0x8
|
|
};
|
|
|
|
enum HWCCompositionType {
|
|
HWC_USE_GPU, // This layer is to be handled by Surfaceflinger
|
|
HWC_USE_OVERLAY, // This layer is to be handled by the overlay
|
|
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,
|
|
HWC_S3D_LAYER = 0x4,
|
|
HWC_STOP_UI_MIRRORING_MASK = 0xF
|
|
};
|
|
|
|
#ifdef COMPOSITION_BYPASS
|
|
enum BypassState {
|
|
BYPASS_ON,
|
|
BYPASS_OFF,
|
|
BYPASS_OFF_PENDING,
|
|
};
|
|
|
|
enum {
|
|
MAX_BYPASS_LAYERS = 2,
|
|
ANIM_FRAME_COUNT = 30,
|
|
};
|
|
#endif
|
|
|
|
struct hwc_context_t {
|
|
hwc_composer_device_t device;
|
|
/* our private state goes below here */
|
|
overlay::Overlay* mOverlayLibObject;
|
|
#ifdef COMPOSITION_BYPASS
|
|
overlay::OverlayUI* mOvUI[MAX_BYPASS_LAYERS];
|
|
int animCount;
|
|
BypassState bypassState;
|
|
#endif
|
|
#if defined HDMI_DUAL_DISPLAY
|
|
bool mHDMIEnabled;
|
|
bool pendingHDMI;
|
|
#endif
|
|
int previousLayerCount;
|
|
};
|
|
|
|
static int hwc_device_open(const struct hw_module_t* module, const char* name,
|
|
struct hw_device_t** device);
|
|
|
|
static struct hw_module_methods_t hwc_module_methods = {
|
|
open: hwc_device_open
|
|
};
|
|
|
|
|
|
struct private_hwc_module_t {
|
|
hwc_module_t base;
|
|
overlay_control_device_t *overlayEngine;
|
|
copybit_device_t *copybitEngine;
|
|
framebuffer_device_t *fbDevice;
|
|
int compositionType;
|
|
bool isBypassEnabled; //from build.prop debug.compbypass.enable
|
|
};
|
|
|
|
struct private_hwc_module_t HAL_MODULE_INFO_SYM = {
|
|
base: {
|
|
common: {
|
|
tag: HARDWARE_MODULE_TAG,
|
|
version_major: 1,
|
|
version_minor: 0,
|
|
id: HWC_HARDWARE_MODULE_ID,
|
|
name: "Hardware Composer Module",
|
|
author: "The Android Open Source Project",
|
|
methods: &hwc_module_methods,
|
|
}
|
|
},
|
|
overlayEngine: NULL,
|
|
copybitEngine: NULL,
|
|
fbDevice: NULL,
|
|
compositionType: 0,
|
|
isBypassEnabled: false,
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void dump_layer(hwc_layer_t const* l) {
|
|
LOGD("\ttype=%d, flags=%08x, handle=%p, tr=%02x, blend=%04x, {%d,%d,%d,%d}, {%d,%d,%d,%d}",
|
|
l->compositionType, l->flags, l->handle, l->transform, l->blending,
|
|
l->sourceCrop.left,
|
|
l->sourceCrop.top,
|
|
l->sourceCrop.right,
|
|
l->sourceCrop.bottom,
|
|
l->displayFrame.left,
|
|
l->displayFrame.top,
|
|
l->displayFrame.right,
|
|
l->displayFrame.bottom);
|
|
}
|
|
|
|
static int hwc_updateOverlayStatus(hwc_context_t* ctx, int layerType) {
|
|
#if defined HDMI_DUAL_DISPLAY
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
|
|
ctx->device.common.module);
|
|
overlay::Overlay *ovLibObject = ctx->mOverlayLibObject;
|
|
if(!hwcModule || !ovLibObject) {
|
|
LOGE("hwc_set_hdmi_status invalid params");
|
|
return -1;
|
|
}
|
|
|
|
framebuffer_device_t *fbDev = hwcModule->fbDevice;
|
|
if (!fbDev) {
|
|
LOGE("hwc_set_hdmi_status fbDev is NULL");
|
|
return -1;
|
|
}
|
|
|
|
if (layerType & HWC_STOP_UI_MIRRORING_MASK) {
|
|
// Inform the gralloc to stop UI mirroring
|
|
fbDev->videoOverlayStarted(fbDev, true);
|
|
} else {
|
|
// Video mirroring is going on, and we do not have any layers to
|
|
// mirror directly. Close the current video channel and inform the
|
|
// gralloc to start UI mirroring
|
|
ovLibObject->closeChannel();
|
|
fbDev->videoOverlayStarted(fbDev, false);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Configures mdp pipes
|
|
*/
|
|
static int prepareOverlay(hwc_context_t *ctx, hwc_layer_t *layer) {
|
|
int ret = 0;
|
|
if (LIKELY(ctx && ctx->mOverlayLibObject)) {
|
|
private_hwc_module_t* hwcModule =
|
|
reinterpret_cast<private_hwc_module_t*>(ctx->device.common.module);
|
|
if (UNLIKELY(!hwcModule)) {
|
|
LOGE("prepareOverlay null module ");
|
|
return -1;
|
|
}
|
|
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
overlay::Overlay *ovLibObject = ctx->mOverlayLibObject;
|
|
overlay_buffer_info info;
|
|
info.width = hnd->width;
|
|
info.height = hnd->height;
|
|
info.format = hnd->format;
|
|
info.size = hnd->size;
|
|
|
|
ret = ovLibObject->setSource(info, layer->transform,
|
|
(ovLibObject->getHDMIStatus()?true:false), false);
|
|
if (!ret) {
|
|
LOGE("prepareOverlay setSource failed");
|
|
return -1;
|
|
}
|
|
|
|
ret = ovLibObject->setParameter(OVERLAY_TRANSFORM, layer->transform);
|
|
if (!ret) {
|
|
LOGE("prepareOverlay setParameter failed transform %x",
|
|
layer->transform);
|
|
return -1;
|
|
}
|
|
|
|
hwc_rect_t sourceCrop = layer->sourceCrop;
|
|
ret = ovLibObject->setCrop(sourceCrop.left, sourceCrop.top,
|
|
(sourceCrop.right - sourceCrop.left),
|
|
(sourceCrop.bottom - sourceCrop.top));
|
|
if (!ret) {
|
|
LOGE("prepareOverlay setCrop failed");
|
|
return -1;
|
|
}
|
|
|
|
if (layer->flags == HWC_USE_ORIGINAL_RESOLUTION) {
|
|
framebuffer_device_t* fbDev = hwcModule->fbDevice;
|
|
ret = ovLibObject->setPosition(0, 0,
|
|
fbDev->width, fbDev->height);
|
|
} else {
|
|
hwc_rect_t displayFrame = layer->displayFrame;
|
|
ret = ovLibObject->setPosition(displayFrame.left, displayFrame.top,
|
|
(displayFrame.right - displayFrame.left),
|
|
(displayFrame.bottom - displayFrame.top));
|
|
}
|
|
if (!ret) {
|
|
LOGE("prepareOverlay setPosition failed");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool canSkipComposition(hwc_context_t* ctx, int yuvBufferCount, int currentLayerCount,
|
|
int numLayersNotUpdating)
|
|
{
|
|
if (!ctx) {
|
|
LOGE("canSkipComposition invalid context");
|
|
return false;
|
|
}
|
|
|
|
bool compCountChanged = false;
|
|
if (yuvBufferCount == 1) {
|
|
if (currentLayerCount != ctx->previousLayerCount) {
|
|
compCountChanged = true;
|
|
ctx->previousLayerCount = currentLayerCount;
|
|
}
|
|
|
|
if (!compCountChanged) {
|
|
if ((currentLayerCount == 1) ||
|
|
((currentLayerCount-1) == numLayersNotUpdating)) {
|
|
// We either have only one overlay layer or we have
|
|
// all the non-UI layers not updating. In this case
|
|
// we can skip the composition of the UI layers.
|
|
return true;
|
|
}
|
|
}
|
|
} else {
|
|
ctx->previousLayerCount = -1;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool isFullScreenUpdate(const framebuffer_device_t* fbDev, const hwc_layer_list_t* list) {
|
|
|
|
if(!fbDev) {
|
|
LOGE("ERROR: %s : fb device is invalid",__func__);
|
|
return false;
|
|
}
|
|
|
|
int fb_w = fbDev->width;
|
|
int fb_h = fbDev->height;
|
|
|
|
/*
|
|
* We have full screen condition when
|
|
* 1. We have 1 layer to compose
|
|
* a. layers dest rect equals display resolution.
|
|
* 2. We have 2 layers to compose
|
|
* a. Sum of their dest rects equals display resolution.
|
|
*/
|
|
|
|
if(list->numHwLayers == 1)
|
|
{
|
|
hwc_rect_t rect = list->hwLayers[0].displayFrame;
|
|
|
|
int w = rect.right - rect.left;
|
|
int h = rect.bottom - rect.top;
|
|
|
|
int transform = list->hwLayers[0].transform;
|
|
|
|
if(transform & (HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_ROT_270))
|
|
return ((fb_w == h) && (fb_h == w));
|
|
else
|
|
return ((fb_h == h) && (fb_w == w));
|
|
}
|
|
|
|
if(list->numHwLayers == 2) {
|
|
|
|
hwc_rect_t rect_1 = list->hwLayers[0].displayFrame;
|
|
hwc_rect_t rect_2 = list->hwLayers[1].displayFrame;
|
|
|
|
int transform_1 = list->hwLayers[0].transform;
|
|
int transform_2 = list->hwLayers[1].transform;
|
|
|
|
int w1 = rect_1.right - rect_1.left;
|
|
int h1 = rect_1.bottom - rect_1.top;
|
|
int w2 = rect_2.right - rect_2.left;
|
|
int h2 = rect_2.bottom - rect_2.top;
|
|
|
|
if(transform_1 == transform_2) {
|
|
if(transform_1 & (HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_ROT_270)) {
|
|
if((fb_w == (w1 + w2)) && (fb_h == h1) && (fb_h == h2))
|
|
return true;
|
|
} else {
|
|
if((fb_w == w1) && (fb_w == w2) && (fb_h == (h1 + h2)))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#ifdef COMPOSITION_BYPASS
|
|
/*
|
|
* Configures pipe(s) for composition bypass
|
|
*/
|
|
static int prepareBypass(hwc_context_t *ctx, hwc_layer_t *layer, int index,
|
|
int lastLayerIndex) {
|
|
if (ctx && ctx->mOvUI[index]) {
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<
|
|
private_hwc_module_t*>(ctx->device.common.module);
|
|
if (!hwcModule) {
|
|
LOGE("prepareBypass null module ");
|
|
return -1;
|
|
}
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
if(!hnd) {
|
|
LOGE("prepareBypass handle null");
|
|
return -1;
|
|
}
|
|
if(hnd->width > hwcModule->fbDevice->width ||
|
|
hnd->height > hwcModule->fbDevice->height) {
|
|
ctx->animCount = ANIM_FRAME_COUNT;
|
|
return -1;
|
|
}
|
|
overlay::OverlayUI *ovUI = ctx->mOvUI[index];
|
|
int ret = 0;
|
|
int orientation = layer->transform;
|
|
overlay_buffer_info info;
|
|
info.width = hnd->width;
|
|
info.height = hnd->height;
|
|
info.format = hnd->format;
|
|
info.size = hnd->size;
|
|
const bool useVGPipe = true;
|
|
//only last layer should wait for vsync
|
|
const bool waitForVsync = (index == lastLayerIndex);
|
|
const int fbnum = 0;
|
|
//Just to differentiate zorders for different layers
|
|
const int zorder = index;
|
|
ret = ovUI->setSource(info, orientation, useVGPipe, waitForVsync,
|
|
fbnum, zorder);
|
|
if (ret) {
|
|
LOGE("prepareBypass setSource failed");
|
|
return -1;
|
|
}
|
|
hwc_rect_t displayFrame = layer->displayFrame;
|
|
ret = ovUI->setPosition(displayFrame.left, displayFrame.top,
|
|
(displayFrame.right - displayFrame.left),
|
|
(displayFrame.bottom - displayFrame.top));
|
|
if (ret) {
|
|
LOGE("prepareBypass setPosition failed");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int drawLayerUsingBypass(hwc_context_t *ctx, hwc_layer_t *layer,
|
|
int index) {
|
|
if (ctx && ctx->mOvUI[index]) {
|
|
overlay::OverlayUI *ovUI = ctx->mOvUI[index];
|
|
int ret = 0;
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
ret = ovUI->queueBuffer(hnd);
|
|
if (ret) {
|
|
LOGE("drawLayerUsingBypass queueBuffer failed");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Checks if doing comp. bypass is possible. If video is not on and there
|
|
* are 2 layers then its doable.
|
|
*/
|
|
inline static bool isBypassDoable(hwc_composer_device_t *dev, const int yuvCount,
|
|
const int numLayers) {
|
|
hwc_context_t* ctx = (hwc_context_t*)(dev);
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
|
|
dev->common.module);
|
|
//Check if enabled in build.prop
|
|
if(hwcModule->isBypassEnabled == false) {
|
|
return false;
|
|
}
|
|
//Disable bypass during animation
|
|
if(UNLIKELY(ctx->animCount)) {
|
|
--(ctx->animCount);
|
|
return false;
|
|
}
|
|
#if defined HDMI_DUAL_DISPLAY
|
|
//Disable bypass when HDMI is enabled
|
|
if(ctx->mHDMIEnabled || ctx->pendingHDMI) {
|
|
return false;
|
|
}
|
|
#endif
|
|
return (yuvCount == 0) && (numLayers == MAX_BYPASS_LAYERS);
|
|
}
|
|
|
|
/*
|
|
* Bypass is not efficient if area is greater than 1280x720
|
|
* AND rotation is necessary, since the rotator consumes
|
|
* time greater than 1 Vsync and is sequential.
|
|
*/
|
|
inline static bool isBypassEfficient(const framebuffer_device_t* fbDev,
|
|
const hwc_layer_list_t* list, hwc_context_t* ctx) {
|
|
bool rotationNeeded = false;
|
|
for(int i = 0; i < list->numHwLayers; ++i) {
|
|
if(list->hwLayers[i].transform) {
|
|
rotationNeeded = true;
|
|
break;
|
|
}
|
|
}
|
|
return !(rotationNeeded);
|
|
}
|
|
|
|
bool setupBypass(hwc_context_t* ctx, hwc_layer_list_t* list) {
|
|
for (int index = 0 ; index < list->numHwLayers; index++) {
|
|
if(prepareBypass(ctx, &(list->hwLayers[index]), index,
|
|
list->numHwLayers - 1) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void setBypassLayerFlags(hwc_context_t* ctx, hwc_layer_list_t* list) {
|
|
for (int index = 0 ; index < list->numHwLayers; index++) {
|
|
list->hwLayers[index].flags = HWC_COMP_BYPASS;
|
|
list->hwLayers[index].compositionType = HWC_USE_OVERLAY;
|
|
#ifdef DEBUG
|
|
LOGE("%s: layer = %d", __FUNCTION__, index);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void unsetBypassLayerFlags(hwc_layer_list_t* list) {
|
|
for (int index = 0 ; index < list->numHwLayers; index++) {
|
|
if(list->hwLayers[index].flags == HWC_COMP_BYPASS) {
|
|
list->hwLayers[index].flags = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void closeBypass(hwc_context_t* ctx) {
|
|
for (int index = 0 ; index < MAX_BYPASS_LAYERS; index++) {
|
|
ctx->mOvUI[index]->closeChannel();
|
|
#ifdef DEBUG
|
|
LOGE("%s", __FUNCTION__);
|
|
#endif
|
|
}
|
|
}
|
|
#endif //COMPOSITION_BYPASS
|
|
|
|
|
|
static void handleHDMIStateChange(hwc_composer_device_t *dev) {
|
|
#if defined HDMI_DUAL_DISPLAY
|
|
hwc_context_t* ctx = (hwc_context_t*)(dev);
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
|
|
dev->common.module);
|
|
framebuffer_device_t *fbDev = hwcModule->fbDevice;
|
|
if (fbDev) {
|
|
fbDev->enableHDMIOutput(fbDev, ctx->mHDMIEnabled);
|
|
}
|
|
|
|
if(ctx && ctx->mOverlayLibObject) {
|
|
overlay::Overlay *ovLibObject = ctx->mOverlayLibObject;
|
|
ovLibObject->setHDMIStatus(ctx->mHDMIEnabled);
|
|
if (!(ctx->mHDMIEnabled)) {
|
|
// Close the overlay channels if HDMI is disconnected
|
|
ovLibObject->closeChannel();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/* Just mark flags and do stuff after eglSwapBuffers */
|
|
static void hwc_enableHDMIOutput(hwc_composer_device_t *dev, bool enable) {
|
|
#if defined HDMI_DUAL_DISPLAY
|
|
hwc_context_t* ctx = (hwc_context_t*)(dev);
|
|
ctx->mHDMIEnabled = enable;
|
|
if(enable) { //On connect, allow bypass to draw once to FB
|
|
ctx->pendingHDMI = true;
|
|
} else { //On disconnect, close immediately (there will be no bypass)
|
|
handleHDMIStateChange(dev);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
|
|
dev->common.module);
|
|
if(!hwcModule) {
|
|
LOGE("hwc_prepare null module ");
|
|
return -1;
|
|
}
|
|
|
|
int yuvBufferCount = 0;
|
|
int layerType = 0;
|
|
int numLayersNotUpdating = 0;
|
|
bool fullscreen = false;
|
|
|
|
if (list) {
|
|
fullscreen = isFullScreenUpdate(hwcModule->fbDevice, list);
|
|
|
|
for (size_t i=0 ; i<list->numHwLayers; i++) {
|
|
private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle;
|
|
if(hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) &&
|
|
!(list->hwLayers[i].flags & HWC_DO_NOT_USE_OVERLAY)) {
|
|
yuvBufferCount++;
|
|
if (yuvBufferCount > 1) {
|
|
break;
|
|
}
|
|
} else if (list->hwLayers[i].flags & HWC_LAYER_NOT_UPDATING) {
|
|
numLayersNotUpdating++;
|
|
}
|
|
}
|
|
|
|
if (list->flags & HWC_GEOMETRY_CHANGED) {
|
|
layerType |= (yuvBufferCount == 1) ? HWC_SINGLE_VIDEO: 0;
|
|
// Inform the gralloc of the current HDMI status
|
|
hwc_updateOverlayStatus(ctx, layerType);
|
|
}
|
|
|
|
for (size_t i=0 ; i<list->numHwLayers ; i++) {
|
|
private_handle_t *hnd = (private_handle_t *)list->hwLayers[i].handle;
|
|
// If there is a single Fullscreen layer, we can bypass it - TBD
|
|
// If there is only one video/camera buffer, we can bypass itn
|
|
if (hnd && (hnd->bufferType == BUFFER_TYPE_VIDEO) && (yuvBufferCount == 1)) {
|
|
if(prepareOverlay(ctx, &(list->hwLayers[i])) == 0) {
|
|
list->hwLayers[i].compositionType = HWC_USE_OVERLAY;
|
|
list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB;
|
|
} else if (hwcModule->compositionType & (COMPOSITION_TYPE_C2D)) {
|
|
//Fail safe path: If drawing with overlay fails,
|
|
//Use C2D if available.
|
|
list->hwLayers[i].compositionType = HWC_USE_COPYBIT;
|
|
yuvBufferCount = 0;
|
|
} else {
|
|
//If C2D is not enabled fall back to GPU.
|
|
list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
|
|
yuvBufferCount = 0;
|
|
}
|
|
} else if (list->hwLayers[i].flags == HWC_USE_ORIGINAL_RESOLUTION) {
|
|
list->hwLayers[i].compositionType = HWC_USE_OVERLAY;
|
|
list->hwLayers[i].hints |= HWC_HINT_CLEAR_FB;
|
|
layerType |= HWC_ORIG_RESOLUTION;
|
|
} else if (hnd && (hwcModule->compositionType &
|
|
(COMPOSITION_TYPE_C2D|COMPOSITION_TYPE_MDP))) {
|
|
list->hwLayers[i].compositionType = HWC_USE_COPYBIT;
|
|
} else if ((hwcModule->compositionType == COMPOSITION_TYPE_DYN)
|
|
&& fullscreen) {
|
|
list->hwLayers[i].compositionType = HWC_USE_COPYBIT;
|
|
} else {
|
|
list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
|
|
}
|
|
}
|
|
|
|
if (canSkipComposition(ctx, yuvBufferCount, list->numHwLayers,
|
|
numLayersNotUpdating)) {
|
|
list->flags |= HWC_SKIP_COMPOSITION;
|
|
} else {
|
|
list->flags &= ~HWC_SKIP_COMPOSITION;
|
|
}
|
|
|
|
#ifdef COMPOSITION_BYPASS
|
|
//Check if bypass is feasible
|
|
if(isBypassDoable(dev, yuvBufferCount, list->numHwLayers) &&
|
|
isBypassEfficient(hwcModule->fbDevice, list, ctx)) {
|
|
//Setup bypass
|
|
if(setupBypass(ctx, list)) {
|
|
//Overwrite layer flags only if setup succeeds.
|
|
setBypassLayerFlags(ctx, list);
|
|
list->flags |= HWC_SKIP_COMPOSITION;
|
|
ctx->bypassState = BYPASS_ON;
|
|
}
|
|
} else {
|
|
unsetBypassLayerFlags(list);
|
|
if(ctx->bypassState == BYPASS_ON) {
|
|
ctx->bypassState = BYPASS_OFF_PENDING;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
struct range {
|
|
int current;
|
|
int end;
|
|
};
|
|
struct region_iterator : public copybit_region_t {
|
|
|
|
region_iterator(hwc_region_t region) {
|
|
mRegion = region;
|
|
r.end = region.numRects;
|
|
r.current = 0;
|
|
this->next = iterate;
|
|
}
|
|
|
|
private:
|
|
static int iterate(copybit_region_t const * self, copybit_rect_t* rect) {
|
|
if (!self || !rect) {
|
|
LOGE("iterate invalid parameters");
|
|
return 0;
|
|
}
|
|
|
|
region_iterator const* me = static_cast<region_iterator const*>(self);
|
|
if (me->r.current != me->r.end) {
|
|
rect->l = me->mRegion.rects[me->r.current].left;
|
|
rect->t = me->mRegion.rects[me->r.current].top;
|
|
rect->r = me->mRegion.rects[me->r.current].right;
|
|
rect->b = me->mRegion.rects[me->r.current].bottom;
|
|
me->r.current++;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
hwc_region_t mRegion;
|
|
mutable range r;
|
|
};
|
|
|
|
|
|
static int drawLayerUsingCopybit(hwc_composer_device_t *dev, hwc_layer_t *layer, EGLDisplay dpy,
|
|
EGLSurface surface)
|
|
{
|
|
hwc_context_t* ctx = (hwc_context_t*)(dev);
|
|
if(!ctx) {
|
|
LOGE("drawLayerUsingCopybit null context ");
|
|
return -1;
|
|
}
|
|
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(dev->common.module);
|
|
if(!hwcModule) {
|
|
LOGE("drawLayerUsingCopybit null module ");
|
|
return -1;
|
|
}
|
|
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
if(!hnd) {
|
|
LOGE("drawLayerUsingCopybit invalid handle");
|
|
return -1;
|
|
}
|
|
|
|
// Set the copybit source:
|
|
copybit_image_t src;
|
|
src.w = ALIGN(hnd->width, 32);
|
|
src.h = hnd->height;
|
|
src.format = hnd->format;
|
|
src.base = (void *)hnd->base;
|
|
src.handle = (native_handle_t *)layer->handle;
|
|
|
|
// Copybit source rect
|
|
hwc_rect_t sourceCrop = layer->sourceCrop;
|
|
copybit_rect_t srcRect = {sourceCrop.left, sourceCrop.top,
|
|
sourceCrop.right,
|
|
sourceCrop.bottom};
|
|
|
|
// Copybit destination rect
|
|
hwc_rect_t displayFrame = layer->displayFrame;
|
|
copybit_rect_t dstRect = {displayFrame.left, displayFrame.top,
|
|
displayFrame.right,
|
|
displayFrame.bottom};
|
|
|
|
// Copybit dst
|
|
copybit_image_t dst;
|
|
android_native_buffer_t *renderBuffer = (android_native_buffer_t *)eglGetRenderBufferANDROID(dpy, surface);
|
|
if (!renderBuffer) {
|
|
LOGE("eglGetRenderBufferANDROID returned NULL buffer");
|
|
return -1;
|
|
}
|
|
private_handle_t *fbHandle = (private_handle_t *)renderBuffer->handle;
|
|
if(!fbHandle) {
|
|
LOGE("Framebuffer handle is NULL");
|
|
return -1;
|
|
}
|
|
dst.w = ALIGN(fbHandle->width,32);
|
|
dst.h = fbHandle->height;
|
|
dst.format = fbHandle->format;
|
|
dst.base = (void *)fbHandle->base;
|
|
dst.handle = (native_handle_t *)renderBuffer->handle;
|
|
|
|
// Copybit region
|
|
hwc_region_t region = layer->visibleRegionScreen;
|
|
region_iterator copybitRegion(region);
|
|
|
|
copybit_device_t *copybit = hwcModule->copybitEngine;
|
|
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);
|
|
|
|
if(err < 0)
|
|
LOGE("copybit stretch failed");
|
|
|
|
return err;
|
|
}
|
|
|
|
static int drawLayerUsingOverlay(hwc_context_t *ctx, hwc_layer_t *layer)
|
|
{
|
|
if (ctx && ctx->mOverlayLibObject) {
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(ctx->device.common.module);
|
|
if (!hwcModule) {
|
|
LOGE("drawLayerUsingLayer null module ");
|
|
return -1;
|
|
}
|
|
private_handle_t *hnd = (private_handle_t *)layer->handle;
|
|
overlay::Overlay *ovLibObject = ctx->mOverlayLibObject;
|
|
int ret = 0;
|
|
|
|
ret = ovLibObject->queueBuffer(hnd);
|
|
if (!ret) {
|
|
LOGE("drawLayerUsingOverlay queueBuffer failed");
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int hwc_set(hwc_composer_device_t *dev,
|
|
hwc_display_t dpy,
|
|
hwc_surface_t sur,
|
|
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;
|
|
}
|
|
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
|
|
dev->common.module);
|
|
if(!hwcModule) {
|
|
LOGE("hwc_set null module ");
|
|
return -1;
|
|
}
|
|
for (size_t i=0; i<list->numHwLayers; i++) {
|
|
if (list->hwLayers[i].flags == HWC_SKIP_LAYER) {
|
|
continue;
|
|
#ifdef COMPOSITION_BYPASS
|
|
} else if (list->hwLayers[i].flags == HWC_COMP_BYPASS) {
|
|
drawLayerUsingBypass(ctx, &(list->hwLayers[i]), i);
|
|
#endif
|
|
} else if (list->hwLayers[i].compositionType == HWC_USE_OVERLAY) {
|
|
drawLayerUsingOverlay(ctx, &(list->hwLayers[i]));
|
|
} else if (list->flags & HWC_SKIP_COMPOSITION) {
|
|
break;
|
|
} else if (list->hwLayers[i].compositionType == HWC_USE_COPYBIT) {
|
|
drawLayerUsingCopybit(dev, &(list->hwLayers[i]), (EGLDisplay)dpy, (EGLSurface)sur);
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
return HWC_EGL_ERROR;
|
|
}
|
|
}
|
|
#ifdef COMPOSITION_BYPASS
|
|
if(ctx->bypassState == BYPASS_OFF_PENDING) {
|
|
closeBypass(ctx);
|
|
ctx->bypassState = BYPASS_OFF;
|
|
}
|
|
#endif
|
|
#if defined HDMI_DUAL_DISPLAY
|
|
if(ctx->pendingHDMI) {
|
|
handleHDMIStateChange(dev);
|
|
ctx->pendingHDMI = false;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int hwc_device_close(struct hw_device_t *dev)
|
|
{
|
|
if(!dev) {
|
|
LOGE("hwc_device_close null device pointer");
|
|
return -1;
|
|
}
|
|
|
|
struct hwc_context_t* ctx = (struct hwc_context_t*)dev;
|
|
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>(
|
|
ctx->device.common.module);
|
|
|
|
// Close the overlay and copybit modules
|
|
if(hwcModule->copybitEngine) {
|
|
copybit_close(hwcModule->copybitEngine);
|
|
hwcModule->copybitEngine = NULL;
|
|
}
|
|
if(hwcModule->overlayEngine) {
|
|
overlay_control_close(hwcModule->overlayEngine);
|
|
hwcModule->overlayEngine = NULL;
|
|
}
|
|
if(hwcModule->fbDevice) {
|
|
framebuffer_close(hwcModule->fbDevice);
|
|
hwcModule->fbDevice = NULL;
|
|
}
|
|
|
|
if (ctx) {
|
|
delete ctx->mOverlayLibObject;
|
|
ctx->mOverlayLibObject = NULL;
|
|
#ifdef COMPOSITION_BYPASS
|
|
for(int i = 0; i < MAX_BYPASS_LAYERS; i++) {
|
|
delete ctx->mOvUI[i];
|
|
}
|
|
#endif
|
|
free(ctx);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
static int hwc_module_initialize(struct private_hwc_module_t* hwcModule)
|
|
{
|
|
|
|
// Open the overlay and copybit modules
|
|
hw_module_t const *module;
|
|
|
|
if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
|
|
copybit_open(module, &(hwcModule->copybitEngine));
|
|
}
|
|
if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
|
|
overlay_control_open(module, &(hwcModule->overlayEngine));
|
|
}
|
|
if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
|
|
framebuffer_open(module, &(hwcModule->fbDevice));
|
|
}
|
|
|
|
// get the current composition type
|
|
char property[PROPERTY_VALUE_MAX];
|
|
if (property_get("debug.sf.hw", property, NULL) > 0) {
|
|
if(atoi(property) == 0) {
|
|
//debug.sf.hw = 0
|
|
hwcModule->compositionType = COMPOSITION_TYPE_CPU;
|
|
} else { //debug.sf.hw = 1
|
|
// Get the composition type
|
|
property_get("debug.composition.type", property, NULL);
|
|
if (property == NULL) {
|
|
hwcModule->compositionType = COMPOSITION_TYPE_GPU;
|
|
} else if ((strncmp(property, "mdp", 3)) == 0) {
|
|
hwcModule->compositionType = COMPOSITION_TYPE_MDP;
|
|
} else if ((strncmp(property, "c2d", 3)) == 0) {
|
|
hwcModule->compositionType = COMPOSITION_TYPE_C2D;
|
|
} else if ((strncmp(property, "dyn", 3)) == 0) {
|
|
hwcModule->compositionType = COMPOSITION_TYPE_DYN;
|
|
} else {
|
|
hwcModule->compositionType = COMPOSITION_TYPE_GPU;
|
|
}
|
|
|
|
if(!hwcModule->copybitEngine)
|
|
hwcModule->compositionType = COMPOSITION_TYPE_GPU;
|
|
}
|
|
} else { //debug.sf.hw is not set. Use cpu composition
|
|
hwcModule->compositionType = COMPOSITION_TYPE_CPU;
|
|
}
|
|
|
|
//Check if composition bypass is enabled
|
|
if(property_get("debug.compbypass.enable", property, NULL) > 0) {
|
|
if(atoi(property) == 1) {
|
|
hwcModule->isBypassEnabled = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hwc_device_open(const struct hw_module_t* module, const char* name,
|
|
struct hw_device_t** device)
|
|
{
|
|
int status = -EINVAL;
|
|
if (!strcmp(name, HWC_HARDWARE_COMPOSER)) {
|
|
private_hwc_module_t* hwcModule = reinterpret_cast<private_hwc_module_t*>
|
|
(const_cast<hw_module_t*>(module));
|
|
|
|
hwc_module_initialize(hwcModule);
|
|
struct hwc_context_t *dev;
|
|
dev = (hwc_context_t*)malloc(sizeof(*dev));
|
|
|
|
/* initialize our state here */
|
|
memset(dev, 0, sizeof(*dev));
|
|
if(hwcModule->overlayEngine) {
|
|
dev->mOverlayLibObject = new overlay::Overlay();
|
|
#ifdef COMPOSITION_BYPASS
|
|
for(int i = 0; i < MAX_BYPASS_LAYERS; i++) {
|
|
dev->mOvUI[i] = new overlay::OverlayUI();
|
|
}
|
|
dev->animCount = 0;
|
|
dev->bypassState = BYPASS_OFF;
|
|
#endif
|
|
} else {
|
|
dev->mOverlayLibObject = NULL;
|
|
#ifdef COMPOSITION_BYPASS
|
|
for(int i = 0; i < MAX_BYPASS_LAYERS; i++) {
|
|
dev->mOvUI[i] = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
#if defined HDMI_DUAL_DISPLAY
|
|
dev->mHDMIEnabled = false;
|
|
dev->pendingHDMI = false;
|
|
#endif
|
|
/* initialize the procs */
|
|
dev->device.common.tag = HARDWARE_DEVICE_TAG;
|
|
dev->device.common.version = 0;
|
|
dev->device.common.module = const_cast<hw_module_t*>(module);
|
|
dev->device.common.close = hwc_device_close;
|
|
|
|
dev->device.prepare = hwc_prepare;
|
|
dev->device.set = hwc_set;
|
|
dev->device.enableHDMIOutput = hwc_enableHDMIOutput;
|
|
*device = &dev->device.common;
|
|
|
|
status = 0;
|
|
}
|
|
return status;
|
|
}
|