diff options
Diffstat (limited to 'widget/gonk/HwcComposer2D.cpp')
-rw-r--r-- | widget/gonk/HwcComposer2D.cpp | 971 |
1 files changed, 971 insertions, 0 deletions
diff --git a/widget/gonk/HwcComposer2D.cpp b/widget/gonk/HwcComposer2D.cpp new file mode 100644 index 000000000..6b4c7a1cc --- /dev/null +++ b/widget/gonk/HwcComposer2D.cpp @@ -0,0 +1,971 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + * Copyright (c) 2012, 2013 The Linux Foundation. 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 <android/log.h> +#include <string.h> + +#include "gfxPrefs.h" +#include "ImageLayers.h" +#include "libdisplay/GonkDisplay.h" +#include "HwcComposer2D.h" +#include "LayerScope.h" +#include "Units.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/LayerManagerComposite.h" +#include "mozilla/layers/PLayerTransaction.h" +#include "mozilla/layers/ShadowLayerUtilsGralloc.h" +#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL +#include "mozilla/StaticPtr.h" +#include "nsThreadUtils.h" +#include "cutils/properties.h" +#include "gfx2DGlue.h" +#include "gfxPlatform.h" +#include "VsyncSource.h" +#include "nsScreenManagerGonk.h" +#include "nsWindow.h" + +#if ANDROID_VERSION >= 17 +#include "libdisplay/DisplaySurface.h" +#endif + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "HWComposer" + +/* + * By default the debug message of hwcomposer (LOG_DEBUG level) are undefined, + * but can be enabled by uncommenting HWC_DEBUG below. + */ +//#define HWC_DEBUG + +#ifdef HWC_DEBUG +#define LOGD(args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, ## args) +#else +#define LOGD(args...) ((void)0) +#endif + +#define LOGI(args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, ## args) +#define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, ## args) + +#define LAYER_COUNT_INCREMENTS 5 + +using namespace android; +using namespace mozilla::gfx; +using namespace mozilla::layers; + +namespace mozilla { + +static void +HookInvalidate(const struct hwc_procs* aProcs) +{ + HwcComposer2D::GetInstance()->Invalidate(); +} + +static void +HookVsync(const struct hwc_procs* aProcs, int aDisplay, + int64_t aTimestamp) +{ + HwcComposer2D::GetInstance()->Vsync(aDisplay, aTimestamp); +} + +static void +HookHotplug(const struct hwc_procs* aProcs, int aDisplay, + int aConnected) +{ + HwcComposer2D::GetInstance()->Hotplug(aDisplay, aConnected); +} + +static StaticRefPtr<HwcComposer2D> sInstance; + +HwcComposer2D::HwcComposer2D() + : mList(nullptr) + , mMaxLayerCount(0) + , mColorFill(false) + , mRBSwapSupport(false) + , mPrepared(false) + , mHasHWVsync(false) + , mLock("mozilla.HwcComposer2D.mLock") +{ + mHal = HwcHALBase::CreateHwcHAL(); + if (!mHal->HasHwc()) { + LOGD("no hwc support"); + return; + } + + RegisterHwcEventCallback(); + + nsIntSize screenSize; + + GonkDisplay::NativeData data = GetGonkDisplay()->GetNativeData(GonkDisplay::DISPLAY_PRIMARY); + ANativeWindow *win = data.mNativeWindow.get(); + win->query(win, NATIVE_WINDOW_WIDTH, &screenSize.width); + win->query(win, NATIVE_WINDOW_HEIGHT, &screenSize.height); + mScreenRect = gfx::IntRect(gfx::IntPoint(0, 0), screenSize); + + mColorFill = mHal->Query(HwcHALBase::QueryType::COLOR_FILL); + mRBSwapSupport = mHal->Query(HwcHALBase::QueryType::RB_SWAP); +} + +HwcComposer2D::~HwcComposer2D() +{ + free(mList); +} + +HwcComposer2D* +HwcComposer2D::GetInstance() +{ + if (!sInstance) { +#ifdef HWC_DEBUG + // Make sure only create once + static int timesCreated = 0; + ++timesCreated; + MOZ_ASSERT(timesCreated == 1); +#endif + LOGI("Creating new instance"); + sInstance = new HwcComposer2D(); + + // If anyone uses the compositor thread to create HwcComposer2D, + // we just skip this function. + // If ClearOnShutdown() can handle objects in other threads + // in the future, we can remove this check. + if (NS_IsMainThread()) { + // If we create HwcComposer2D by the main thread, we can use + // ClearOnShutdown() to make sure it will be nullified properly. + ClearOnShutdown(&sInstance); + } + } + return sInstance; +} + +bool +HwcComposer2D::EnableVsync(bool aEnable) +{ + MOZ_ASSERT(NS_IsMainThread()); + if (!mHasHWVsync) { + return false; + } + return mHal->EnableVsync(aEnable) && aEnable; +} + +bool +HwcComposer2D::RegisterHwcEventCallback() +{ + const HwcHALProcs_t cHWCProcs = { + &HookInvalidate, // 1st: void (*invalidate)(...) + &HookVsync, // 2nd: void (*vsync)(...) + &HookHotplug // 3rd: void (*hotplug)(...) + }; + mHasHWVsync = mHal->RegisterHwcEventCallback(cHWCProcs); + return mHasHWVsync; +} + +void +HwcComposer2D::Vsync(int aDisplay, nsecs_t aVsyncTimestamp) +{ + // Only support hardware vsync on kitkat, L and up due to inaccurate timings + // with JellyBean. +#if (ANDROID_VERSION == 19 || ANDROID_VERSION >= 21) + TimeStamp vsyncTime = mozilla::TimeStamp::FromSystemTime(aVsyncTimestamp); + gfxPlatform::GetPlatform()->GetHardwareVsync()->GetGlobalDisplay().NotifyVsync(vsyncTime); +#else + // If this device doesn't support vsync, this function should not be used. + MOZ_ASSERT(false); +#endif +} + +// Called on the "invalidator" thread (run from HAL). +void +HwcComposer2D::Invalidate() +{ + if (!mHal->HasHwc()) { + LOGE("HwcComposer2D::Invalidate failed!"); + return; + } + + MutexAutoLock lock(mLock); + if (mCompositorBridgeParent) { + mCompositorBridgeParent->ScheduleRenderOnCompositorThread(); + } +} + +namespace { +class HotplugEvent : public Runnable { +public: + HotplugEvent(GonkDisplay::DisplayType aType, bool aConnected) + : mType(aType) + , mConnected(aConnected) + { + } + + NS_IMETHOD Run() override + { + RefPtr<nsScreenManagerGonk> screenManager = + nsScreenManagerGonk::GetInstance(); + if (mConnected) { + screenManager->AddScreen(mType); + } else { + screenManager->RemoveScreen(mType); + } + return NS_OK; + } +private: + GonkDisplay::DisplayType mType; + bool mConnected; +}; +} // namespace + +void +HwcComposer2D::Hotplug(int aDisplay, int aConnected) +{ + NS_DispatchToMainThread(new HotplugEvent(GonkDisplay::DISPLAY_EXTERNAL, + aConnected)); +} + +void +HwcComposer2D::SetCompositorBridgeParent(CompositorBridgeParent* aCompositorBridgeParent) +{ + MutexAutoLock lock(mLock); + mCompositorBridgeParent = aCompositorBridgeParent; +} + +bool +HwcComposer2D::ReallocLayerList() +{ + int size = sizeof(HwcList) + + ((mMaxLayerCount + LAYER_COUNT_INCREMENTS) * sizeof(HwcLayer)); + + HwcList* listrealloc = (HwcList*)realloc(mList, size); + + if (!listrealloc) { + return false; + } + + if (!mList) { + //first alloc, initialize + listrealloc->numHwLayers = 0; + listrealloc->flags = 0; + } + + mList = listrealloc; + mMaxLayerCount += LAYER_COUNT_INCREMENTS; + return true; +} + +bool +HwcComposer2D::PrepareLayerList(Layer* aLayer, + const nsIntRect& aClip, + const Matrix& aParentTransform, + bool aFindSidebandStreams) +{ + // NB: we fall off this path whenever there are container layers + // that require intermediate surfaces. That means all the + // GetEffective*() coordinates are relative to the framebuffer. + + bool fillColor = false; + + const nsIntRegion visibleRegion = aLayer->GetLocalVisibleRegion().ToUnknownRegion(); + if (visibleRegion.IsEmpty()) { + return true; + } + + uint8_t opacity = std::min(0xFF, (int)(aLayer->GetEffectiveOpacity() * 256.0)); + if (opacity == 0) { + LOGD("%s Layer has zero opacity; skipping", aLayer->Name()); + return true; + } + + if (!mHal->SupportTransparency() && opacity < 0xFF && !aFindSidebandStreams) { + LOGD("%s Layer has planar semitransparency which is unsupported by hwcomposer", aLayer->Name()); + return false; + } + + if (aLayer->GetMaskLayer() && !aFindSidebandStreams) { + LOGD("%s Layer has MaskLayer which is unsupported by hwcomposer", aLayer->Name()); + return false; + } + + nsIntRect clip; + nsIntRect layerClip = aLayer->GetLocalClipRect().valueOr(ParentLayerIntRect()).ToUnknownRect(); + nsIntRect* layerClipPtr = aLayer->GetLocalClipRect() ? &layerClip : nullptr; + if (!HwcUtils::CalculateClipRect(aParentTransform, + layerClipPtr, + aClip, + &clip)) + { + LOGD("%s Clip rect is empty. Skip layer", aLayer->Name()); + return true; + } + + // HWC supports only the following 2D transformations: + // + // Scaling via the sourceCrop and displayFrame in HwcLayer + // Translation via the sourceCrop and displayFrame in HwcLayer + // Rotation (in square angles only) via the HWC_TRANSFORM_ROT_* flags + // Reflection (horizontal and vertical) via the HWC_TRANSFORM_FLIP_* flags + // + // A 2D transform with PreservesAxisAlignedRectangles() has all the attributes + // above + Matrix layerTransform; + if (!aLayer->GetEffectiveTransform().Is2D(&layerTransform) || + !layerTransform.PreservesAxisAlignedRectangles()) { + LOGD("Layer EffectiveTransform has a 3D transform or a non-square angle rotation"); + return false; + } + + Matrix layerBufferTransform; + if (!aLayer->GetEffectiveTransformForBuffer().Is2D(&layerBufferTransform) || + !layerBufferTransform.PreservesAxisAlignedRectangles()) { + LOGD("Layer EffectiveTransformForBuffer has a 3D transform or a non-square angle rotation"); + return false; + } + + if (ContainerLayer* container = aLayer->AsContainerLayer()) { + if (container->UseIntermediateSurface() && !aFindSidebandStreams) { + LOGD("Container layer needs intermediate surface"); + return false; + } + AutoTArray<Layer*, 12> children; + container->SortChildrenBy3DZOrder(children); + + for (uint32_t i = 0; i < children.Length(); i++) { + if (!PrepareLayerList(children[i], clip, layerTransform, aFindSidebandStreams) && + !aFindSidebandStreams) { + return false; + } + } + return true; + } + + LayerRenderState state = aLayer->GetRenderState(); + +#if ANDROID_VERSION >= 21 + if (!state.GetGrallocBuffer() && !state.GetSidebandStream().IsValid()) { +#else + if (!state.GetGrallocBuffer()) { +#endif + if (aLayer->AsColorLayer() && mColorFill) { + fillColor = true; + } else { + LOGD("%s Layer doesn't have a gralloc buffer", aLayer->Name()); + return false; + } + } + + nsIntRect visibleRect = visibleRegion.GetBounds(); + + nsIntRect bufferRect; + if (fillColor) { + bufferRect = nsIntRect(visibleRect); + } else { + nsIntRect layerRect; + if (state.mHasOwnOffset) { + bufferRect = nsIntRect(state.mOffset.x, state.mOffset.y, + state.mSize.width, state.mSize.height); + layerRect = bufferRect; + } else { + //Since the buffer doesn't have its own offset, assign the whole + //surface size as its buffer bounds + bufferRect = nsIntRect(0, 0, state.mSize.width, state.mSize.height); + layerRect = bufferRect; + if (aLayer->GetType() == Layer::TYPE_IMAGE) { + ImageLayer* imageLayer = static_cast<ImageLayer*>(aLayer); + if(imageLayer->GetScaleMode() != ScaleMode::SCALE_NONE) { + layerRect = nsIntRect(0, 0, imageLayer->GetScaleToSize().width, imageLayer->GetScaleToSize().height); + } + } + } + // In some cases the visible rect assigned to the layer can be larger + // than the layer's surface, e.g., an ImageLayer with a small Image + // in it. + visibleRect.IntersectRect(visibleRect, layerRect); + } + + // Buffer rotation is not to be confused with the angled rotation done by a transform matrix + // It's a fancy PaintedLayer feature used for scrolling + if (state.BufferRotated()) { + LOGD("%s Layer has a rotated buffer", aLayer->Name()); + return false; + } + + const bool needsYFlip = state.OriginBottomLeft() ? true + : false; + + hwc_rect_t sourceCrop, displayFrame; + if(!HwcUtils::PrepareLayerRects(visibleRect, + layerTransform, + layerBufferTransform, + clip, + bufferRect, + needsYFlip, + &(sourceCrop), + &(displayFrame))) + { + return true; + } + + // OK! We can compose this layer with hwc. + int current = mList ? mList->numHwLayers : 0; + + // Do not compose any layer below full-screen Opaque layer + // Note: It can be generalized to non-fullscreen Opaque layers. + bool isOpaque = opacity == 0xFF && + (state.mFlags & LayerRenderStateFlags::OPAQUE); + // Currently we perform opacity calculation using the *bounds* of the layer. + // We can only make this assumption if we're not dealing with a complex visible region. + bool isSimpleVisibleRegion = visibleRegion.Contains(visibleRect); + if (current && isOpaque && isSimpleVisibleRegion) { + nsIntRect displayRect = nsIntRect(displayFrame.left, displayFrame.top, + displayFrame.right - displayFrame.left, displayFrame.bottom - displayFrame.top); + if (displayRect.Contains(mScreenRect)) { + // In z-order, all previous layers are below + // the current layer. We can ignore them now. + mList->numHwLayers = current = 0; + mHwcLayerMap.Clear(); + } + } + + if (!mList || current >= mMaxLayerCount) { + if (!ReallocLayerList() || current >= mMaxLayerCount) { + LOGE("PrepareLayerList failed! Could not increase the maximum layer count"); + return false; + } + } + + HwcLayer& hwcLayer = mList->hwLayers[current]; + hwcLayer.displayFrame = displayFrame; + mHal->SetCrop(hwcLayer, sourceCrop); + buffer_handle_t handle = nullptr; +#if ANDROID_VERSION >= 21 + if (state.GetSidebandStream().IsValid()) { + handle = state.GetSidebandStream().GetRawNativeHandle(); + } else if (state.GetGrallocBuffer()) { + handle = state.GetGrallocBuffer()->getNativeBuffer()->handle; + } +#else + if (state.GetGrallocBuffer()) { + handle = state.GetGrallocBuffer()->getNativeBuffer()->handle; + } +#endif + hwcLayer.handle = handle; + + hwcLayer.flags = 0; + hwcLayer.hints = 0; + hwcLayer.blending = isOpaque ? HWC_BLENDING_NONE : HWC_BLENDING_PREMULT; +#if ANDROID_VERSION >= 17 + hwcLayer.compositionType = HWC_FRAMEBUFFER; +#if ANDROID_VERSION >= 21 + if (state.GetSidebandStream().IsValid()) { + hwcLayer.compositionType = HWC_SIDEBAND; + } +#endif + hwcLayer.acquireFenceFd = -1; + hwcLayer.releaseFenceFd = -1; +#if ANDROID_VERSION >= 18 + hwcLayer.planeAlpha = opacity; +#endif +#else + hwcLayer.compositionType = HwcUtils::HWC_USE_COPYBIT; +#endif + + if (!fillColor) { + if (state.FormatRBSwapped()) { + if (!mRBSwapSupport) { + LOGD("No R/B swap support in H/W Composer"); + return false; + } + hwcLayer.flags |= HwcUtils::HWC_FORMAT_RB_SWAP; + } + + // Translation and scaling have been addressed in PrepareLayerRects(). + // Given the above and that we checked for PreservesAxisAlignedRectangles() + // the only possible transformations left to address are + // square angle rotation and horizontal/vertical reflection. + // + // The rotation and reflection permutations total 16 but can be + // reduced to 8 transformations after eliminating redundancies. + // + // All matrices represented here are in the form + // + // | xx xy | + // | yx yy | + // + // And ignore scaling. + // + // Reflection is applied before rotation + gfx::Matrix rotation = layerTransform; + // Compute fuzzy zero like PreservesAxisAlignedRectangles() + if (fabs(rotation._11) < 1e-6) { + if (rotation._21 < 0) { + if (rotation._12 > 0) { + // 90 degree rotation + // + // | 0 -1 | + // | 1 0 | + // + hwcLayer.transform = HWC_TRANSFORM_ROT_90; + LOGD("Layer rotated 90 degrees"); + } + else { + // Horizontal reflection then 90 degree rotation + // + // | 0 -1 | | -1 0 | = | 0 -1 | + // | 1 0 | | 0 1 | | -1 0 | + // + // same as vertical reflection then 270 degree rotation + // + // | 0 1 | | 1 0 | = | 0 -1 | + // | -1 0 | | 0 -1 | | -1 0 | + // + hwcLayer.transform = HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_FLIP_H; + LOGD("Layer vertically reflected then rotated 270 degrees"); + } + } else { + if (rotation._12 < 0) { + // 270 degree rotation + // + // | 0 1 | + // | -1 0 | + // + hwcLayer.transform = HWC_TRANSFORM_ROT_270; + LOGD("Layer rotated 270 degrees"); + } + else { + // Vertical reflection then 90 degree rotation + // + // | 0 1 | | -1 0 | = | 0 1 | + // | -1 0 | | 0 1 | | 1 0 | + // + // Same as horizontal reflection then 270 degree rotation + // + // | 0 -1 | | 1 0 | = | 0 1 | + // | 1 0 | | 0 -1 | | 1 0 | + // + hwcLayer.transform = HWC_TRANSFORM_ROT_90 | HWC_TRANSFORM_FLIP_V; + LOGD("Layer horizontally reflected then rotated 270 degrees"); + } + } + } else if (rotation._11 < 0) { + if (rotation._22 > 0) { + // Horizontal reflection + // + // | -1 0 | + // | 0 1 | + // + hwcLayer.transform = HWC_TRANSFORM_FLIP_H; + LOGD("Layer rotated 180 degrees"); + } + else { + // 180 degree rotation + // + // | -1 0 | + // | 0 -1 | + // + // Same as horizontal and vertical reflection + // + // | -1 0 | | 1 0 | = | -1 0 | + // | 0 1 | | 0 -1 | | 0 -1 | + // + hwcLayer.transform = HWC_TRANSFORM_ROT_180; + LOGD("Layer rotated 180 degrees"); + } + } else { + if (rotation._22 < 0) { + // Vertical reflection + // + // | 1 0 | + // | 0 -1 | + // + hwcLayer.transform = HWC_TRANSFORM_FLIP_V; + LOGD("Layer rotated 180 degrees"); + } + else { + // No rotation or reflection + // + // | 1 0 | + // | 0 1 | + // + hwcLayer.transform = 0; + } + } + + const bool needsYFlip = state.OriginBottomLeft() ? true + : false; + + if (needsYFlip) { + // Invert vertical reflection flag if it was already set + hwcLayer.transform ^= HWC_TRANSFORM_FLIP_V; + } + hwc_region_t region; + if (visibleRegion.GetNumRects() > 1) { + mVisibleRegions.push_back(HwcUtils::RectVector()); + HwcUtils::RectVector* visibleRects = &(mVisibleRegions.back()); + bool isVisible = false; + if(!HwcUtils::PrepareVisibleRegion(visibleRegion, + layerTransform, + layerBufferTransform, + clip, + bufferRect, + visibleRects, + isVisible)) { + LOGD("A region of layer is too small to be rendered by HWC"); + return false; + } + if (!isVisible) { + // Layer is not visible, no need to render it + return true; + } + region.numRects = visibleRects->size(); + region.rects = &((*visibleRects)[0]); + } else { + region.numRects = 1; + region.rects = &(hwcLayer.displayFrame); + } + hwcLayer.visibleRegionScreen = region; + } else { + hwcLayer.flags |= HwcUtils::HWC_COLOR_FILL; + ColorLayer* colorLayer = aLayer->AsColorLayer(); + if (colorLayer->GetColor().a < 1.0) { + LOGD("Color layer has semitransparency which is unsupported"); + return false; + } + hwcLayer.transform = colorLayer->GetColor().ToABGR(); + } + +#if ANDROID_VERSION >= 21 + if (aFindSidebandStreams && hwcLayer.compositionType == HWC_SIDEBAND) { + mCachedSidebandLayers.AppendElement(hwcLayer); + } +#endif + + mHwcLayerMap.AppendElement(static_cast<LayerComposite*>(aLayer->ImplData())); + mList->numHwLayers++; + return true; +} + + +#if ANDROID_VERSION >= 17 +bool +HwcComposer2D::TryHwComposition(nsScreenGonk* aScreen) +{ + DisplaySurface* dispSurface = aScreen->GetDisplaySurface(); + + if (!(dispSurface && dispSurface->lastHandle)) { + LOGD("H/W Composition failed. DispSurface not initialized."); + return false; + } + + // Add FB layer + int idx = mList->numHwLayers++; + if (idx >= mMaxLayerCount) { + if (!ReallocLayerList() || idx >= mMaxLayerCount) { + LOGE("TryHwComposition failed! Could not add FB layer"); + return false; + } + } + + Prepare(dispSurface->lastHandle, -1, aScreen); + + /* Possible composition paths, after hwc prepare: + 1. GPU Composition + 2. BLIT Composition + 3. Full OVERLAY Composition + 4. Partial OVERLAY Composition (GPU + OVERLAY) */ + + bool gpuComposite = false; + bool blitComposite = false; + bool overlayComposite = true; + + for (int j=0; j < idx; j++) { + if (mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER || + mList->hwLayers[j].compositionType == HWC_BLIT) { + // Full OVERLAY composition is not possible on this frame + // It is either GPU / BLIT / partial OVERLAY composition. + overlayComposite = false; + break; + } + } + + if (!overlayComposite) { + for (int k=0; k < idx; k++) { + switch (mList->hwLayers[k].compositionType) { + case HWC_FRAMEBUFFER: + gpuComposite = true; + break; + case HWC_BLIT: + blitComposite = true; + break; +#if ANDROID_VERSION >= 21 + case HWC_SIDEBAND: +#endif + case HWC_OVERLAY: { + // HWC will compose HWC_OVERLAY layers in partial + // Overlay Composition, set layer composition flag + // on mapped LayerComposite to skip GPU composition + mHwcLayerMap[k]->SetLayerComposited(true); + + uint8_t opacity = std::min(0xFF, (int)(mHwcLayerMap[k]->GetLayer()->GetEffectiveOpacity() * 256.0)); + if ((mList->hwLayers[k].hints & HWC_HINT_CLEAR_FB) && + (opacity == 0xFF)) { + // Clear visible rect on FB with transparent pixels. + hwc_rect_t r = mList->hwLayers[k].displayFrame; + mHwcLayerMap[k]->SetClearRect(nsIntRect(r.left, r.top, + r.right - r.left, + r.bottom - r.top)); + } + break; + } + default: + break; + } + } + + if (gpuComposite) { + // GPU or partial OVERLAY Composition + return false; + } else if (blitComposite) { + // BLIT Composition, flip DispSurface target + GetGonkDisplay()->UpdateDispSurface(aScreen->GetEGLDisplay(), aScreen->GetEGLSurface()); + DisplaySurface* dispSurface = aScreen->GetDisplaySurface(); + if (!dispSurface) { + LOGE("H/W Composition failed. NULL DispSurface."); + return false; + } + mList->hwLayers[idx].handle = dispSurface->lastHandle; + mList->hwLayers[idx].acquireFenceFd = dispSurface->GetPrevDispAcquireFd(); + } + } + + // BLIT or full OVERLAY Composition + return Commit(aScreen); +} + +bool +HwcComposer2D::Render(nsIWidget* aWidget) +{ + nsScreenGonk* screen = static_cast<nsWindow*>(aWidget)->GetScreen(); + + // HWC module does not exist or mList is not created yet. + if (!mHal->HasHwc() || !mList) { + return GetGonkDisplay()->SwapBuffers(screen->GetEGLDisplay(), screen->GetEGLSurface()); + } else if (!mList && !ReallocLayerList()) { + LOGE("Cannot realloc layer list"); + return false; + } + + DisplaySurface* dispSurface = screen->GetDisplaySurface(); + if (!dispSurface) { + LOGE("H/W Composition failed. DispSurface not initialized."); + return false; + } + + if (mPrepared) { + // No mHwc prepare, if already prepared in current draw cycle + mList->hwLayers[mList->numHwLayers - 1].handle = dispSurface->lastHandle; + mList->hwLayers[mList->numHwLayers - 1].acquireFenceFd = dispSurface->GetPrevDispAcquireFd(); + } else { + // Update screen rect to handle a case that TryRenderWithHwc() is not called. + mScreenRect = screen->GetNaturalBounds().ToUnknownRect(); + + mList->flags = HWC_GEOMETRY_CHANGED; + mList->numHwLayers = 2; + mList->hwLayers[0].hints = 0; + mList->hwLayers[0].compositionType = HWC_FRAMEBUFFER; + mList->hwLayers[0].flags = HWC_SKIP_LAYER; + mList->hwLayers[0].backgroundColor = {0}; + mList->hwLayers[0].acquireFenceFd = -1; + mList->hwLayers[0].releaseFenceFd = -1; + mList->hwLayers[0].displayFrame = {0, 0, mScreenRect.width, mScreenRect.height}; + +#if ANDROID_VERSION >= 21 + // Prepare layers for sideband streams + const uint32_t len = mCachedSidebandLayers.Length(); + for (uint32_t i = 0; i < len; ++i) { + ++mList->numHwLayers; + mList->hwLayers[i+1] = mCachedSidebandLayers[i]; + } +#endif + Prepare(dispSurface->lastHandle, dispSurface->GetPrevDispAcquireFd(), screen); + } + + // GPU or partial HWC Composition + return Commit(screen); +} + +void +HwcComposer2D::Prepare(buffer_handle_t dispHandle, int fence, nsScreenGonk* screen) +{ + if (mPrepared) { + LOGE("Multiple hwc prepare calls!"); + } + hwc_rect_t dispRect = {0, 0, mScreenRect.width, mScreenRect.height}; + mHal->Prepare(mList, screen->GetDisplayType(), dispRect, dispHandle, fence); + mPrepared = true; +} + +bool +HwcComposer2D::Commit(nsScreenGonk* aScreen) +{ + for (uint32_t j=0; j < (mList->numHwLayers - 1); j++) { + mList->hwLayers[j].acquireFenceFd = -1; + if (mHwcLayerMap.IsEmpty() || + (mList->hwLayers[j].compositionType == HWC_FRAMEBUFFER)) { + continue; + } + LayerRenderState state = mHwcLayerMap[j]->GetLayer()->GetRenderState(); + if (!state.mTexture) { + continue; + } + FenceHandle fence = state.mTexture->GetAndResetAcquireFenceHandle(); + if (fence.IsValid()) { + RefPtr<FenceHandle::FdObj> fdObj = fence.GetAndResetFdObj(); + mList->hwLayers[j].acquireFenceFd = fdObj->GetAndResetFd(); + } + } + + int err = mHal->Set(mList, aScreen->GetDisplayType()); + + mPrevRetireFence.TransferToAnotherFenceHandle(mPrevDisplayFence); + + for (uint32_t j=0; j < (mList->numHwLayers - 1); j++) { + if (mList->hwLayers[j].releaseFenceFd >= 0) { + int fd = mList->hwLayers[j].releaseFenceFd; + mList->hwLayers[j].releaseFenceFd = -1; + RefPtr<FenceHandle::FdObj> fdObj = new FenceHandle::FdObj(fd); + FenceHandle fence(fdObj); + + LayerRenderState state = mHwcLayerMap[j]->GetLayer()->GetRenderState(); + if (!state.mTexture) { + continue; + } + state.mTexture->SetReleaseFenceHandle(fence); + } + } + + if (mList->retireFenceFd >= 0) { + mPrevRetireFence = FenceHandle(new FenceHandle::FdObj(mList->retireFenceFd)); + } + + // Set DisplaySurface layer fence + DisplaySurface* displaySurface = aScreen->GetDisplaySurface(); + displaySurface->setReleaseFenceFd(mList->hwLayers[mList->numHwLayers - 1].releaseFenceFd); + mList->hwLayers[mList->numHwLayers - 1].releaseFenceFd = -1; + + mPrepared = false; + return !err; +} +#else +bool +HwcComposer2D::TryHwComposition(nsScreenGonk* aScreen) +{ + mHal->SetEGLInfo(aScreen->GetEGLDisplay(), aScreen->GetEGLSurface()); + return !mHal->Set(mList, aScreen->GetDisplayType()); +} + +bool +HwcComposer2D::Render(nsIWidget* aWidget) +{ + nsScreenGonk* screen = static_cast<nsWindow*>(aWidget)->GetScreen(); + return GetGonkDisplay()->SwapBuffers(screen->GetEGLDisplay(), screen->GetEGLSurface()); +} +#endif + +bool +HwcComposer2D::TryRenderWithHwc(Layer* aRoot, + nsIWidget* aWidget, + bool aGeometryChanged, + bool aHasImageHostOverlays) +{ + if (!mHal->HasHwc()) { + return false; + } + + nsScreenGonk* screen = static_cast<nsWindow*>(aWidget)->GetScreen(); + + if (mList) { + mList->flags = mHal->GetGeometryChangedFlag(aGeometryChanged); + mList->numHwLayers = 0; + mHwcLayerMap.Clear(); + } + + if (mPrepared) { + mHal->ResetHwc(); + mPrepared = false; + } + + // XXX: The clear() below means all rect vectors will be have to be + // reallocated. We may want to avoid this if possible + mVisibleRegions.clear(); + + mScreenRect = screen->GetNaturalBounds().ToUnknownRect(); + MOZ_ASSERT(mHwcLayerMap.IsEmpty()); + mCachedSidebandLayers.Clear(); + if (!PrepareLayerList(aRoot, + mScreenRect, + gfx::Matrix(), + /* aFindSidebandStreams */ false)) + { + mHwcLayerMap.Clear(); + LOGD("Render aborted. Fallback to GPU Composition"); + if (aHasImageHostOverlays) { + LOGD("Prepare layers of SidebandStreams"); + // Failed to create a layer list for hwc. But we need the list + // only for handling sideband streams. Traverse layer tree without + // some early returns to make sure we can find all the layers. + // It is the best wrong thing that we can do. + PrepareLayerList(aRoot, + mScreenRect, + gfx::Matrix(), + /* aFindSidebandStreams */ true); + // Reset mPrepared to false, since we already fell back to + // gpu composition. + mPrepared = false; + } + return false; + } + + // Send data to LayerScope for debugging + SendtoLayerScope(); + + if (!TryHwComposition(screen)) { + LOGD("Full HWC Composition failed. Fallback to GPU Composition or partial OVERLAY Composition"); + LayerScope::CleanLayer(); + return false; + } + + LOGD("Frame rendered"); + return true; +} + +void +HwcComposer2D::SendtoLayerScope() +{ + if (!LayerScope::CheckSendable()) { + return; + } + + const int len = mList->numHwLayers; + for (int i = 0; i < len; ++i) { + LayerComposite* layer = mHwcLayerMap[i]; + const hwc_rect_t r = mList->hwLayers[i].displayFrame; + LayerScope::SendLayer(layer, r.right - r.left, r.bottom - r.top); + } +} + +} // namespace mozilla |