* limitations under the License.
*/
+//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <stdint.h>
#include <EGL/egl.h>
#include <cutils/log.h>
+#include <cutils/iosched_policy.h>
#include <cutils/properties.h>
#include <binder/IPCThreadState.h>
#include "EventThread.h"
#include "Layer.h"
#include "LayerDim.h"
+#include "LayerBlur.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
+#include "ExSurfaceFlinger/ExHWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
#include "Effects/Daltonizer.h"
#include "RenderEngine/RenderEngine.h"
#include <cutils/compiler.h>
+#include "DisplayUtils.h"
#define DISPLAY_COUNT 1
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");
+static sp<Layer> lastSurfaceViewLayer;
+
// ---------------------------------------------------------------------------
SurfaceFlinger::SurfaceFlinger()
mDebugInTransaction(0),
mLastTransactionTime(0),
mBootFinished(false),
+ mForceFullDamage(false),
mPrimaryHWVsyncEnabled(false),
mHWVsyncAvailable(false),
mDaltonize(false),
- mHasColorMatrix(false)
+ mHasColorMatrix(false),
+ mHasSecondaryColorMatrix(false),
+ mHasPoweredOff(false),
+ mFrameBuckets(),
+ mTotalTime(0),
+ mLastSwapTime(0),
+ mActiveFrameSequence(0)
{
ALOGI("SurfaceFlinger is starting");
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
mGpuToCpuSupported = !atoi(value);
+ property_get("debug.sf.drop_missed_frames", value, "0");
+ mDropMissedFrames = atoi(value);
+
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
const char* label) :
mValue(0),
- mPhaseOffset(phaseOffset),
mTraceVsync(traceVsync),
mVsyncOnLabel(String8::format("VsyncOn-%s", label)),
mVsyncEventLabel(String8::format("VSYNC-%s", label)),
- mDispSync(dispSync) {}
+ mDispSync(dispSync),
+ mCallbackMutex(),
+ mCallback(),
+ mVsyncMutex(),
+ mPhaseOffset(phaseOffset),
+ mEnabled(false) {}
virtual ~DispSyncSource() {}
virtual void setVSyncEnabled(bool enable) {
- // Do NOT lock the mutex here so as to avoid any mutex ordering issues
- // with locking it in the onDispSyncEvent callback.
+ Mutex::Autolock lock(mVsyncMutex);
if (enable) {
status_t err = mDispSync->addEventListener(mPhaseOffset,
static_cast<DispSync::Callback*>(this));
}
//ATRACE_INT(mVsyncOnLabel.string(), 0);
}
+ mEnabled = enable;
}
virtual void setCallback(const sp<VSyncSource::Callback>& callback) {
- Mutex::Autolock lock(mMutex);
+ Mutex::Autolock lock(mCallbackMutex);
mCallback = callback;
}
+ virtual void setPhaseOffset(nsecs_t phaseOffset) {
+ Mutex::Autolock lock(mVsyncMutex);
+
+ // Normalize phaseOffset to [0, period)
+ auto period = mDispSync->getPeriod();
+ phaseOffset %= period;
+ if (phaseOffset < 0) {
+ // If we're here, then phaseOffset is in (-period, 0). After this
+ // operation, it will be in (0, period)
+ phaseOffset += period;
+ }
+ mPhaseOffset = phaseOffset;
+
+ // If we're not enabled, we don't need to mess with the listeners
+ if (!mEnabled) {
+ return;
+ }
+
+ // Remove the listener with the old offset
+ status_t err = mDispSync->removeEventListener(
+ static_cast<DispSync::Callback*>(this));
+ if (err != NO_ERROR) {
+ ALOGE("error unregistering vsync callback: %s (%d)",
+ strerror(-err), err);
+ }
+
+ // Add a listener with the new offset
+ err = mDispSync->addEventListener(mPhaseOffset,
+ static_cast<DispSync::Callback*>(this));
+ if (err != NO_ERROR) {
+ ALOGE("error registering vsync callback: %s (%d)",
+ strerror(-err), err);
+ }
+ }
+
private:
virtual void onDispSyncEvent(nsecs_t when) {
sp<VSyncSource::Callback> callback;
{
- Mutex::Autolock lock(mMutex);
+ Mutex::Autolock lock(mCallbackMutex);
callback = mCallback;
if (mTraceVsync) {
int mValue;
- const nsecs_t mPhaseOffset;
const bool mTraceVsync;
const String8 mVsyncOnLabel;
const String8 mVsyncEventLabel;
DispSync* mDispSync;
+
+ Mutex mCallbackMutex; // Protects the following
sp<VSyncSource::Callback> mCallback;
- Mutex mMutex;
+
+ Mutex mVsyncMutex; // Protects the following
+ nsecs_t mPhaseOffset;
+ bool mEnabled;
};
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- status_t err;
Mutex::Autolock _l(mStateLock);
// initialize EGL for the default display
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mEGLDisplay, NULL, NULL);
+ // start the EventThread
+ if (vsyncPhaseOffsetNs != sfVsyncPhaseOffsetNs) {
+ sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
+ vsyncPhaseOffsetNs, true, "app");
+ mEventThread = new EventThread(vsyncSrc);
+ sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
+ sfVsyncPhaseOffsetNs, true, "sf");
+ mSFEventThread = new EventThread(sfVsyncSrc);
+ mEventQueue.setEventThread(mSFEventThread);
+ } else {
+ sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
+ vsyncPhaseOffsetNs, true, "sf-app");
+ mEventThread = new EventThread(vsyncSrc);
+ mEventQueue.setEventThread(mEventThread);
+ }
+
// Initialize the H/W composer object. There may or may not be an
// actual hardware composer underneath.
- mHwc = new HWComposer(this,
+ mHwc = DisplayUtils::getInstance()->getHWCInstance(this,
*static_cast<HWComposer::EventHandler *>(this));
// get a RenderEngine for the given display / config (can't fail)
ALOGD("marking display %zu as acquired/unblanked", i);
hw->setPowerMode(HWC_POWER_MODE_NORMAL);
}
+ // When a non-virtual display device is added at boot time,
+ // update the active config by querying HWC otherwise the
+ // default config (config 0) will be used.
+ int activeConfig = mHwc->getActiveConfig(hwcId);
+ if (activeConfig >= 0) {
+ hw->setActiveConfig(activeConfig);
+ }
mDisplays.add(token, hw);
}
}
// (which may happens before we render something)
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
- // start the EventThread
- sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- vsyncPhaseOffsetNs, true, "app");
- mEventThread = new EventThread(vsyncSrc);
- sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
- sfVsyncPhaseOffsetNs, true, "sf");
- mSFEventThread = new EventThread(sfVsyncSrc);
- mEventQueue.setEventThread(mSFEventThread);
-
mEventControlThread = new EventControlThread(this);
mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
+ android_set_rt_ioprio(mEventControlThread->getTid(), 1);
// set a fake vsync period if there is no HWComposer
if (mHwc->initCheck() != NO_ERROR) {
bool SurfaceFlinger::authenticateSurfaceTexture(
const sp<IGraphicBufferProducer>& bufferProducer) const {
Mutex::Autolock _l(mStateLock);
- sp<IBinder> surfaceTextureBinder(bufferProducer->asBinder());
+ sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
return mGraphicBufferProducerList.indexOf(surfaceTextureBinder) >= 0;
}
status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& display,
Vector<DisplayInfo>* configs) {
- if (configs == NULL) {
+ if ((configs == NULL) || (display.get() == NULL)) {
return BAD_VALUE;
}
+ if (!display.get())
+ return NAME_NOT_FOUND;
+
int32_t type = NAME_NOT_FOUND;
for (int i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
if (display == mBuiltinDisplays[i]) {
public:
static int getEmuDensity() {
return getDensityFromProperty("qemu.sf.lcd_density"); }
- static int getBuildDensity() {
- return getDensityFromProperty("ro.sf.lcd_density"); }
+ static int getBuildDensity(const DisplayInfo& info) {
+ static int density = getDensityFromProperty("ro.sf.lcd_density");
+#if defined(__i386__) || defined(__x86_64__)
+ if (density == 0) {
+ uint32_t area = info.w * info.h;
+ if (area <= 800 * 480) {
+ density = 120;
+ } else if (area <= 1024 * 600) {
+ density = 130;
+ } else if (area < 1024 * 768) {
+ density = 140;
+ } else if (area < 1920 * 1080) {
+ density = 160;
+ } else if (area < 2160 * 1440) {
+ density = 240;
+ } else if (area < 2736 * 1824) {
+ density = 280;
+ } else {
+ density = 320;
+ }
+ ALOGI("auto set density to %d", density);
+ }
+#endif
+ return density;
+ }
};
configs->clear();
float xdpi = hwConfig.xdpi;
float ydpi = hwConfig.ydpi;
+ info.w = hwConfig.width;
+ info.h = hwConfig.height;
if (type == DisplayDevice::DISPLAY_PRIMARY) {
// The density of the device is provided by a build property
- float density = Density::getBuildDensity() / 160.0f;
+ float density = Density::getBuildDensity(info) / 160.0f;
if (density == 0) {
// the build doesn't provide a density -- this is wrong!
// use xdpi instead
info.orientation = 0;
}
- info.w = hwConfig.width;
- info.h = hwConfig.height;
- info.xdpi = xdpi;
- info.ydpi = ydpi;
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.sf.hwrotation", value, "0");
+ int additionalRot = atoi(value) / 90;
+ if ((type == DisplayDevice::DISPLAY_PRIMARY) && (additionalRot & DisplayState::eOrientationSwapMask)) {
+ info.h = hwConfig.width;
+ info.w = hwConfig.height;
+ info.xdpi = ydpi;
+ info.ydpi = xdpi;
+ }
+ else {
+ info.w = hwConfig.width;
+ info.h = hwConfig.height;
+ info.xdpi = xdpi;
+ info.ydpi = ydpi;
+ }
info.fps = float(1e9 / hwConfig.refresh);
info.appVsyncOffset = VSYNC_EVENT_PHASE_OFFSET_NS;
+ info.colorTransform = hwConfig.colorTransform;
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
return NO_ERROR;
}
-status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& display,
+status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& /* display */,
DisplayStatInfo* stats) {
if (stats == NULL) {
return BAD_VALUE;
}
int SurfaceFlinger::getActiveConfig(const sp<IBinder>& display) {
- return getDisplayDevice(display)->getActiveConfig();
+ sp<DisplayDevice> device(getDisplayDevice(display));
+ if (device != NULL) {
+ return device->getActiveConfig();
+ }
+ return BAD_VALUE;
}
void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& hw, int mode) {
return;
}
- hw->setActiveConfig(mode);
- getHwComposer().setActiveConfig(type, mode);
+ status_t status = getHwComposer().setActiveConfig(type, mode);
+ if (status == NO_ERROR) {
+ hw->setActiveConfig(mode);
+ }
}
status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& display, int mode) {
virtual bool handler() {
Vector<DisplayInfo> configs;
mFlinger.getDisplayConfigs(mDisplay, &configs);
- if(mMode < 0 || mMode >= configs.size()) {
+ if (mMode < 0 || mMode >= static_cast<int>(configs.size())) {
ALOGE("Attempt to set active config = %d for display with %zu configs",
mMode, configs.size());
}
mEventQueue.waitMessage();
}
+#ifdef CONSOLE_MANAGER
+void SurfaceFlinger::screenReleased(const sp<IBinder>& display) {
+ // this may be called by a signal handler, we can't do too much in here
+ setPowerMode(display, HWC_POWER_MODE_OFF);
+ signalLayerUpdate();
+}
+
+void SurfaceFlinger::screenAcquired(const sp<IBinder>& display) {
+ // this may be called by a signal handler, we can't do too much in here
+ setPowerMode(display, HWC_POWER_MODE_NORMAL);
+ signalLayerUpdate();
+}
+#endif
+
void SurfaceFlinger::signalTransaction() {
mEventQueue.invalidate();
}
} else {
mCurrentState.displays.removeItem(mBuiltinDisplays[type]);
mBuiltinDisplays[type].clear();
+ updateVisibleRegionsDirty();
}
setTransactionFlags(eDisplayTransactionNeeded);
void SurfaceFlinger::onMessageReceived(int32_t what) {
ATRACE_CALL();
switch (what) {
- case MessageQueue::TRANSACTION:
- handleMessageTransaction();
- break;
- case MessageQueue::INVALIDATE:
- handleMessageTransaction();
- handleMessageInvalidate();
- signalRefresh();
- break;
- case MessageQueue::REFRESH:
- handleMessageRefresh();
- break;
+ case MessageQueue::TRANSACTION: {
+ handleMessageTransaction();
+ break;
+ }
+ case MessageQueue::INVALIDATE: {
+ bool refreshNeeded = handleMessageTransaction();
+ refreshNeeded |= handleMessageInvalidate();
+ refreshNeeded |= mRepaintEverything;
+ if (refreshNeeded) {
+ // Signal a refresh if a transaction modified the window state,
+ // a new buffer was latched, or if HWC has requested a full
+ // repaint
+ signalRefresh();
+ }
+ break;
+ }
+ case MessageQueue::REFRESH: {
+ handleMessageRefresh();
+ break;
+ }
}
}
-void SurfaceFlinger::handleMessageTransaction() {
+bool SurfaceFlinger::handleMessageTransaction() {
uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
if (transactionFlags) {
handleTransaction(transactionFlags);
+ return true;
}
+ return false;
}
-void SurfaceFlinger::handleMessageInvalidate() {
+bool SurfaceFlinger::handleMessageInvalidate() {
ATRACE_CALL();
- handlePageFlip();
+ return handlePageFlip();
}
void SurfaceFlinger::handleMessageRefresh() {
ATRACE_CALL();
- preComposition();
- rebuildLayerStacks();
- setUpHWComposer();
- doDebugFlashRegions();
- doComposition();
- postComposition();
+
+ static nsecs_t previousExpectedPresent = 0;
+ nsecs_t expectedPresent = mPrimaryDispSync.computeNextRefresh(0);
+ static bool previousFrameMissed = false;
+ bool frameMissed = (expectedPresent == previousExpectedPresent);
+ if (frameMissed != previousFrameMissed) {
+ ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
+ }
+ previousFrameMissed = frameMissed;
+
+ if (CC_UNLIKELY(mDropMissedFrames && frameMissed)) {
+ // Latch buffers, but don't send anything to HWC, then signal another
+ // wakeup for the next vsync
+ preComposition();
+ repaintEverything();
+ } else {
+ preComposition();
+ rebuildLayerStacks();
+ setUpHWComposer();
+ doDebugFlashRegions();
+ doComposition();
+ postComposition();
+ }
+
+ previousExpectedPresent = mPrimaryDispSync.computeNextRefresh(0);
}
void SurfaceFlinger::doDebugFlashRegions()
}
}
+ const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
if (kIgnorePresentFences) {
- const sp<const DisplayDevice> hw(getDefaultDisplayDevice());
if (hw->isDisplayOn()) {
enableHardwareVsync();
}
}
mAnimFrameTracker.advanceFrame();
}
+
+ dumpDrawCycle(false);
+
+ if (hw->getPowerMode() == HWC_POWER_MODE_OFF) {
+ return;
+ }
+
+ nsecs_t currentTime = systemTime();
+ if (mHasPoweredOff) {
+ mHasPoweredOff = false;
+ } else {
+ nsecs_t period = mPrimaryDispSync.getPeriod();
+ nsecs_t elapsedTime = currentTime - mLastSwapTime;
+ size_t numPeriods = static_cast<size_t>(elapsedTime / period);
+ if (numPeriods < NUM_BUCKETS - 1) {
+ mFrameBuckets[numPeriods] += elapsedTime;
+ } else {
+ mFrameBuckets[NUM_BUCKETS - 1] += elapsedTime;
+ }
+ mTotalTime += elapsedTime;
+ }
+ mLastSwapTime = currentTime;
}
void SurfaceFlinger::rebuildLayerStacks() {
+ updateExtendedMode();
// rebuild the visible layer list per screen
if (CC_UNLIKELY(mVisibleRegionsDirty)) {
ATRACE_CALL();
const Transform& tr(hw->getTransform());
const Rect bounds(hw->getBounds());
if (hw->isDisplayOn()) {
- SurfaceFlinger::computeVisibleRegions(layers,
+ computeVisibleRegions(hw->getHwcDisplayId(), layers,
hw->getLayerStack(), dirtyRegion, opaqueRegion);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(layers[i]);
- const Layer::State& s(layer->getDrawingState());
- if (s.layerStack == hw->getLayerStack()) {
+ {
Region drawRegion(tr.transform(
layer->visibleNonTransparentRegion));
drawRegion.andSelf(bounds);
for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
const sp<Layer>& layer(currentLayers[i]);
layer->setGeometry(hw, *cur);
- if (mDebugDisableHWC || mDebugRegion || mDaltonize || mHasColorMatrix) {
+ if (mDebugDisableHWC || mDebugRegion || mDaltonize || mHasColorMatrix || mHasSecondaryColorMatrix) {
cur->setSkip(true);
}
}
sp<const DisplayDevice> hw(mDisplays[dpy]);
const int32_t id = hw->getHwcDisplayId();
if (id >= 0) {
+ bool freezeSurfacePresent = false;
+ isfreezeSurfacePresent(freezeSurfacePresent, hw, id);
const Vector< sp<Layer> >& currentLayers(
hw->getVisibleLayersSortedByZ());
const size_t count = currentLayers.size();
*/
const sp<Layer>& layer(currentLayers[i]);
layer->setPerFrameData(hw, *cur);
+ setOrientationEventControl(freezeSurfacePresent,id);
+ if(!strncmp(layer->getName(), "SurfaceView",
+ 11)) {
+ lastSurfaceViewLayer = layer;
+ }
}
}
}
}
}
+ dumpDrawCycle(true);
+
status_t err = hwc.prepare();
ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
// repaint the framebuffer (if needed)
doDisplayComposition(hw, dirtyRegion);
+ ++mActiveFrameSequence;
+
hw->dirtyRegion.clear();
hw->flip(hw->swapRegion);
hw->swapRegion.clear();
if (flipCount % LOG_FRAME_STATS_PERIOD == 0) {
logFrameStats();
}
+ ALOGV_IF(mFrameRateHelper.update(), "FPS: %d", mFrameRateHelper.get());
}
void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
// this display is in both lists. see if something changed.
const DisplayDeviceState& state(curr[j]);
const wp<IBinder>& display(curr.keyAt(j));
- if (state.surface->asBinder() != draw[i].surface->asBinder()) {
+ const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
+ const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
+ if (state_binder != draw_binder) {
// changing the surface is like destroying and
// recreating the DisplayDevice, so we just remove it
// from the drawing state, so that it get re-added
// etc.) but no internal state (i.e. a DisplayDevice).
if (state.surface != NULL) {
- hwcDisplayId = allocateHwcDisplayId(state.type);
- sp<VirtualDisplaySurface> vds = new VirtualDisplaySurface(
- *mHwc, hwcDisplayId, state.surface,
- bqProducer, bqConsumer, state.displayName);
+ int width = 0;
+ DisplayUtils* displayUtils = DisplayUtils::getInstance();
+ int status = state.surface->query(
+ NATIVE_WINDOW_WIDTH, &width);
+ ALOGE_IF(status != NO_ERROR,
+ "Unable to query width (%d)", status);
+ int height = 0;
+ status = state.surface->query(
+ NATIVE_WINDOW_HEIGHT, &height);
+ ALOGE_IF(status != NO_ERROR,
+ "Unable to query height (%d)", status);
+ if (MAX_VIRTUAL_DISPLAY_DIMENSION == 0 ||
+ (width <= MAX_VIRTUAL_DISPLAY_DIMENSION &&
+ height <= MAX_VIRTUAL_DISPLAY_DIMENSION)) {
+ int usage = 0;
+ status = state.surface->query(
+ NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage);
+ ALOGW_IF(status != NO_ERROR,
+ "Unable to query usage (%d)", status);
+ if ( (status == NO_ERROR) &&
+ displayUtils->canAllocateHwcDisplayIdForVDS(usage)) {
+ hwcDisplayId = allocateHwcDisplayId(state.type);
+ }
+ }
+
+ displayUtils->initVDSInstance(mHwc, hwcDisplayId, state.surface,
+ dispSurface, producer, bqProducer, bqConsumer,
+ state.displayName, state.isSecure, state.type);
- dispSurface = vds;
- producer = vds;
}
} else {
ALOGE_IF(state.surface!=NULL,
}
const wp<IBinder>& display(curr.keyAt(i));
- if (dispSurface != NULL) {
+ if (dispSurface != NULL && producer != NULL) {
sp<DisplayDevice> hw = new DisplayDevice(this,
state.type, hwcDisplayId,
mHwc->getFormat(hwcDisplayId), state.isSecure,
hw->setProjection(state.orientation,
state.viewport, state.frame);
hw->setDisplayName(state.displayName);
+ // When a new display device is added update the active
+ // config by querying HWC otherwise the default config
+ // (config 0) will be used.
+ if (hwcDisplayId >= DisplayDevice::DISPLAY_PRIMARY &&
+ hwcDisplayId < DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES) {
+ int activeConfig = mHwc->getActiveConfig(hwcDisplayId);
+ if (activeConfig >= 0) {
+ hw->setActiveConfig(activeConfig);
+ }
+ }
mDisplays.add(display, hw);
if (state.isVirtualDisplay()) {
if (hwcDisplayId >= 0) {
mTransactionCV.broadcast();
}
-void SurfaceFlinger::computeVisibleRegions(
+void SurfaceFlinger::computeVisibleRegions(size_t dpy,
const LayerVector& currentLayers, uint32_t layerStack,
Region& outDirtyRegion, Region& outOpaqueRegion)
{
Region dirty;
outDirtyRegion.clear();
+ bool bIgnoreLayers = false;
+ int indexLOI = -1;
+ getIndexLOI(dpy, currentLayers, bIgnoreLayers, indexLOI);
size_t i = currentLayers.size();
while (i--) {
// start with the whole surface at its current location
const Layer::State& s(layer->getDrawingState());
- // only consider the layers on the given layer stack
- if (s.layerStack != layerStack)
+ if(updateLayerVisibleNonTransparentRegion(dpy, layer,
+ bIgnoreLayers, indexLOI,
+ layerStack, i))
continue;
/*
}
}
-void SurfaceFlinger::handlePageFlip()
+bool SurfaceFlinger::handlePageFlip()
{
Region dirtyRegion;
bool visibleRegions = false;
const LayerVector& layers(mDrawingState.layersSortedByZ);
+ bool frameQueued = false;
// Store the set of layers that need updates. This set must not change as
// buffers are being latched, as this could result in a deadlock.
Vector<Layer*> layersWithQueuedFrames;
for (size_t i = 0, count = layers.size(); i<count ; i++) {
const sp<Layer>& layer(layers[i]);
- if (layer->hasQueuedFrame())
- layersWithQueuedFrames.push_back(layer.get());
+ if (layer->hasQueuedFrame()) {
+ frameQueued = true;
+ if (layer->shouldPresentNow(mPrimaryDispSync)) {
+ layersWithQueuedFrames.push_back(layer.get());
+ } else {
+ layer->useEmptyDamage();
+ }
+ } else {
+ layer->useEmptyDamage();
+ }
}
for (size_t i = 0, count = layersWithQueuedFrames.size() ; i<count ; i++) {
Layer* layer = layersWithQueuedFrames[i];
const Region dirty(layer->latchBuffer(visibleRegions));
+ layer->useSurfaceDamage();
const Layer::State& s(layer->getDrawingState());
invalidateLayerStack(s.layerStack, dirty);
}
mVisibleRegionsDirty |= visibleRegions;
+
+ // If we will need to wake up at some time in the future to deal with a
+ // queued frame that shouldn't be displayed during this vsync period, wake
+ // up during the next vsync period to check again.
+ if (frameQueued && layersWithQueuedFrames.empty()) {
+ signalLayerUpdate();
+ }
+
+ // Only continue with the refresh if there is actually new work to do
+ return !layersWithQueuedFrames.empty();
}
void SurfaceFlinger::invalidateHwcGeometry()
}
}
- if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) {
+ if (CC_LIKELY(!mDaltonize && !mHasColorMatrix && !mHasSecondaryColorMatrix)) {
if (!doComposeSurfaces(hw, dirtyRegion)) return;
} else {
RenderEngine& engine(getRenderEngine());
mat4 colorMatrix = mColorMatrix;
+ if (mHasSecondaryColorMatrix) {
+ colorMatrix = mHasColorMatrix ? (colorMatrix * mSecondaryColorMatrix) : mSecondaryColorMatrix;
+ }
if (mDaltonize) {
colorMatrix = colorMatrix * mDaltonizer();
}
- engine.beginGroup(colorMatrix);
+ mat4 oldMatrix = engine.setupColorTransform(colorMatrix);
doComposeSurfaces(hw, dirtyRegion);
- engine.endGroup();
+ engine.setupColorTransform(oldMatrix);
}
// update the swap region and clear the dirty region
}
// Never touch the framebuffer if we don't have any framebuffer layers
+#if defined(QTI_BSP) && defined(SDM_TARGET)
+ const bool hasHwcComposition = hwc.hasHwcComposition(id) |
+ (reinterpret_cast<ExHWComposer*>(&hwc))->getS3DFlag(id);
+#else
const bool hasHwcComposition = hwc.hasHwcComposition(id);
+#endif
if (hasHwcComposition) {
// when using overlays, we assume a fully transparent framebuffer
// NOTE: we could reduce how much we need to clear, for instance
// screen is already cleared here
if (!region.isEmpty()) {
// can happen with SurfaceView
- drawWormhole(hw, region);
+ drawWormHoleIfRequired(cur, end, hw, region);
}
}
engine.fillRegionWithColor(region, height, 0, 0, 0, 0);
}
-void SurfaceFlinger::addClientLayer(const sp<Client>& client,
+status_t SurfaceFlinger::addClientLayer(const sp<Client>& client,
const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc,
const sp<Layer>& lbc)
{
+ // add this layer to the current state list
+ {
+ Mutex::Autolock _l(mStateLock);
+ if (mCurrentState.layersSortedByZ.size() >= MAX_LAYERS) {
+ return NO_MEMORY;
+ }
+ mCurrentState.layersSortedByZ.add(lbc);
+ mGraphicBufferProducerList.add(IInterface::asBinder(gbc));
+ }
+
// attach this layer to the client
client->attachLayer(handle, lbc);
- // add this layer to the current state list
- Mutex::Autolock _l(mStateLock);
- mCurrentState.layersSortedByZ.add(lbc);
- mGraphicBufferProducerList.add(gbc->asBinder());
+ return NO_ERROR;
}
status_t SurfaceFlinger::removeLayer(const sp<Layer>& layer) {
uint32_t flags)
{
ATRACE_CALL();
+
+ delayDPTransactionIfNeeded(displays);
Mutex::Autolock _l(mStateLock);
uint32_t transactionFlags = 0;
// NOTE: it would be better to use RTTI as we could directly check
// that we have a Client*. however, RTTI is disabled in Android.
if (s.client != NULL) {
- sp<IBinder> binder = s.client->asBinder();
+ sp<IBinder> binder = IInterface::asBinder(s.client);
if (binder != NULL) {
String16 desc(binder->getInterfaceDescriptor());
if (desc == ISurfaceComposerClient::descriptor) {
if (disp.isValid()) {
const uint32_t what = s.what;
if (what & DisplayState::eSurfaceChanged) {
- if (disp.surface->asBinder() != s.surface->asBinder()) {
+ if (IInterface::asBinder(disp.surface) != IInterface::asBinder(s.surface)) {
disp.surface = s.surface;
flags |= eDisplayTransactionNeeded;
}
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
+ if (what & layer_state_t::eBlurChanged) {
+ ALOGV("eBlurChanged");
+ if (layer->setBlur(uint8_t(255.0f*s.blur+0.5f))) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ if (what & layer_state_t::eBlurMaskSurfaceChanged) {
+ ALOGV("eBlurMaskSurfaceChanged");
+ sp<Layer> maskLayer = 0;
+ if (s.blurMaskSurface != 0) {
+ maskLayer = client->getLayerUser(s.blurMaskSurface);
+ }
+ if (maskLayer == 0) {
+ ALOGV("eBlurMaskSurfaceChanged. maskLayer == 0");
+ } else {
+ ALOGV("eBlurMaskSurfaceChagned. maskLayer.z == %d", maskLayer->getCurrentState().z);
+ if (maskLayer->isBlurLayer()) {
+ ALOGE("Blur layer can not be used as blur mask surface");
+ maskLayer = 0;
+ }
+ }
+ if (layer->setBlurMaskLayer(maskLayer)) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ if (what & layer_state_t::eBlurMaskSamplingChanged) {
+ if (layer->setBlurMaskSampling(s.blurMaskSampling)) {
+ flags |= eTraversalNeeded;
+ }
+ }
+ if (what & layer_state_t::eBlurMaskAlphaThresholdChanged) {
+ if (layer->setBlurMaskAlphaThreshold(s.blurMaskAlphaThreshold)) {
+ flags |= eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eSizeChanged) {
if (layer->setSize(s.w, s.h)) {
flags |= eTraversalNeeded;
if (layer->setTransparentRegionHint(s.transparentRegion))
flags |= eTraversalNeeded;
}
- if ((what & layer_state_t::eVisibilityChanged) ||
- (what & layer_state_t::eOpacityChanged)) {
- // TODO: should we just use an eFlagsChanged for this?
+ if (what & layer_state_t::eFlagsChanged) {
if (layer->setFlags(s.flags, s.mask))
flags |= eTraversalNeeded;
}
name, w, h, flags,
handle, gbp, &layer);
break;
+ case ISurfaceComposerClient::eFXSurfaceBlur:
+ result = createBlurLayer(client,
+ name, w, h, flags,
+ handle, gbp, &layer);
+ break;
default:
result = BAD_VALUE;
break;
}
- if (result == NO_ERROR) {
- addClientLayer(client, *handle, *gbp, layer);
- setTransactionFlags(eTransactionNeeded);
+ if (result != NO_ERROR) {
+ return result;
+ }
+
+ result = addClientLayer(client, *handle, *gbp, layer);
+ if (result != NO_ERROR) {
+ return result;
}
+
+ setTransactionFlags(eTransactionNeeded);
return result;
}
break;
}
- *outLayer = new Layer(this, client, name, w, h, flags);
+ *outLayer = DisplayUtils::getInstance()->getLayerInstance(this, client, name, w, h, flags);
status_t err = (*outLayer)->setBuffers(w, h, format, flags);
if (err == NO_ERROR) {
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
+status_t SurfaceFlinger::createBlurLayer(const sp<Client>& client,
+ const String8& name, uint32_t w, uint32_t h, uint32_t flags,
+ sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
+{
+ *outLayer = new LayerBlur(this, client, name, w, h, flags);
+ *handle = (*outLayer)->getHandle();
+ *gbp = (*outLayer)->getProducer();
+ return NO_ERROR;
+}
+
status_t SurfaceFlinger::onLayerRemoved(const sp<Client>& client, const sp<IBinder>& handle)
{
// called by the window manager when it wants to remove a Layer
}
mVisibleRegionsDirty = true;
+ mHasPoweredOff = true;
repaintEverything();
} else if (mode == HWC_POWER_MODE_OFF) {
if (type == DisplayDevice::DISPLAY_PRIMARY) {
result.appendFormat("Permission Denial: "
"can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid);
} else {
- // Try to get the main lock, but don't insist if we can't
+ // Try to get the main lock, but give up after one second
// (this would indicate SF is stuck, but we want to be able to
// print something in dumpsys).
- int retry = 3;
- while (mStateLock.tryLock()<0 && --retry>=0) {
- usleep(1000000);
- }
- const bool locked(retry >= 0);
+ status_t err = mStateLock.timedLock(s2ns(1));
+ bool locked = (err == NO_ERROR);
if (!locked) {
- result.append(
- "SurfaceFlinger appears to be unresponsive, "
- "dumping anyways (no locks held)\n");
+ result.appendFormat(
+ "SurfaceFlinger appears to be unresponsive (%s [%d]), "
+ "dumping anyways (no locks held)\n", strerror(-err), err);
}
bool dumpAll = true;
mPrimaryDispSync.dump(result);
dumpAll = false;
}
+
+ if ((index < numArgs) &&
+ (args[index] == String16("--static-screen"))) {
+ index++;
+ dumpStaticScreenStats(result);
+ dumpAll = false;
+ }
}
if (dumpAll) {
if (name.isEmpty()) {
mAnimFrameTracker.dumpStats(result);
} else {
+ bool found = false;
const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
const size_t count = currentLayers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(currentLayers[i]);
if (name == layer->getName()) {
+ found = true;
layer->dumpFrameStats(result);
}
}
+ if (!found && !strncmp(name.string(), "SurfaceView", 11)) {
+ lastSurfaceViewLayer->dumpFrameStats(result);
+ }
}
}
result.append(config);
}
+void SurfaceFlinger::dumpStaticScreenStats(String8& result) const
+{
+ result.appendFormat("Static screen stats:\n");
+ for (size_t b = 0; b < NUM_BUCKETS - 1; ++b) {
+ float bucketTimeSec = mFrameBuckets[b] / 1e9;
+ float percent = 100.0f *
+ static_cast<float>(mFrameBuckets[b]) / mTotalTime;
+ result.appendFormat(" < %zd frames: %.3f s (%.1f%%)\n",
+ b + 1, bucketTimeSec, percent);
+ }
+ float bucketTimeSec = mFrameBuckets[NUM_BUCKETS - 1] / 1e9;
+ float percent = 100.0f *
+ static_cast<float>(mFrameBuckets[NUM_BUCKETS - 1]) / mTotalTime;
+ result.appendFormat(" %zd+ frames: %.3f s (%.1f%%)\n",
+ NUM_BUCKETS - 1, bucketTimeSec, percent);
+}
+
void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
String8& result) const
{
mHwc->getRefreshPeriod(HWC_DISPLAY_PRIMARY));
result.append("\n");
+ // Dump static screen stats
+ result.append("\n");
+ dumpStaticScreenStats(result);
+ result.append("\n");
+
/*
* Dump the visible layer list
*/
result.appendFormat(" h/w composer %s and %s\n",
hwc.initCheck()==NO_ERROR ? "present" : "not present",
(mDebugDisableHWC || mDebugRegion || mDaltonize
- || mHasColorMatrix) ? "disabled" : "enabled");
+ || mHasColorMatrix
+ || mHasSecondaryColorMatrix) ? "disabled" : "enabled");
hwc.dump(result);
/*
}
}
if (dpy == NULL) {
- ALOGE("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id);
+ ALOGW("getLayerSortedByZForHwcDisplay: invalid hwc display id %d", id);
// Just use the primary display so we have something to return
dpy = getBuiltInDisplay(DisplayDevice::DISPLAY_PRIMARY);
}
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- if ((uid != AID_GRAPHICS) &&
+ if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) &&
!PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) {
ALOGE("Permission Denial: "
"can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
mPrimaryDispSync.setRefreshSkipCount(n);
return NO_ERROR;
}
+ case 1017: {
+ n = data.readInt32();
+ mForceFullDamage = static_cast<bool>(n);
+ return NO_ERROR;
+ }
+ case 1018: { // Modify Choreographer's phase offset
+ n = data.readInt32();
+ if (mEventThread != NULL)
+ mEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+ return NO_ERROR;
+ }
+ case 1019: { // Modify SurfaceFlinger's phase offset
+ n = data.readInt32();
+ if (mSFEventThread != NULL)
+ mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n));
+ return NO_ERROR;
+ }
+ case 1030: {
+ // apply a secondary color matrix
+ // this will be combined with any other transformations
+ n = data.readInt32();
+ mHasSecondaryColorMatrix = n ? 1 : 0;
+ if (n) {
+ // color matrix is sent as mat3 matrix followed by vec3
+ // offset, then packed into a mat4 where the last row is
+ // the offset and extra values are 0
+ for (size_t i = 0 ; i < 4; i++) {
+ for (size_t j = 0; j < 4; j++) {
+ mSecondaryColorMatrix[i][j] = data.readFloat();
+ }
+ }
+ } else {
+ mSecondaryColorMatrix = mat4();
+ }
+ invalidateHwcGeometry();
+ repaintEverything();
+ return NO_ERROR;
+ }
+
}
}
return err;
// Prevent reads below from happening before the read from Message
atomic_thread_fence(memory_order_acquire);
if (what == MSG_API_CALL) {
- result = impl->asBinder()->transact(code, data[0], reply);
+ result = IInterface::asBinder(impl)->transact(code, data[0], reply);
barrier.open();
} else if (what == MSG_EXIT) {
exitRequested = true;
const sp<IGraphicBufferProducer>& producer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ,
- bool useIdentityTransform, ISurfaceComposer::Rotation rotation) {
+ bool useIdentityTransform, ISurfaceComposer::Rotation rotation,
+ bool useReadPixels) {
if (CC_UNLIKELY(display == 0))
return BAD_VALUE;
// if we have secure windows on this display, never allow the screen capture
// unless the producer interface is local (i.e.: we can take a screenshot for
// ourselves).
- if (!producer->asBinder()->localBinder()) {
+ if (!IInterface::asBinder(producer)->localBinder()) {
Mutex::Autolock _l(mStateLock);
sp<const DisplayDevice> hw(getDisplayDevice(display));
if (hw->getSecureLayerVisible()) {
uint32_t minLayerZ,maxLayerZ;
bool useIdentityTransform;
Transform::orientation_flags rotation;
+ bool useReadPixels;
status_t result;
public:
MessageCaptureScreen(SurfaceFlinger* flinger,
const sp<IGraphicBufferProducer>& producer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ,
- bool useIdentityTransform, Transform::orientation_flags rotation)
+ bool useIdentityTransform, Transform::orientation_flags rotation,
+ bool useReadPixels)
: flinger(flinger), display(display), producer(producer),
sourceCrop(sourceCrop), reqWidth(reqWidth), reqHeight(reqHeight),
minLayerZ(minLayerZ), maxLayerZ(maxLayerZ),
useIdentityTransform(useIdentityTransform),
rotation(rotation),
+ useReadPixels(useReadPixels),
result(PERMISSION_DENIED)
{
}
virtual bool handler() {
Mutex::Autolock _l(flinger->mStateLock);
sp<const DisplayDevice> hw(flinger->getDisplayDevice(display));
+ bool useReadPixels = this->useReadPixels && !flinger->mGpuToCpuSupported;
result = flinger->captureScreenImplLocked(hw, producer,
sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
- useIdentityTransform, rotation);
- static_cast<GraphicProducerWrapper*>(producer->asBinder().get())->exit(result);
+ useIdentityTransform, rotation, useReadPixels);
+ static_cast<GraphicProducerWrapper*>(IInterface::asBinder(producer).get())->exit(result);
return true;
}
};
sp<MessageBase> msg = new MessageCaptureScreen(this,
display, IGraphicBufferProducer::asInterface( wrapper ),
sourceCrop, reqWidth, reqHeight, minLayerZ, maxLayerZ,
- useIdentityTransform, rotationFlags);
+ useIdentityTransform, rotationFlags, useReadPixels);
status_t res = postMessageAsync(msg);
if (res == NO_ERROR) {
RenderEngine& engine(getRenderEngine());
// get screen geometry
- const uint32_t hw_w = hw->getWidth();
- const uint32_t hw_h = hw->getHeight();
- const bool filtering = reqWidth != hw_w || reqWidth != hw_h;
+ const int32_t hw_w = hw->getWidth();
+ const int32_t hw_h = hw->getHeight();
+ const bool filtering = static_cast<int32_t>(reqWidth) != hw_w ||
+ static_cast<int32_t>(reqHeight) != hw_h;
// if a default or invalid sourceCrop is passed in, set reasonable values
if (sourceCrop.width() == 0 || sourceCrop.height() == 0 ||
// make sure to clear all GL error flags
engine.checkErrors();
+ if (DisplayDevice::DISPLAY_PRIMARY == hw->getDisplayType()) {
+ rotation = (Transform::orientation_flags)
+ (rotation ^ hw->getPanelMountFlip());
+ }
+
// set-up our viewport
engine.setViewportAndProjection(
reqWidth, reqHeight, sourceCrop, hw_h, yswap, rotation);
const Layer::State& state(layer->getDrawingState());
if (state.layerStack == hw->getLayerStack()) {
if (state.z >= minLayerZ && state.z <= maxLayerZ) {
- if (layer->isVisible()) {
+ if (canDrawLayerinScreenShot(hw,layer)) {
if (filtering) layer->setFiltering(true);
layer->draw(hw, useIdentityTransform);
if (filtering) layer->setFiltering(false);
const sp<IGraphicBufferProducer>& producer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
uint32_t minLayerZ, uint32_t maxLayerZ,
- bool useIdentityTransform, Transform::orientation_flags rotation)
+ bool useIdentityTransform, Transform::orientation_flags rotation,
+ bool useReadPixels)
{
ATRACE_CALL();
// get screen geometry
- const uint32_t hw_w = hw->getWidth();
- const uint32_t hw_h = hw->getHeight();
+ uint32_t hw_w = hw->getWidth();
+ uint32_t hw_h = hw->getHeight();
+
+ if (rotation & Transform::ROT_90) {
+ std::swap(hw_w, hw_h);
+ }
if ((reqWidth > hw_w) || (reqHeight > hw_h)) {
ALOGE("size mismatch (%d, %d) > (%d, %d)",
return BAD_VALUE;
}
+ ++mActiveFrameSequence;
+
reqWidth = (!reqWidth) ? hw_w : reqWidth;
reqHeight = (!reqHeight) ? hw_h : reqHeight;
sp<Surface> sur = new Surface(producer, false);
ANativeWindow* window = sur.get();
- status_t result = NO_ERROR;
- if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) == NO_ERROR) {
+ status_t result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+ if (result == NO_ERROR) {
uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
if (image != EGL_NO_IMAGE_KHR) {
// this binds the given EGLImage as a framebuffer for the
// duration of this scope.
- RenderEngine::BindImageAsFramebuffer imageBond(getRenderEngine(), image);
+ RenderEngine::BindImageAsFramebuffer imageBond(getRenderEngine(), image,
+ useReadPixels, reqWidth, reqHeight);
if (imageBond.getStatus() == NO_ERROR) {
// this will in fact render into our dequeued buffer
// via an FBO, which means we didn't have to create
EGLSyncKHR sync;
if (!DEBUG_SCREENSHOTS) {
sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+ // native fence fd will not be populated until flush() is done.
+ getRenderEngine().flush();
} else {
sync = EGL_NO_SYNC_KHR;
}
ALOGW("captureScreen: error creating EGL fence: %#x", eglGetError());
}
}
+ if (useReadPixels) {
+ sp<GraphicBuffer> buf = static_cast<GraphicBuffer*>(buffer);
+ void* vaddr;
+ if (buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &vaddr) == NO_ERROR) {
+ getRenderEngine().readPixels(0, 0, buffer->stride, reqHeight,
+ (uint32_t *)vaddr);
+ buf->unlock();
+ }
+ }
if (DEBUG_SCREENSHOTS) {
uint32_t* pixels = new uint32_t[reqWidth*reqHeight];
getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
result = BAD_VALUE;
}
// queueBuffer takes ownership of syncFd
- window->queueBuffer(window, buffer, syncFd);
+ result = window->queueBuffer(window, buffer, syncFd);
}
} else {
result = BAD_VALUE;
}
}
+/* ------------------------------------------------------------------------
+ * Extensions
+ */
+
+bool SurfaceFlinger::updateLayerVisibleNonTransparentRegion(const int& /*dpy*/,
+ const sp<Layer>& layer, bool& /*bIgnoreLayers*/, int& /*indexLOI*/,
+ uint32_t layerStack, const int& /*i*/) {
+
+ const Layer::State& s(layer->getDrawingState());
+
+ // only consider the layers on the given layer stack
+ if (s.layerStack != layerStack) {
+ /* set the visible region as empty since we have removed the
+ * layerstack check in rebuildLayerStack() function
+ */
+ Region visibleNonTransRegion;
+ visibleNonTransRegion.set(Rect(0,0));
+ layer->setVisibleNonTransparentRegion(visibleNonTransRegion);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SurfaceFlinger::canDrawLayerinScreenShot(
+ const sp<const DisplayDevice>& /*hw*/,
+ const sp<Layer>& layer) {
+ return layer->isVisible();
+}
+
+void SurfaceFlinger::drawWormHoleIfRequired(HWComposer::LayerListIterator& /*cur*/,
+ const HWComposer::LayerListIterator& /*end*/,
+ const sp<const DisplayDevice>& hw,
+ const Region& region) {
+ drawWormhole(hw, region);
+}
+
// ---------------------------------------------------------------------------
SurfaceFlinger::LayerVector::LayerVector() {