diff options
30 files changed, 2984 insertions, 91 deletions
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 4558fe87c6..ed2f30aaf5 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -93,6 +93,7 @@ cc_library_shared { "IProducerListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", + "LayerDebugInfo.cpp", "LayerState.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 0a0d112af6..8e7f814313 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -28,6 +28,7 @@ #include <gui/IGraphicBufferProducer.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> +#include <gui/LayerDebugInfo.h> #include <private/gui/LayerState.h> @@ -469,6 +470,36 @@ public: return result; } + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const + { + if (!outLayers) { + return UNEXPECTED_NULL; + } + + Parcel data, reply; + + status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + return err; + } + + err = remote()->transact(BnSurfaceComposer::GET_LAYER_DEBUG_INFO, data, &reply); + if (err != NO_ERROR) { + return err; + } + + int32_t result = 0; + err = reply.readInt32(&result); + if (err != NO_ERROR) { + return err; + } + if (result != NO_ERROR) { + return result; + } + + outLayers->clear(); + return reply.readParcelableVector(outLayers); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -763,6 +794,17 @@ status_t BnSurfaceComposer::onTransact( } return injectVSync(when); } + case GET_LAYER_DEBUG_INFO: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + std::vector<LayerDebugInfo> outLayers; + status_t result = getLayerDebugInfo(&outLayers); + reply->writeInt32(result); + if (result == NO_ERROR) + { + result = reply->writeParcelableVector(outLayers); + } + return result; + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp new file mode 100644 index 0000000000..57ddde075a --- /dev/null +++ b/libs/gui/LayerDebugInfo.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2017 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 <gui/LayerDebugInfo.h> + +#include <ui/DebugUtils.h> + +#include <binder/Parcel.h> + +#include <utils/String8.h> + +using namespace android; + +#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false) + +namespace android { + +status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { + RETURN_ON_ERROR(parcel->writeCString(mName.c_str())); + RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str())); + RETURN_ON_ERROR(parcel->writeCString(mType.c_str())); + RETURN_ON_ERROR(parcel->write(mTransparentRegion)); + RETURN_ON_ERROR(parcel->write(mVisibleRegion)); + RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion)); + RETURN_ON_ERROR(parcel->writeUint32(mLayerStack)); + RETURN_ON_ERROR(parcel->writeFloat(mX)); + RETURN_ON_ERROR(parcel->writeFloat(mY)); + RETURN_ON_ERROR(parcel->writeUint32(mZ)); + RETURN_ON_ERROR(parcel->writeInt32(mWidth)); + RETURN_ON_ERROR(parcel->writeInt32(mHeight)); + RETURN_ON_ERROR(parcel->write(mCrop)); + RETURN_ON_ERROR(parcel->write(mFinalCrop)); + RETURN_ON_ERROR(parcel->writeFloat(mAlpha)); + RETURN_ON_ERROR(parcel->writeUint32(mFlags)); + RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat)); + RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace))); + for (size_t index = 0; index < 4; index++) { + RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2])); + } + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride)); + RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat)); + RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames)); + RETURN_ON_ERROR(parcel->writeBool(mRefreshPending)); + RETURN_ON_ERROR(parcel->writeBool(mIsOpaque)); + RETURN_ON_ERROR(parcel->writeBool(mContentDirty)); + return NO_ERROR; +} + +status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { + mName = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + mParentName = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + mType = parcel->readCString(); + RETURN_ON_ERROR(parcel->errorCheck()); + RETURN_ON_ERROR(parcel->read(mTransparentRegion)); + RETURN_ON_ERROR(parcel->read(mVisibleRegion)); + RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion)); + RETURN_ON_ERROR(parcel->readUint32(&mLayerStack)); + RETURN_ON_ERROR(parcel->readFloat(&mX)); + RETURN_ON_ERROR(parcel->readFloat(&mY)); + RETURN_ON_ERROR(parcel->readUint32(&mZ)); + RETURN_ON_ERROR(parcel->readInt32(&mWidth)); + RETURN_ON_ERROR(parcel->readInt32(&mHeight)); + RETURN_ON_ERROR(parcel->read(mCrop)); + RETURN_ON_ERROR(parcel->read(mFinalCrop)); + RETURN_ON_ERROR(parcel->readFloat(&mAlpha)); + RETURN_ON_ERROR(parcel->readUint32(&mFlags)); + RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat)); + // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways? + mDataSpace = static_cast<android_dataspace>(parcel->readUint32()); + RETURN_ON_ERROR(parcel->errorCheck()); + for (size_t index = 0; index < 4; index++) { + RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2])); + } + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride)); + RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat)); + RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames)); + RETURN_ON_ERROR(parcel->readBool(&mRefreshPending)); + RETURN_ON_ERROR(parcel->readBool(&mIsOpaque)); + RETURN_ON_ERROR(parcel->readBool(&mContentDirty)); + return NO_ERROR; +} + +std::string to_string(const LayerDebugInfo& info) { + String8 result; + + result.appendFormat("+ %s (%s)\n", info.mType.c_str(), info.mName.c_str()); + info.mTransparentRegion.dump(result, "TransparentRegion"); + info.mVisibleRegion.dump(result, "VisibleRegion"); + info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion"); + + result.appendFormat(" layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ", + info.mLayerStack, info.mZ, static_cast<double>(info.mX), static_cast<double>(info.mY), + info.mWidth, info.mHeight); + + result.appendFormat("crop=%s, finalCrop=%s, ", + to_string(info.mCrop).c_str(), to_string(info.mFinalCrop).c_str()); + result.appendFormat("isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty); + result.appendFormat("dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str()); + result.appendFormat("pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str()); + result.appendFormat("alpha=%.3f, flags=0x%08x, ", + static_cast<double>(info.mAlpha), info.mFlags); + result.appendFormat("tr=[%.2f, %.2f][%.2f, %.2f]", + static_cast<double>(info.mMatrix[0][0]), static_cast<double>(info.mMatrix[0][1]), + static_cast<double>(info.mMatrix[1][0]), static_cast<double>(info.mMatrix[1][1])); + result.append("\n"); + result.appendFormat(" parent=%s\n", info.mParentName.c_str()); + result.appendFormat(" activeBuffer=[%4ux%4u:%4u,%s],", + info.mActiveBufferWidth, info.mActiveBufferHeight, + info.mActiveBufferStride, + decodePixelFormat(info.mActiveBufferFormat).c_str()); + result.appendFormat(" queued-frames=%d, mRefreshPending=%d", + info.mNumQueuedFrames, info.mRefreshPending); + result.append("\n"); + return std::string(result.c_str()); +} + +} // android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index f80ba000b4..b2267426a8 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -39,6 +39,7 @@ struct ComposerState; struct DisplayState; struct DisplayInfo; struct DisplayStatInfo; +class LayerDebugInfo; class HdrCapabilities; class IDisplayEventConnection; class IGraphicBufferProducer; @@ -195,6 +196,12 @@ public: virtual status_t enableVSyncInjections(bool enable) = 0; virtual status_t injectVSync(nsecs_t when) = 0; + + /* Gets the list of active layers in Z order for debugging purposes + * + * Requires the ACCESS_SURFACE_FLINGER permission. + */ + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const = 0; }; // ---------------------------------------------------------------------------- @@ -229,6 +236,7 @@ public: SET_ACTIVE_COLOR_MODE, ENABLE_VSYNC_INJECTIONS, INJECT_VSYNC, + GET_LAYER_DEBUG_INFO, CREATE_SCOPED_CONNECTION }; diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h new file mode 100644 index 0000000000..8453e043ef --- /dev/null +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 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. + */ + +#pragma once + +#include <binder/Parcelable.h> + +#include <ui/PixelFormat.h> +#include <ui/Region.h> + +#include <string> + +namespace android { + +/* Class for transporting debug info from SurfaceFlinger to authorized + * recipients. The class is intended to be a data container. There are + * no getters or setters. + */ +class LayerDebugInfo : public Parcelable { +public: + LayerDebugInfo() = default; + LayerDebugInfo(const LayerDebugInfo&) = default; + virtual ~LayerDebugInfo() = default; + + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + + std::string mName = std::string("NOT FILLED"); + std::string mParentName = std::string("NOT FILLED"); + std::string mType = std::string("NOT FILLED"); + Region mTransparentRegion = Region::INVALID_REGION; + Region mVisibleRegion = Region::INVALID_REGION; + Region mSurfaceDamageRegion = Region::INVALID_REGION; + uint32_t mLayerStack = 0; + float mX = 0.f; + float mY = 0.f; + uint32_t mZ = 0 ; + int32_t mWidth = -1; + int32_t mHeight = -1; + Rect mCrop = Rect::INVALID_RECT; + Rect mFinalCrop = Rect::INVALID_RECT; + float mAlpha = 0.f; + uint32_t mFlags = 0; + PixelFormat mPixelFormat = PIXEL_FORMAT_NONE; + android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN; + // Row-major transform matrix (SurfaceControl::setMatrix()) + float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}}; + int32_t mActiveBufferWidth = -1; + int32_t mActiveBufferHeight = -1; + int32_t mActiveBufferStride = 0; + PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE; + int32_t mNumQueuedFrames = -1; + bool mRefreshPending = false; + bool mIsOpaque = false; + bool mContentDirty = false; +}; + +std::string to_string(const LayerDebugInfo& info); + +} // namespace android diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index 8bb705cf77..c15209d32c 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -90,6 +90,16 @@ public: status_t setFlags(uint32_t flags, uint32_t mask); status_t setTransparentRegionHint(const Region& transparent); status_t setAlpha(float alpha=1.0f); + + // Experimentarily it appears that the matrix transforms the + // on-screen rectangle and it's contents before the position is + // applied. + // + // TODO: Test with other combinations to find approximate transformation rules. + // + // For example: + // Layer sized (W,H) set to position (x,y) with matrix M=[-1, 0, 0, 1] (Horizontal flip) gives + // [((0, 0), (W, H)) x M] + (x,y) = ((-W, 0), (0, H)) + (x,y) = ((-W + x, y), (x, H+y)) status_t setMatrix(float dsdx, float dtdx, float dtdy, float dsdy); status_t setCrop(const Rect& crop); status_t setFinalCrop(const Rect& crop); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index e18af17bde..45e95a593b 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -539,6 +539,9 @@ public: return NO_ERROR; } status_t injectVSync(nsecs_t /*when*/) override { return NO_ERROR; } + status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* /*layers*/) const override { + return NO_ERROR; + } protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp index 6e31d2a2c4..2d72944665 100644 --- a/libs/ui/DebugUtils.cpp +++ b/libs/ui/DebugUtils.cpp @@ -16,10 +16,13 @@ #include <ui/DebugUtils.h> #include <ui/PixelFormat.h> +#include <ui/Rect.h> #include <android-base/stringprintf.h> #include <string> +using android::base::StringPrintf; + std::string decodeStandard(android_dataspace dataspace) { const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK); switch (dataspaceSelect) { @@ -262,3 +265,7 @@ std::string decodePixelFormat(android::PixelFormat format) { return android::base::StringPrintf("Unknown %#08x", format); } } + +std::string to_string(const android::Rect& rect) { + return StringPrintf("(%4d,%4d,%4d,%4d)", rect.left, rect.top, rect.right, rect.bottom); +} diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h index 30f4a59fe0..dad9446b3a 100644 --- a/libs/ui/include/ui/DebugUtils.h +++ b/libs/ui/include/ui/DebugUtils.h @@ -21,9 +21,14 @@ #include <string> +namespace android { +class Rect; +} + std::string decodeStandard(android_dataspace dataspace); std::string decodeTransfer(android_dataspace dataspace); std::string decodeRange(android_dataspace dataspace); std::string dataspaceDetails(android_dataspace dataspace); std::string decodeColorMode(android_color_mode colormode); std::string decodePixelFormat(android::PixelFormat format); +std::string to_string(const android::Rect& rect); diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index cc93105543..4775e4ef54 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -2,3 +2,5 @@ cc_library_static { name: "libsurfaceflingerincludes", export_include_dirs: ["."], } + +subdirs = ["tests/fakehwc"]
\ No newline at end of file diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index e34fa163c4..704b17ef43 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -157,15 +157,11 @@ void Composer::CommandWriter::writeBufferMetadata( write64(metadata.usage); } -Composer::Composer(bool useVrComposer) +Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize), - mIsUsingVrComposer(useVrComposer) + mIsUsingVrComposer(serviceName == std::string("vr")) { - if (mIsUsingVrComposer) { - mComposer = IComposer::getService("vr"); - } else { - mComposer = IComposer::getService(); // use default name - } + mComposer = IComposer::getService(serviceName); if (mComposer == nullptr) { LOG_ALWAYS_FATAL("failed to get hwcomposer service"); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 96dd833cd5..40d2a4c3aa 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -136,7 +136,7 @@ private: // Composer is a wrapper to IComposer, a proxy to server-side composer. class Composer { public: - Composer(bool useVrComposer); + Composer(const std::string& serviceName); std::vector<IComposer::Capability> getCapabilities(); std::string dumpDebugInfo(); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 270a73228b..b749ce630d 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -88,8 +88,8 @@ namespace Hwc2 = android::Hwc2; // Device methods -Device::Device(bool useVrComposer) - : mComposer(std::make_unique<Hwc2::Composer>(useVrComposer)), +Device::Device(const std::string& serviceName) + : mComposer(std::make_unique<Hwc2::Composer>(serviceName)), mCapabilities(), mDisplays(), mHotplug(), diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 404bb284c5..9bcda1eb0a 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -63,10 +63,9 @@ typedef std::function<void(std::shared_ptr<Display>, nsecs_t)> VsyncCallback; class Device { public: - // useVrComposer is passed to the composer HAL. When true, the composer HAL - // will use the vr composer service, otherwise it uses the real hardware - // composer. - Device(bool useVrComposer); + // Service name is expected to be 'default' or 'vr' for normal use. + // 'vr' will slightly modify the behavior of the mComposer. + Device(const std::string& serviceName); ~Device(); friend class HWC2::Display; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index ac2dde29d4..abf7dd12d0 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -59,7 +59,7 @@ namespace android { // --------------------------------------------------------------------------- -HWComposer::HWComposer(bool useVrComposer) +HWComposer::HWComposer(const std::string& serviceName) : mHwcDevice(), mDisplayData(2), mFreeDisplaySlots(), @@ -74,7 +74,7 @@ HWComposer::HWComposer(bool useVrComposer) mVSyncCounts[i] = 0; } - loadHwcModule(useVrComposer); + loadHwcModule(serviceName); } HWComposer::~HWComposer() {} @@ -103,10 +103,11 @@ void HWComposer::setEventHandler(EventHandler* handler) } // Load and prepare the hardware composer module. Sets mHwc. -void HWComposer::loadHwcModule(bool useVrComposer) +void HWComposer::loadHwcModule(const std::string& serviceName) { ALOGV("loadHwcModule"); - mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer); + mHwcDevice = std::make_unique<HWC2::Device>(serviceName); + mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount(); } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 7463362c35..3dfb65b1e3 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -75,10 +75,9 @@ public: virtual ~EventHandler() {} }; - // useVrComposer is passed to the composer HAL. When true, the composer HAL - // will use the vr composer service, otherwise it uses the real hardware - // composer. - HWComposer(bool useVrComposer); + // Uses the named composer service. Valid choices for normal use + // are 'default' and 'vr'. + HWComposer(const std::string& serviceName); ~HWComposer(); @@ -170,7 +169,7 @@ public: private: static const int32_t VIRTUAL_DISPLAY_ID_BASE = 2; - void loadHwcModule(bool useVrComposer); + void loadHwcModule(const std::string& serviceName); bool isValidDisplay(int32_t displayId) const; static void validateChange(HWC2::Composition from, HWC2::Composition to); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 8eb406747a..3903a5546f 100755 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -40,6 +40,7 @@ #include <gui/BufferItem.h> #include <gui/BufferQueue.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include "clz.h" @@ -2363,71 +2364,51 @@ void Layer::updateTransformHint(const sp<const DisplayDevice>& hw) const { // debugging // ---------------------------------------------------------------------------- -void Layer::dump(String8& result, Colorizer& colorizer) const -{ - const Layer::State& s(getDrawingState()); - - colorizer.colorize(result, Colorizer::GREEN); - result.appendFormat( - "+ %s %p (%s)\n", - getTypeId(), this, getName().string()); - colorizer.reset(result); - - s.activeTransparentRegion.dump(result, "transparentRegion"); - visibleRegion.dump(result, "visibleRegion"); - surfaceDamageRegion.dump(result, "surfaceDamageRegion"); - sp<Client> client(mClientRef.promote()); - PixelFormat pf = PIXEL_FORMAT_UNKNOWN; - const sp<GraphicBuffer>& buffer(getActiveBuffer()); - if (buffer != NULL) { - pf = buffer->getPixelFormat(); - } - +LayerDebugInfo Layer::getLayerDebugInfo() const { + LayerDebugInfo info; + const Layer::State& ds = getDrawingState(); + info.mName = getName(); sp<Layer> parent = getParent(); - - result.appendFormat( " " - "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), " - "crop=(%4d,%4d,%4d,%4d), finalCrop=(%4d,%4d,%4d,%4d), " - "isOpaque=%1d, invalidate=%1d, " - "dataspace=%s, pixelformat=%s " -#ifdef USE_HWC2 - "alpha=%.3f, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" -#else - "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" -#endif - " client=%p parent=%s\n", - getLayerStack(), s.z, - s.active.transform.tx(), s.active.transform.ty(), - s.active.w, s.active.h, - s.crop.left, s.crop.top, - s.crop.right, s.crop.bottom, - s.finalCrop.left, s.finalCrop.top, - s.finalCrop.right, s.finalCrop.bottom, - isOpaque(s), contentDirty, - dataspaceDetails(getDataSpace()).c_str(), decodePixelFormat(pf).c_str(), - s.alpha, s.flags, - s.active.transform[0][0], s.active.transform[0][1], - s.active.transform[1][0], s.active.transform[1][1], - client.get(), parent == nullptr ? "none" : parent->getName().string()); - - sp<const GraphicBuffer> buf0(mActiveBuffer); - uint32_t w0=0, h0=0, s0=0, f0=0; - if (buf0 != 0) { - w0 = buf0->getWidth(); - h0 = buf0->getHeight(); - s0 = buf0->getStride(); - f0 = buf0->format; - } - result.appendFormat( - " " - "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X]," - " queued-frames=%d, mRefreshPending=%d\n", - mFormat, w0, h0, s0,f0, - mQueuedFrames, mRefreshPending); - - if (mSurfaceFlingerConsumer != 0) { - mSurfaceFlingerConsumer->dumpState(result, " "); + info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string()); + info.mType = String8(getTypeId()); + info.mTransparentRegion = ds.activeTransparentRegion; + info.mVisibleRegion = visibleRegion; + info.mSurfaceDamageRegion = surfaceDamageRegion; + info.mLayerStack = getLayerStack(); + info.mX = ds.active.transform.tx(); + info.mY = ds.active.transform.ty(); + info.mZ = ds.z; + info.mWidth = ds.active.w; + info.mHeight = ds.active.h; + info.mCrop = ds.crop; + info.mFinalCrop = ds.finalCrop; + info.mAlpha = ds.alpha; + info.mFlags = ds.flags; + info.mPixelFormat = getPixelFormat(); + info.mDataSpace = getDataSpace(); + info.mMatrix[0][0] = ds.active.transform[0][0]; + info.mMatrix[0][1] = ds.active.transform[0][1]; + info.mMatrix[1][0] = ds.active.transform[1][0]; + info.mMatrix[1][1] = ds.active.transform[1][1]; + { + sp<const GraphicBuffer> activeBuffer = getActiveBuffer(); + if (activeBuffer != 0) { + info.mActiveBufferWidth = activeBuffer->getWidth(); + info.mActiveBufferHeight = activeBuffer->getHeight(); + info.mActiveBufferStride = activeBuffer->getStride(); + info.mActiveBufferFormat = activeBuffer->format; + } else { + info.mActiveBufferWidth = 0; + info.mActiveBufferHeight = 0; + info.mActiveBufferStride = 0; + info.mActiveBufferFormat = 0; + } } + info.mNumQueuedFrames = getQueuedFrameCount(); + info.mRefreshPending = isBufferLatched(); + info.mIsOpaque = isOpaque(ds); + info.mContentDirty = contentDirty; + return info; } #ifdef USE_HWC2 diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2306d1a43a..8df8c49bbe 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -60,6 +60,7 @@ class Colorizer; class DisplayDevice; class GraphicBuffer; class SurfaceFlinger; +class LayerDebugInfo; // --------------------------------------------------------------------------- @@ -441,6 +442,8 @@ public: bool hasQueuedFrame() const { return mQueuedFrames > 0 || mSidebandStreamChanged || mAutoRefresh; } + int32_t getQueuedFrameCount() const { return mQueuedFrames; } + #ifdef USE_HWC2 // ----------------------------------------------------------------------- @@ -489,9 +492,9 @@ public: inline const State& getCurrentState() const { return mCurrentState; } inline State& getCurrentState() { return mCurrentState; } + LayerDebugInfo getLayerDebugInfo() const; /* always call base class first */ - void dump(String8& result, Colorizer& colorizer) const; #ifdef USE_HWC2 static void miniDumpHeader(String8& result); void miniDump(String8& result, int32_t hwcId) const; @@ -689,6 +692,9 @@ public: sp<IGraphicBufferProducer> getProducer() const; const String8& getName() const; void notifyAvailableFrames(); + + PixelFormat getPixelFormat() const { return mFormat; } + private: // ----------------------------------------------------------------------- diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index aaaafbfa9e..4154d6a87f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -46,6 +46,7 @@ #include <gui/BufferQueue.h> #include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <ui/GraphicBufferAllocator.h> @@ -123,6 +124,21 @@ bool SurfaceFlinger::useVrFlinger; int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers; bool SurfaceFlinger::hasWideColorDisplay; + +std::string getHwcServiceName() { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.hwc_service_name", value, "default"); + ALOGI("Using HWComposer service: '%s'", value); + return std::string(value); +} + +bool useTrebleTestingOverride() { + char value[PROPERTY_VALUE_MAX] = {}; + property_get("debug.sf.treble_testing_override", value, "false"); + ALOGI("Treble testing override: '%s'", value); + return std::string(value) == "true"; +} + SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), mTransactionFlags(0), @@ -134,6 +150,7 @@ SurfaceFlinger::SurfaceFlinger() mHwc(nullptr), mRealHwc(nullptr), mVrHwc(nullptr), + mHwcServiceName(getHwcServiceName()), mRenderEngine(nullptr), mBootTime(systemTime()), mBuiltinDisplays(), @@ -233,6 +250,15 @@ SurfaceFlinger::SurfaceFlinger() // but since /data may be encrypted, we need to wait until after vold // comes online to attempt to read the property. The property is // instead read after the boot animation + + if (useTrebleTestingOverride()) { + // Without the override SurfaceFlinger cannot connect to HIDL + // services that are not listed in the manifests. Considered + // deriving the setting from the set service name, but it + // would be brittle if the name that's not 'default' is used + // for production purposes later on. + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + } } void SurfaceFlinger::onFirstRef() @@ -594,7 +620,7 @@ void SurfaceFlinger::init() { // initialize the primary display. LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, "Starting with vr flinger active is not currently supported."); - mRealHwc = new HWComposer(false); + mRealHwc = new HWComposer(mHwcServiceName); mHwc = mRealHwc; mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this)); @@ -1055,6 +1081,33 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { return NO_ERROR; } +status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission(sDump, pid, uid)) { + ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + + // Try to acquire a lock for 1s, fail gracefully + const status_t err = mStateLock.timedLock(s2ns(1)); + const bool locked = (err == NO_ERROR); + if (!locked) { + ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err); + return TIMED_OUT; + } + + outLayers->clear(); + mCurrentState.traverseInZOrder([&](Layer* layer) { + outLayers->push_back(layer->getLayerDebugInfo()); + }); + + mStateLock.unlock(); + return NO_ERROR; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( @@ -1312,7 +1365,7 @@ void SurfaceFlinger::updateVrFlinger() { if (vrFlingerRequestsDisplay && !mVrHwc) { // Construct new HWComposer without holding any locks. - mVrHwc = new HWComposer(true); + mVrHwc = new HWComposer("vr"); // Set up the event handlers. This step is neccessary to initialize the internal state of // the hardware composer object properly. Our callbacks are designed such that if they are @@ -3706,7 +3759,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.appendFormat("Visible layers (count = %zu)\n", mNumLayers); colorizer.reset(result); mCurrentState.traverseInZOrder([&](Layer* layer) { - layer->dump(result, colorizer); + result.append(to_string(layer->getLayerDebugInfo()).c_str()); }); /* diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index acfad46526..5123b58913 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -300,6 +300,7 @@ private: HdrCapabilities* outCapabilities) const; virtual status_t enableVSyncInjections(bool enable); virtual status_t injectVSync(nsecs_t when); + virtual status_t getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const; /* ------------------------------------------------------------------------ @@ -628,6 +629,7 @@ private: #ifdef USE_HWC2 HWComposer* mRealHwc; HWComposer* mVrHwc; + const std::string mHwcServiceName; // "default" for real use, something else for testing. #endif // constant members (no synchronization needed for access) RenderEngine* mRenderEngine; diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp index dae03b3c25..7aaa42aa4b 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -41,6 +41,7 @@ #include <gui/BufferQueue.h> #include <gui/GuiConfig.h> #include <gui/IDisplayEventConnection.h> +#include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <ui/GraphicBufferAllocator.h> @@ -931,6 +932,34 @@ status_t SurfaceFlinger::injectVSync(nsecs_t when) { return NO_ERROR; } +status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) && + !PermissionCache::checkPermission(sDump, pid, uid)) { + ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + + // Try to acquire a lock for 1s, fail gracefully + status_t err = mStateLock.timedLock(s2ns(1)); + bool locked = (err == NO_ERROR); + if (!locked) { + ALOGE("LayerDebugInfo: SurfaceFlinger unresponsive (%s [%d]) - exit", strerror(-err), err); + return TIMED_OUT; + } + + outLayers->clear(); + mCurrentState.traverseInZOrder([&](Layer* layer) { + outLayers->push_back(layer->getLayerDebugInfo()); + }); + + mStateLock.unlock(); + + return NO_ERROR; +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( @@ -3261,7 +3290,7 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, result.appendFormat("Visible layers (count = %zu)\n", mNumLayers); colorizer.reset(result); mCurrentState.traverseInZOrder([&](Layer* layer) { - layer->dump(result, colorizer); + result.append(to_string(layer->getLayerDebugInfo()).c_str()); }); /* diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp new file mode 100644 index 0000000000..94f3f2561a --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -0,0 +1,35 @@ +cc_test { + name: "sffakehwc_test", + srcs: [ + "FakeComposerClient.cpp", + "FakeComposerService.cpp", + "FakeComposerUtils.cpp", + "SFFakeHwc_test.cpp" + ], + shared_libs: [ + "libcutils", + "libutils", + "libbinder", + "libui", + "libgui", + "liblog", + "libnativewindow", + "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.mapper@2.0", + "libhwbinder", + "libhardware", + "libhidlbase", + "libsync", + "libfmq", + "libbase", + "libhidltransport" + ], + static_libs: [ + "libhwcomposer-client", + "libsurfaceflingerincludes", + "libtrace_proto", + "libgmock" + ], + tags: ["tests"], + test_suites: ["device-tests"] +}
\ No newline at end of file diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp new file mode 100644 index 0000000000..60916f3ab9 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp @@ -0,0 +1,613 @@ +/* + * Copyright (C) 2017 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. + */ + +//#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeComposer" + +#include "FakeComposerClient.h" + +#include <gui/SurfaceComposerClient.h> + +#include <log/log.h> + +#include <gtest/gtest.h> + +#include <inttypes.h> +#include <time.h> +#include <algorithm> +#include <condition_variable> +#include <iostream> +#include <mutex> +#include <set> +#include <thread> + +constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0); +constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1); + +using namespace sftest; + +using android::Condition; +using android::Mutex; + +using Clock = std::chrono::steady_clock; +using TimePoint = std::chrono::time_point<Clock>; + +namespace { + +// Internal state of a layer in the HWC API. +class LayerImpl { +public: + LayerImpl() = default; + + bool mValid = true; + RenderState mRenderState; + uint32_t mZ = 0; +}; + +// Struct for storing per frame rectangle state. Contains the render +// state shared to the test case. Basically a snapshot and a subset of +// LayerImpl sufficient to re-create the pixels of a layer for the +// frame. +struct FrameRect { +public: + FrameRect(Layer layer_, const RenderState& state, uint32_t z_) + : layer(layer_), renderState(state), z(z_) {} + + const Layer layer; + const RenderState renderState; + const uint32_t z; +}; + +// Collection of FrameRects forming one rendered frame. Could store +// related fences and other data in the future. +class Frame { +public: + Frame() = default; + std::vector<std::unique_ptr<FrameRect>> rectangles; +}; + +class DelayedEventGenerator { +public: + DelayedEventGenerator(std::function<void()> onTimerExpired) + : mOnTimerExpired(onTimerExpired), mThread([this]() { loop(); }) {} + + ~DelayedEventGenerator() { + ALOGI("DelayedEventGenerator exiting."); + { + std::unique_lock<std::mutex> lock(mMutex); + mRunning = false; + mWakeups.clear(); + mCondition.notify_one(); + } + mThread.join(); + ALOGI("DelayedEventGenerator exited."); + } + + void wakeAfter(std::chrono::nanoseconds waitTime) { + std::unique_lock<std::mutex> lock(mMutex); + mWakeups.insert(Clock::now() + waitTime); + mCondition.notify_one(); + } + +private: + void loop() { + while (true) { + // Lock scope + { + std::unique_lock<std::mutex> lock(mMutex); + mCondition.wait(lock, [this]() { return !mRunning || !mWakeups.empty(); }); + if (!mRunning && mWakeups.empty()) { + // This thread should only exit once the destructor has been called and all + // wakeups have been processed + return; + } + + // At this point, mWakeups will not be empty + + TimePoint target = *(mWakeups.begin()); + auto status = mCondition.wait_until(lock, target); + while (status == std::cv_status::no_timeout) { + // This was either a spurious wakeup or another wakeup was added, so grab the + // oldest point and wait again + target = *(mWakeups.begin()); + status = mCondition.wait_until(lock, target); + } + + // status must have been timeout, so we can finally clear this point + mWakeups.erase(target); + } + // Callback *without* locks! + mOnTimerExpired(); + } + } + + std::function<void()> mOnTimerExpired; + std::thread mThread; + std::mutex mMutex; + std::condition_variable mCondition; + bool mRunning = true; + std::set<TimePoint> mWakeups; +}; + +} // namespace + +FakeComposerClient::FakeComposerClient() + : mCallbacksOn(false), + mClient(nullptr), + mCurrentConfig(NULL_DISPLAY_CONFIG), + mVsyncEnabled(false), + mLayers(), + mDelayedEventGenerator( + std::make_unique<DelayedEventGenerator>([this]() { this->requestVSync(); })), + mSurfaceComposer(nullptr) {} + +FakeComposerClient::~FakeComposerClient() {} + +void FakeComposerClient::removeClient() { + ALOGV("removeClient"); + // TODO: Ahooga! Only thing current lifetime management choices in + // APIs make possible. Sad. + delete this; +} + +void FakeComposerClient::enableCallback(bool enable) { + ALOGV("enableCallback"); + mCallbacksOn = enable; + if (mCallbacksOn) { + mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED); + } +} + +void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) { + if (mCallbacksOn) { + mClient->onHotplug(display, state); + } +} + +uint32_t FakeComposerClient::getMaxVirtualDisplayCount() { + ALOGV("getMaxVirtualDisplayCount"); + return 1; +} + +Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/, + PixelFormat* /*format*/, Display* /*outDisplay*/) { + ALOGV("createVirtualDisplay"); + return Error::NONE; +} + +Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) { + ALOGV("destroyVirtualDisplay"); + return Error::NONE; +} + +Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) { + ALOGV("createLayer"); + *outLayer = mLayers.size(); + auto newLayer = std::make_unique<LayerImpl>(); + mLayers.push_back(std::move(newLayer)); + return Error::NONE; +} + +Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) { + ALOGV("destroyLayer"); + mLayers[layer]->mValid = false; + return Error::NONE; +} + +Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) { + ALOGV("getActiveConfig"); + + // TODO Assert outConfig != nullptr + + // TODO This is my reading of the + // IComposerClient::getActiveConfig, but returning BAD_CONFIG + // seems to not fit SurfaceFlinger plans. See version 2 below. + // if (mCurrentConfig == NULL_DISPLAY_CONFIG) { + // return Error::BAD_CONFIG; + // } + //*outConfig = mCurrentConfig; + *outConfig = 1; // Very special config for you my friend + return Error::NONE; +} + +Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/, + uint32_t /*height*/, PixelFormat /*format*/, + Dataspace /*dataspace*/) { + ALOGV("getClientTargetSupport"); + return Error::NONE; +} + +Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) { + ALOGV("getColorModes"); + return Error::NONE; +} + +Error FakeComposerClient::getDisplayAttribute(Display display, Config config, + IComposerClient::Attribute attribute, + int32_t* outValue) { + ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display), + static_cast<int>(config), static_cast<int>(attribute), outValue); + + // TODO: SOOO much fun to be had with these alone + switch (attribute) { + case IComposerClient::Attribute::WIDTH: + *outValue = 1920; + break; + case IComposerClient::Attribute::HEIGHT: + *outValue = 1080; + break; + case IComposerClient::Attribute::VSYNC_PERIOD: + *outValue = 1666666666; + break; // TOOD: Tests break down if lowered to 16ms? + case IComposerClient::Attribute::DPI_X: + *outValue = 240; + break; + case IComposerClient::Attribute::DPI_Y: + *outValue = 240; + break; + default: + LOG_ALWAYS_FATAL("Say what!?! New attribute"); + } + + return Error::NONE; +} + +Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) { + ALOGV("getDisplayConfigs"); + // TODO assert display == 1, outConfigs != nullptr + + outConfigs->resize(1); + (*outConfigs)[0] = 1; + + return Error::NONE; +} + +Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) { + ALOGV("getDisplayName"); + return Error::NONE; +} + +Error FakeComposerClient::getDisplayType(Display /*display*/, + IComposerClient::DisplayType* outType) { + ALOGV("getDisplayType"); + // TODO: This setting nothing on the output had no effect on initial trials. Is first display + // assumed to be physical? + *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL); + return Error::NONE; +} + +Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) { + ALOGV("getDozeSupport"); + return Error::NONE; +} + +Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/, + float* /*outMaxLuminance*/, + float* /*outMaxAverageLuminance*/, + float* /*outMinLuminance*/) { + ALOGV("getHdrCapabilities"); + return Error::NONE; +} + +Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) { + ALOGV("setActiveConfig"); + mCurrentConfig = config; + return Error::NONE; +} + +Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) { + ALOGV("setColorMode"); + return Error::NONE; +} + +Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) { + ALOGV("setPowerMode"); + return Error::NONE; +} + +Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) { + mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE); + ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE"); + return Error::NONE; +} + +Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/, + int32_t /*hint*/) { + ALOGV("setColorTransform"); + return Error::NONE; +} + +Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/, + int32_t /*acquireFence*/, int32_t /*dataspace*/, + const std::vector<hwc_rect_t>& /*damage*/) { + ALOGV("setClientTarget"); + return Error::NONE; +} + +Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/, + int32_t /*releaseFence*/) { + ALOGV("setOutputBuffer"); + return Error::NONE; +} + +Error FakeComposerClient::validateDisplay( + Display /*display*/, std::vector<Layer>* /*outChangedLayers*/, + std::vector<IComposerClient::Composition>* /*outCompositionTypes*/, + uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/, + std::vector<uint32_t>* /*outRequestMasks*/) { + ALOGV("validateDisplay"); + // TODO: Assume touching nothing means All Korrekt! + return Error::NONE; +} + +Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) { + ALOGV("acceptDisplayChanges"); + // Didn't ask for changes because software is omnipotent. + return Error::NONE; +} + +bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) { + return a->z <= b->z; +} + +Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/, + std::vector<Layer>* /*outLayers*/, + std::vector<int32_t>* /*outReleaseFences*/) { + ALOGV("presentDisplay"); + // TODO Leaving layers and their fences out for now. Doing so + // means that we've already processed everything. Important to + // test that the fences are respected, though. (How?) + + std::unique_ptr<Frame> newFrame(new Frame); + for (uint64_t layer = 0; layer < mLayers.size(); layer++) { + const LayerImpl& layerImpl = *mLayers[layer]; + + if (!layerImpl.mValid) continue; + + auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ); + newFrame->rectangles.push_back(std::move(rect)); + } + std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering); + { + Mutex::Autolock _l(mStateMutex); + mFrames.push_back(std::move(newFrame)); + mFramesAvailable.broadcast(); + } + return Error::NONE; +} + +Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/, + int32_t /*x*/, int32_t /*y*/) { + ALOGV("setLayerCursorPosition"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer, + int32_t acquireFence) { + ALOGV("setLayerBuffer"); + LayerImpl& l = getLayerImpl(layer); + if (buffer != l.mRenderState.mBuffer) { + l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not? + } + l.mRenderState.mBuffer = buffer; + l.mRenderState.mAcquireFence = acquireFence; + + return Error::NONE; +} + +Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/, + const std::vector<hwc_rect_t>& /*damage*/) { + ALOGV("setLayerSurfaceDamage"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) { + ALOGV("setLayerBlendMode"); + getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode); + return Error::NONE; +} + +Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer, + IComposerClient::Color color) { + ALOGV("setLayerColor"); + getLayerImpl(layer).mRenderState.mLayerColor.r = color.r; + getLayerImpl(layer).mRenderState.mLayerColor.g = color.g; + getLayerImpl(layer).mRenderState.mLayerColor.b = color.b; + getLayerImpl(layer).mRenderState.mLayerColor.a = color.a; + return Error::NONE; +} + +Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/, + int32_t /*type*/) { + ALOGV("setLayerCompositionType"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/, + int32_t /*dataspace*/) { + ALOGV("setLayerDataspace"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer, + const hwc_rect_t& frame) { + ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right, + frame.bottom); + getLayerImpl(layer).mRenderState.mDisplayFrame = frame; + return Error::NONE; +} + +Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) { + ALOGV("setLayerPlaneAlpha"); + getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha; + return Error::NONE; +} + +Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/, + buffer_handle_t /*stream*/) { + ALOGV("setLayerSidebandStream"); + return Error::NONE; +} + +Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer, + const hwc_frect_t& crop) { + ALOGV("setLayerSourceCrop"); + getLayerImpl(layer).mRenderState.mSourceCrop = crop; + return Error::NONE; +} + +Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) { + ALOGV("setLayerTransform"); + getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform); + return Error::NONE; +} + +Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer, + const std::vector<hwc_rect_t>& visible) { + ALOGV("setLayerVisibleRegion"); + getLayerImpl(layer).mRenderState.mVisibleRegion = visible; + return Error::NONE; +} + +Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) { + ALOGV("setLayerZOrder"); + getLayerImpl(layer).mZ = z; + return Error::NONE; +} + +////////////////////////////////////////////////////////////////// + +void FakeComposerClient::setClient(ComposerClient* client) { + mClient = client; +} + +void FakeComposerClient::requestVSync(uint64_t vsyncTime) { + if (mCallbacksOn) { + uint64_t timestamp = vsyncTime; + ALOGV("Vsync"); + if (timestamp == 0) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; + } + if (mSurfaceComposer != nullptr) { + mSurfaceComposer->injectVSync(timestamp); + } else { + mClient->onVsync(DEFAULT_DISPLAY, timestamp); + } + } +} + +void FakeComposerClient::runVSyncAfter(std::chrono::nanoseconds wait) { + mDelayedEventGenerator->wakeAfter(wait); +} + +LayerImpl& FakeComposerClient::getLayerImpl(Layer handle) { + // TODO Change these to an internal state check that can be + // invoked from the gtest? GTest macros do not seem all that safe + // when used outside the test class + EXPECT_GE(handle, static_cast<Layer>(0)); + EXPECT_LT(handle, mLayers.size()); + return *(mLayers[handle]); +} + +int FakeComposerClient::getFrameCount() const { + return mFrames.size(); +} + +static std::vector<RenderState> extractRenderState( + const std::vector<std::unique_ptr<FrameRect>>& internalRects) { + std::vector<RenderState> result; + result.reserve(internalRects.size()); + for (const std::unique_ptr<FrameRect>& rect : internalRects) { + result.push_back(rect->renderState); + } + return result; +} + +std::vector<RenderState> FakeComposerClient::getFrameRects(int frame) const { + Mutex::Autolock _l(mStateMutex); + return extractRenderState(mFrames[frame]->rectangles); +} + +std::vector<RenderState> FakeComposerClient::getLatestFrame() const { + Mutex::Autolock _l(mStateMutex); + return extractRenderState(mFrames[mFrames.size() - 1]->rectangles); +} + +void FakeComposerClient::runVSyncAndWait(std::chrono::nanoseconds maxWait) { + int currentFrame = 0; + { + Mutex::Autolock _l(mStateMutex); // I hope this is ok... + currentFrame = static_cast<int>(mFrames.size()); + requestVSync(); + } + waitUntilFrame(currentFrame + 1, maxWait); +} + +void FakeComposerClient::waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait) const { + Mutex::Autolock _l(mStateMutex); + while (mFrames.size() < static_cast<size_t>(targetFrame)) { + android::status_t result = mFramesAvailable.waitRelative(mStateMutex, maxWait.count()); + if (result == android::TIMED_OUT) { + ALOGE("Waiting for frame %d (at frame %zu now) timed out after %lld ns", targetFrame, + mFrames.size(), maxWait.count()); + return; + } + } +} + +void FakeComposerClient::clearFrames() { + Mutex::Autolock _l(mStateMutex); + mFrames.clear(); + for (const std::unique_ptr<LayerImpl>& layer : mLayers) { + if (layer->mValid) { + layer->mRenderState.mSwapCount = 0; + } + } +} + +void FakeComposerClient::onSurfaceFlingerStart() { + mSurfaceComposer == nullptr; + do { + mSurfaceComposer = new android::SurfaceComposerClient; + android::status_t initResult = mSurfaceComposer->initCheck(); + if (initResult != android::NO_ERROR) { + ALOGD("Init result: %d", initResult); + mSurfaceComposer = nullptr; + std::this_thread::sleep_for(10ms); + } + } while (mSurfaceComposer == nullptr); + ALOGD("SurfaceComposerClient created"); + mSurfaceComposer->enableVSyncInjections(true); +} + +void FakeComposerClient::onSurfaceFlingerStop() { + mSurfaceComposer->dispose(); + mSurfaceComposer.clear(); +} + +// Includes destroyed layers, stored in order of creation. +int FakeComposerClient::getLayerCount() const { + return mLayers.size(); +} + +Layer FakeComposerClient::getLayer(size_t index) const { + // NOTE: If/when passing calls through to actual implementation, + // this might get more involving. + return static_cast<Layer>(index); +} diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h new file mode 100644 index 0000000000..294abb2c59 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h @@ -0,0 +1,146 @@ +/* + * Copyright 2017 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. + */ + +#pragma once + +#include "ComposerClient.h" +#include "RenderState.h" + +#include <utils/Condition.h> + +#include <chrono> + +using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_1::implementation; +using namespace android::hardware; +using namespace std::chrono_literals; + +namespace { +class LayerImpl; +class Frame; +class DelayedEventGenerator; +} // namespace + +namespace android { +class SurfaceComposerClient; +} // namespace android + +namespace sftest { + +class FakeComposerClient : public ComposerBase { +public: + FakeComposerClient(); + virtual ~FakeComposerClient(); + + void removeClient() override; + void enableCallback(bool enable) override; + uint32_t getMaxVirtualDisplayCount() override; + Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format, + Display* outDisplay) override; + Error destroyVirtualDisplay(Display display) override; + Error createLayer(Display display, Layer* outLayer) override; + Error destroyLayer(Display display, Layer layer) override; + + Error getActiveConfig(Display display, Config* outConfig) override; + Error getClientTargetSupport(Display display, uint32_t width, uint32_t height, + PixelFormat format, Dataspace dataspace) override; + Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override; + Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute, + int32_t* outValue) override; + Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override; + Error getDisplayName(Display display, hidl_string* outName) override; + Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override; + Error getDozeSupport(Display display, bool* outSupport) override; + Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance, + float* outMaxAverageLuminance, float* outMinLuminance) override; + + Error setActiveConfig(Display display, Config config) override; + Error setColorMode(Display display, ColorMode mode) override; + Error setPowerMode(Display display, IComposerClient::PowerMode mode) override; + Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override; + + Error setColorTransform(Display display, const float* matrix, int32_t hint) override; + Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence, + int32_t dataspace, const std::vector<hwc_rect_t>& damage) override; + Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override; + Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers, + std::vector<IComposerClient::Composition>* outCompositionTypes, + uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, + std::vector<uint32_t>* outRequestMasks) override; + Error acceptDisplayChanges(Display display) override; + Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers, + std::vector<int32_t>* outReleaseFences) override; + + Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override; + Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer, + int32_t acquireFence) override; + Error setLayerSurfaceDamage(Display display, Layer layer, + const std::vector<hwc_rect_t>& damage) override; + Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override; + Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override; + Error setLayerCompositionType(Display display, Layer layer, int32_t type) override; + Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override; + Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override; + Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override; + Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override; + Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override; + Error setLayerTransform(Display display, Layer layer, int32_t transform) override; + Error setLayerVisibleRegion(Display display, Layer layer, + const std::vector<hwc_rect_t>& visible) override; + Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; + + void setClient(ComposerClient* client); + + void requestVSync(uint64_t vsyncTime = 0); + // We don't want tests hanging, so always use a timeout. Remember + // to always check the number of frames with test ASSERT_! + // Wait until next frame is rendered after requesting vsync. + void runVSyncAndWait(std::chrono::nanoseconds maxWait = 100ms); + void runVSyncAfter(std::chrono::nanoseconds wait); + + int getFrameCount() const; + // We don't want tests hanging, so always use a timeout. Remember + // to always check the number of frames with test ASSERT_! + void waitUntilFrame(int targetFrame, std::chrono::nanoseconds maxWait = 100ms) const; + std::vector<RenderState> getFrameRects(int frame) const; + std::vector<RenderState> getLatestFrame() const; + void clearFrames(); + + void onSurfaceFlingerStart(); + void onSurfaceFlingerStop(); + + int getLayerCount() const; + Layer getLayer(size_t index) const; + + void hotplugDisplay(Display display, IComposerCallback::Connection state); + +private: + LayerImpl& getLayerImpl(Layer handle); + + bool mCallbacksOn; + ComposerClient* mClient; + Config mCurrentConfig; + bool mVsyncEnabled; + std::vector<std::unique_ptr<LayerImpl>> mLayers; + std::vector<std::unique_ptr<Frame>> mFrames; + // Using a pointer to hide the implementation into the CPP file. + std::unique_ptr<DelayedEventGenerator> mDelayedEventGenerator; + android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections + mutable android::Mutex mStateMutex; + mutable android::Condition mFramesAvailable; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp new file mode 100644 index 0000000000..c411604587 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 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. + */ + +#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeHwcService" +#include <log/log.h> + +#include "FakeComposerService.h" + +using namespace android::hardware; + +namespace sftest { + +FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {} + +FakeComposerService::~FakeComposerService() { + ALOGI("Maybe killing client %p", mClient.get()); + // Rely on sp to kill the client. +} + +Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) { + ALOGI("FakeComposerService::getCapabilities"); + hidl_cb(hidl_vec<Capability>()); + return Void(); +} + +Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) { + ALOGI("FakeComposerService::dumpDebugInfo"); + hidl_cb(hidl_string()); + return Void(); +} + +Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) { + ALOGI("FakeComposerService::createClient %p", mClient.get()); + mClient->initialize(); + hidl_cb(Error::NONE, mClient); + return Void(); +} + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h new file mode 100644 index 0000000000..520408496f --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 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. + */ + +#pragma once + +#include "ComposerClient.h" + +using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_1::implementation; +using android::hardware::Return; + +namespace sftest { + +class FakeComposerService : public IComposer { +public: + FakeComposerService(android::sp<ComposerClient>& client); + virtual ~FakeComposerService(); + + Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; + Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; + Return<void> createClient(createClient_cb hidl_cb) override; + +private: + android::sp<ComposerClient> mClient; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp new file mode 100644 index 0000000000..51956ec970 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2017 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. + */ + +#define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeHwcUtil" +#include <log/log.h> + +#include "FakeComposerUtils.h" +#include "RenderState.h" + +#include "SurfaceFlinger.h" // Get the name of the service... + +#include <binder/IServiceManager.h> + +#include <cutils/properties.h> + +#include <iomanip> +#include <thread> + +using android::String16; +using android::sp; +using namespace std::chrono_literals; +using namespace sftest; +using std::setw; + +namespace sftest { + +// clang-format off +inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) { + os << std::fixed << std::setprecision(1) << "(" + << setw(align) << sourceRect.left << setw(0) << "," + << setw(align) << sourceRect.top << setw(0) << "," + << setw(align) << sourceRect.right << setw(0) << "," + << setw(align) << sourceRect.bottom << setw(0) << ")"; +} + +inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) { + os << "(" + << setw(align) << displayRect.left << setw(0) << "," + << setw(align) << displayRect.top << setw(0) << "," + << setw(align) << displayRect.right << setw(0) << "," + << setw(align) << displayRect.bottom << setw(0) << ")"; +} +// clang-format on + +inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) { + printSourceRectAligned(os, state.mSourceCrop, 7); + os << "->"; + printDisplayRectAligned(os, state.mDisplayFrame, 5); + return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3) + << state.mPlaneAlpha << " Xform:" << state.mTransform; +} + +// Helper for verifying the parts of the RenderState +template <typename T> +bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val, + const char* name) { + if (ref != val) { + message = message << "Expected " << name << ":" << ref << ", got:" << val << "."; + return false; + } + return true; +} + +::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) { + // TODO: Message could start as success and be assigned as failure. + // Only problem is that utility assumes it to be failure and just adds stuff. Would + // need still special case the initial failure in the utility? + // TODO: ... or would it be possible to break this back to gtest primitives? + ::testing::AssertionResult message = ::testing::AssertionFailure(); + bool passes = true; + + // The work here is mostly about providing good log strings for differences + passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame"); + passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha"); + passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count"); + passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop"); + // ... add more + if (passes) { + return ::testing::AssertionSuccess(); + } + return message; +} + +::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, + const std::vector<RenderState>& val) { + ::testing::AssertionResult message = ::testing::AssertionFailure(); + bool passed = true; + if (ref.size() != val.size()) { + message << "Expected " << ref.size() << " rects, got " << val.size() << "."; + passed = false; + } + for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) { + ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]); + if (rectResult == false) { + message << "First different rect at " << rectIndex << ": " << rectResult.message(); + passed = false; + break; + } + } + + if (passed) { + return ::testing::AssertionSuccess(); + } else { + message << "\nReference:"; + for (auto state = ref.begin(); state != ref.end(); ++state) { + message << "\n" << *state; + } + message << "\nActual:"; + for (auto state = val.begin(); state != val.end(); ++state) { + message << "\n" << *state; + } + } + return message; +} + +void startSurfaceFlinger() { + ALOGI("Start SurfaceFlinger"); + system("start surfaceflinger"); + + sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<android::IBinder> sf; + while (sf == nullptr) { + std::this_thread::sleep_for(10ms); + sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); + } + ALOGV("SurfaceFlinger running"); +} + +void stopSurfaceFlinger() { + ALOGI("Stop SurfaceFlinger"); + system("stop surfaceflinger"); + sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<android::IBinder> sf; + while (sf != nullptr) { + std::this_thread::sleep_for(10ms); + sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); + } + ALOGV("SurfaceFlinger stopped"); +} + +//////////////////////////////////////////////// + +void FakeHwcEnvironment::SetUp() { + ALOGI("Test env setup"); + system("setenforce 0"); + system("stop"); + property_set("debug.sf.nobootanimation", "1"); + { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.sf.nobootanimation", value, "0"); + LOG_FATAL_IF(atoi(value) != 1, "boot skip not set"); + } + // TODO: Try registering the mock as the default service instead. + property_set("debug.sf.hwc_service_name", "mock"); + // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files. + property_set("debug.sf.treble_testing_override", "true"); +} + +void FakeHwcEnvironment::TearDown() { + ALOGI("Test env tear down"); + system("stop"); + // Wait for mock call signaling teardown? + property_set("debug.sf.nobootanimation", "0"); + property_set("debug.sf.hwc_service_name", "default"); + ALOGI("Test env tear down - done"); +} + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h new file mode 100644 index 0000000000..74dc0e51bb --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h @@ -0,0 +1,119 @@ +/* + * Copyright 2017 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. + */ + +#pragma once + +#include "FakeComposerClient.h" + +#include <gui/SurfaceComposerClient.h> + +#include <hardware/hwcomposer_defs.h> + +#include <log/log.h> + +#include <gtest/gtest.h> + +// clang-format off +// Note: This needs to reside in the global namespace for the GTest to use it +inline ::std::ostream& operator<<(::std::ostream& os, const hwc_rect_t& rect) { + return os << "(" << rect.left << "," + << rect.top << "," + << rect.right << "," + << rect.bottom << ")"; +} + +inline ::std::ostream& operator<<(::std::ostream& os, const hwc_frect_t& rect) { + return os << "(" << rect.left << "," + << rect.top << "," + << rect.right << "," + << rect.bottom << ")"; +} +// clang-format on + +namespace sftest { + +class RenderState; + +// clang-format off +inline bool operator==(const hwc_rect_t& a, const hwc_rect_t& b) { + return a.top == b.top && + a.left == b.left && + a.bottom == b.bottom && + a.right == b.right; +} + +inline bool operator==(const hwc_frect_t& a, const hwc_frect_t& b) { + return a.top == b.top && + a.left == b.left && + a.bottom == b.bottom && + a.right == b.right; +} +// clang-format on + +inline bool operator!=(const hwc_rect_t& a, const hwc_rect_t& b) { + return !(a == b); +} + +inline bool operator!=(const hwc_frect_t& a, const hwc_frect_t& b) { + return !(a == b); +} + +::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val); +::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, + const std::vector<RenderState>& val); + +void startSurfaceFlinger(); +void stopSurfaceFlinger(); + +class FakeHwcEnvironment : public ::testing::Environment { +public: + virtual ~FakeHwcEnvironment() {} + void SetUp() override; + void TearDown() override; +}; + +/* + * All surface state changes are supposed to happen inside a global + * transaction. GlobalTransactionScope object at the beginning of + * scope automates the process. The resulting scope gives a visual cue + * on the span of the transaction as well. + * + * Closing the transaction is synchronous, i.e., it waits for + * SurfaceFlinger to composite one frame. Now, the FakeComposerClient + * is built to explicitly request vsyncs one at the time. A delayed + * request must be made before closing the transaction or the test + * thread stalls until SurfaceFlinger does an emergency vsync by + * itself. GlobalTransactionScope encapsulates this vsync magic. + */ +class GlobalTransactionScope { +public: + GlobalTransactionScope(FakeComposerClient& composer) : mComposer(composer) { + android::SurfaceComposerClient::openGlobalTransaction(); + } + ~GlobalTransactionScope() { + int frameCount = mComposer.getFrameCount(); + mComposer.runVSyncAfter(1ms); + android::SurfaceComposerClient::closeGlobalTransaction(true); + // Make sure that exactly one frame has been rendered. + mComposer.waitUntilFrame(frameCount + 1); + LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(), + "Unexpected frame advance. Delta: %d", + mComposer.getFrameCount() - frameCount); + } + FakeComposerClient& mComposer; +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/RenderState.h b/services/surfaceflinger/tests/fakehwc/RenderState.h new file mode 100644 index 0000000000..0059289d4f --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/RenderState.h @@ -0,0 +1,44 @@ +/* + * Copyright 2017 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. + */ + +#pragma once + +#include <hardware/hwcomposer2.h> + +#include <vector> + +namespace sftest { +// Description of a rendered rectangle. Should only contain +// instructions necessary to rasterize the rectangle. The full scene +// is given as a sorted list of rectangles, bottom layer at index 0. +class RenderState { +public: + RenderState() = default; + // Default copy-ctor + + hwc_rect_t mDisplayFrame = {0, 0, 0, 0}; + hwc_frect_t mSourceCrop = {0.f, 0.f, 0.f, 0.f}; + std::vector<hwc_rect_t> mVisibleRegion; + hwc2_blend_mode_t mBlendMode = HWC2_BLEND_MODE_NONE; + buffer_handle_t mBuffer = 0; + uint32_t mSwapCount = 0; // How many set buffer calls to the layer. + int32_t mAcquireFence = 0; // Probably should not be here. + float mPlaneAlpha = 0.f; + hwc_color_t mLayerColor = {0, 0, 0, 0}; + hwc_transform_t mTransform = static_cast<hwc_transform_t>(0); +}; + +} // namespace sftest diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp new file mode 100644 index 0000000000..8902ede301 --- /dev/null +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -0,0 +1,1306 @@ +/* + * Copyright (C) 2017 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. + */ + +// #define LOG_NDEBUG 0 +#undef LOG_TAG +#define LOG_TAG "FakeHwcTest" + +#include "FakeComposerClient.h" +#include "FakeComposerService.h" +#include "FakeComposerUtils.h" + +#include <gui/ISurfaceComposer.h> +#include <gui/LayerDebugInfo.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <private/gui/ComposerService.h> +#include <private/gui/LayerState.h> + +#include <ui/DisplayInfo.h> + +#include <android/native_window.h> + +#include <android/hidl/manager/1.0/IServiceManager.h> + +#include <hwbinder/ProcessState.h> + +#include <binder/ProcessState.h> + +#include <log/log.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <limits> + +using namespace std::chrono_literals; + +using namespace android; +using namespace android::hardware; + +using namespace sftest; + +namespace { + +// Mock test helpers +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::_; + +/////////////////////////////////////////////// + +struct TestColor { +public: + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +}; + +constexpr static TestColor RED = {195, 63, 63, 255}; +constexpr static TestColor LIGHT_RED = {255, 177, 177, 255}; +constexpr static TestColor GREEN = {63, 195, 63, 255}; +constexpr static TestColor BLUE = {63, 63, 195, 255}; +constexpr static TestColor DARK_GRAY = {63, 63, 63, 255}; +constexpr static TestColor LIGHT_GRAY = {200, 200, 200, 255}; + +// Fill an RGBA_8888 formatted surface with a single color. +static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const TestColor& color, + bool unlock = true) { + ANativeWindow_Buffer outBuffer; + sp<Surface> s = sc->getSurface(); + ASSERT_TRUE(s != nullptr); + ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); + uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits); + for (int y = 0; y < outBuffer.height; y++) { + for (int x = 0; x < outBuffer.width; x++) { + uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); + pixel[0] = color.r; + pixel[1] = color.g; + pixel[2] = color.b; + pixel[3] = color.a; + } + } + if (unlock) { + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + } +} + +inline RenderState makeSimpleRect(int left, int top, int right, int bottom) { + RenderState res; + res.mDisplayFrame = hwc_rect_t{left, top, right, bottom}; + res.mPlaneAlpha = 1.0f; + res.mSwapCount = 0; + res.mSourceCrop = hwc_frect_t{0.f, 0.f, static_cast<float>(right - left), + static_cast<float>(bottom - top)}; + return res; +} + +inline RenderState makeSimpleRect(unsigned int left, unsigned int top, unsigned int right, + unsigned int bottom) { + EXPECT_LE(left, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(top, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(right, static_cast<unsigned int>(INT_MAX)); + EXPECT_LE(bottom, static_cast<unsigned int>(INT_MAX)); + return makeSimpleRect(static_cast<int>(left), static_cast<int>(top), static_cast<int>(right), + static_cast<int>(bottom)); +} + +//////////////////////////////////////////////// + +class DisplayTest : public ::testing::Test { +public: + class MockComposerClient : public FakeComposerClient { + public: + MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType)); + MOCK_METHOD4(getDisplayAttribute, + Error(Display display, Config config, IComposerClient::Attribute attribute, + int32_t* outValue)); + + // Re-routing to basic fake implementation + Error getDisplayAttributeFake(Display display, Config config, + IComposerClient::Attribute attribute, int32_t* outValue) { + return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue); + } + }; + +protected: + void SetUp() override; + void TearDown() override; + + sp<IComposer> mFakeService; + sp<SurfaceComposerClient> mComposerClient; + + MockComposerClient* mMockComposer; +}; + +void DisplayTest::SetUp() { + // TODO: The mMockComposer should be a unique_ptr, but it needs to + // outlive the test class. Currently ComposerClient only dies + // when the service is replaced. The Mock deletes itself when + // removeClient is called on it, which is ugly. This can be + // changed if HIDL ServiceManager allows removing services or + // ComposerClient starts taking the ownership of the contained + // implementation class. Moving the fake class to the HWC2 + // interface instead of the current Composer interface might also + // change the situation. + mMockComposer = new MockComposerClient; + sp<ComposerClient> client = new ComposerClient(*mMockComposer); + mMockComposer->setClient(client.get()); + mFakeService = new FakeComposerService(client); + mFakeService->registerAsService("mock"); + + android::hardware::ProcessState::self()->startThreadPool(); + android::ProcessState::self()->startThreadPool(); + + EXPECT_CALL(*mMockComposer, getDisplayType(1, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), + Return(Error::NONE))); + // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy + // but encoding that here exactly. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _)) + .Times(5) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + // TODO: Find out what code is generating the ID 0. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _)) + .Times(5) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + + startSurfaceFlinger(); + + // Fake composer wants to enable VSync injection + mMockComposer->onSurfaceFlingerStart(); + + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); +} + +void DisplayTest::TearDown() { + mComposerClient->dispose(); + mComposerClient = nullptr; + + // Fake composer needs to release SurfaceComposerClient before the stop. + mMockComposer->onSurfaceFlingerStop(); + stopSurfaceFlinger(); + + mFakeService = nullptr; + // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime + // management. + mMockComposer = nullptr; +} + +TEST_F(DisplayTest, Hotplug) { + ALOGD("DisplayTest::Hotplug"); + + EXPECT_CALL(*mMockComposer, getDisplayType(2, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), + Return(Error::NONE))); + // The attribute queries will get done twice. This is for defaults + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _)) + .Times(2 * 3) + .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); + // ... and then special handling for dimensions. Specifying this + // rules later means that gmock will try them first, i.e., + // ordering of width/height vs. the default implementation for + // other queries is significant. + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE))); + + EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _)) + .Times(2) + .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE))); + + // TODO: Width and height queries are not actually called. Display + // info returns dimensions 0x0 in display info. Why? + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::CONNECTED); + + { + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ASSERT_EQ(400u, info.w); + ASSERT_EQ(200u, info.h); + + auto surfaceControl = + mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(surfaceControl != nullptr); + ASSERT_TRUE(surfaceControl->isValid()); + fillSurfaceRGBA8(surfaceControl, BLUE); + + { + GlobalTransactionScope gts(*mMockComposer); + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, surfaceControl->show()); + } + } + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::DISCONNECTED); + + mMockComposer->clearFrames(); + + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::CONNECTED); + + { + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdHdmi)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + ASSERT_EQ(400u, info.w); + ASSERT_EQ(200u, info.h); + + auto surfaceControl = + mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(surfaceControl != nullptr); + ASSERT_TRUE(surfaceControl->isValid()); + fillSurfaceRGBA8(surfaceControl, BLUE); + + { + GlobalTransactionScope gts(*mMockComposer); + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, surfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, surfaceControl->show()); + } + } + mMockComposer->hotplugDisplay(static_cast<Display>(2), + IComposerCallback::Connection::DISCONNECTED); +} + +//////////////////////////////////////////////// + +class TransactionTest : public ::testing::Test { +protected: + // Layer array indexing constants. + constexpr static int BG_LAYER = 0; + constexpr static int FG_LAYER = 1; + + static void SetUpTestCase(); + static void TearDownTestCase(); + + void SetUp() override; + void TearDown() override; + + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mFGSurfaceControl; + std::vector<RenderState> mBaseFrame; + uint32_t mDisplayWidth; + uint32_t mDisplayHeight; + + static FakeComposerClient* sFakeComposer; +}; + +FakeComposerClient* TransactionTest::sFakeComposer; + +void TransactionTest::SetUpTestCase() { + // TODO: See TODO comment at DisplayTest::SetUp for background on + // the lifetime of the FakeComposerClient. + sFakeComposer = new FakeComposerClient; + sp<ComposerClient> client = new ComposerClient(*sFakeComposer); + sFakeComposer->setClient(client.get()); + sp<IComposer> fakeService = new FakeComposerService(client); + fakeService->registerAsService("mock"); + + android::hardware::ProcessState::self()->startThreadPool(); + android::ProcessState::self()->startThreadPool(); + + startSurfaceFlinger(); + + // Fake composer wants to enable VSync injection + sFakeComposer->onSurfaceFlingerStart(); +} + +void TransactionTest::TearDownTestCase() { + // Fake composer needs to release SurfaceComposerClient before the stop. + sFakeComposer->onSurfaceFlingerStop(); + stopSurfaceFlinger(); + // TODO: This is deleted when the ComposerClient calls + // removeClient. Devise better lifetime control. + sFakeComposer = nullptr; +} + +void TransactionTest::SetUp() { + ALOGI("TransactionTest::SetUp"); + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + ALOGI("TransactionTest::SetUp - display"); + sp<android::IBinder> display( + SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain)); + DisplayInfo info; + SurfaceComposerClient::getDisplayInfo(display, &info); + + mDisplayWidth = info.w; + mDisplayHeight = info.h; + + // Background surface + mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth, + mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mBGSurfaceControl != nullptr); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + fillSurfaceRGBA8(mBGSurfaceControl, BLUE); + + // Foreground surface + mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mFGSurfaceControl != nullptr); + ASSERT_TRUE(mFGSurfaceControl->isValid()); + + fillSurfaceRGBA8(mFGSurfaceControl, RED); + + SurfaceComposerClient::openGlobalTransaction(); + + mComposerClient->setDisplayLayerStack(display, 0); + + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->setLayer(INT32_MAX - 2)); + ASSERT_EQ(NO_ERROR, mBGSurfaceControl->show()); + + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT32_MAX - 1)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(64, 64)); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show()); + + // Synchronous transaction will stop this thread, so we set up a + // delayed, off-thread vsync request before closing the + // transaction. In the test code this is usually done with + // GlobalTransactionScope. Leaving here in the 'vanilla' form for + // reference. + ASSERT_EQ(0, sFakeComposer->getFrameCount()); + sFakeComposer->runVSyncAfter(1ms); + SurfaceComposerClient::closeGlobalTransaction(true); + sFakeComposer->waitUntilFrame(1); + + // Reference data. This is what the HWC should see. + static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing"); + mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight)); + mBaseFrame[BG_LAYER].mSwapCount = 1; + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); + mBaseFrame[FG_LAYER].mSwapCount = 1; + + auto frame = sFakeComposer->getFrameRects(0); + ASSERT_TRUE(framesAreSame(mBaseFrame, frame)); +} + +void TransactionTest::TearDown() { + ALOGD("TransactionTest::TearDown"); + + mComposerClient->dispose(); + mBGSurfaceControl = 0; + mFGSurfaceControl = 0; + mComposerClient = 0; + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.clear(); + sFakeComposer->clearFrames(); + ASSERT_EQ(0, sFakeComposer->getFrameCount()); + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + std::vector<LayerDebugInfo> layers; + status_t result = sf->getLayerDebugInfo(&layers); + if (result != NO_ERROR) { + ALOGE("Failed to get layers %s %d", strerror(-result), result); + } else { + // If this fails, the test being torn down leaked layers. + EXPECT_EQ(0u, layers.size()); + if (layers.size() > 0) { + for (auto layer = layers.begin(); layer != layers.end(); ++layer) { + std::cout << to_string(*layer).c_str(); + } + // To ensure the next test has clean slate, will run the class + // tear down and setup here. + TearDownTestCase(); + SetUpTestCase(); + } + } + ALOGD("TransactionTest::TearDown - complete"); +} + +TEST_F(TransactionTest, LayerMove) { + ALOGD("TransactionTest::LayerMove"); + + // The scope opens and closes a global transaction and, at the + // same time, makes sure the SurfaceFlinger progresses one frame + // after the transaction closes. The results of the transaction + // should be available in the latest frame stored by the fake + // composer. + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128)); + // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls. + // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?) + // + // sFakeComposer->runVSyncAndWait(); + } + + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + + ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's + // no extra frames. + + // NOTE: Frame 0 is produced in the SetUp. + auto frame1Ref = mBaseFrame; + frame1Ref[FG_LAYER].mDisplayFrame = + hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves. + EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1))); + + auto frame2Ref = frame1Ref; + frame2Ref[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2))); +} + +TEST_F(TransactionTest, LayerResize) { + ALOGD("TransactionTest::LayerResize"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setSize(128, 128)); + } + + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + + ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's + // no extra frames. + + auto frame1Ref = mBaseFrame; + // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted. + EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1))); + + auto frame2Ref = frame1Ref; + frame2Ref[FG_LAYER].mSwapCount++; + frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128}; + frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f}; + EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2))); +} + +TEST_F(TransactionTest, LayerCrop) { + // TODO: Add scaling to confirm that crop happens in buffer space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(16, 16, 32, 32); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setCrop(cropRect)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f}; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerFinalCrop) { + // TODO: Add scaling to confirm that crop happens in display space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(32, 32, 32 + 64, 32 + 64); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // In display space we are cropping with [32, 32, 96, 96] against display rect + // [64, 64, 128, 128]. Should yield display rect [64, 64, 96, 96] + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f}; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 32, 64 + 32}; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerFinalCropEmpty) { + // TODO: Add scaling to confirm that crop happens in display space? + { + GlobalTransactionScope gts(*sFakeComposer); + Rect cropRect(16, 16, 32, 32); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setFinalCrop(cropRect)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // In display space we are cropping with [16, 16, 32, 32] against display rect + // [64, 64, 128, 128]. The intersection is empty and only the background layer is composited. + std::vector<RenderState> referenceFrame(1); + referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetLayer) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // The layers will switch order, but both are rendered because the background layer is + // transparent (RGBA8888). + std::vector<RenderState> referenceFrame(2); + referenceFrame[0] = mBaseFrame[FG_LAYER]; + referenceFrame[1] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetLayerOpaque) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayer(INT_MAX - 3)); + ASSERT_EQ(NO_ERROR, + mBGSurfaceControl->setFlags(layer_state_t::eLayerOpaque, + layer_state_t::eLayerOpaque)); + } + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + + // The former foreground layer is now covered with opaque layer - it should have disappeared + std::vector<RenderState> referenceFrame(1); + referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, SetLayerStack) { + ALOGD("TransactionTest::SetLayerStack"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setLayerStack(1)); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerShowHide) { + ALOGD("TransactionTest::LayerShowHide"); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->hide()); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->show()); + } + + // Foreground layer should be back + ASSERT_EQ(3, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetAlpha) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75f)); + } + + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetFlags) { + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, + mFGSurfaceControl->setFlags(layer_state_t::eLayerHidden, + layer_state_t::eLayerHidden)); + } + + // Foreground layer should have disappeared. + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + std::vector<RenderState> refFrame(1); + refFrame[BG_LAYER] = mBaseFrame[BG_LAYER]; + EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, LayerSetMatrix) { + struct matrixTestData { + float matrix[4]; + hwc_transform_t expectedTransform; + hwc_rect_t expectedDisplayFrame; + }; + + // The matrix operates on the display frame and is applied before + // the position is added. So, the foreground layer rect is (0, 0, + // 64, 64) is first transformed, potentially yielding negative + // coordinates and then the position (64, 64) is added yielding + // the final on-screen rectangles given. + + const matrixTestData MATRIX_TESTS[7] = // clang-format off + {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}}, + {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}}, + {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}}, + {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}}, + {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}}, + {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}}, + {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}}; + // clang-format on + constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData); + + for (int i = 0; i < TEST_COUNT; i++) { + // TODO: How to leverage the HWC2 stringifiers? + const matrixTestData& xform = MATRIX_TESTS[i]; + SCOPED_TRACE(i); + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, + mFGSurfaceControl->setMatrix(xform.matrix[0], xform.matrix[1], + xform.matrix[2], xform.matrix[3])); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mTransform = xform.expectedTransform; + referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + } +} + +#if 0 +TEST_F(TransactionTest, LayerSetMatrix2) { + { + GlobalTransactionScope gts(*sFakeComposer); + // TODO: PLEASE SPEC THE FUNCTION! + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setMatrix(0.11f, 0.123f, + -2.33f, 0.22f)); + } + auto referenceFrame = mBaseFrame; + // TODO: Is this correct for sure? + //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90; + + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} +#endif + +TEST_F(TransactionTest, DeferredTransaction) { + // Synchronization surface + constexpr static int SYNC_LAYER = 2; + auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1, + PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(syncSurfaceControl != nullptr); + ASSERT_TRUE(syncSurfaceControl->isValid()); + + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->setLayer(INT32_MAX - 1)); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->setPosition(mDisplayWidth - 2, mDisplayHeight - 2)); + ASSERT_EQ(NO_ERROR, syncSurfaceControl->show()); + } + auto referenceFrame = mBaseFrame; + referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2, + mDisplayWidth - 1, mDisplayHeight - 1)); + referenceFrame[SYNC_LAYER].mSwapCount = 1; + EXPECT_EQ(2, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // set up two deferred transactions on different frames - these should not yield composited + // frames + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.75)); + mFGSurfaceControl + ->deferTransactionUntil(syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber()); + } + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(128, 128)); + mFGSurfaceControl + ->deferTransactionUntil(syncSurfaceControl->getHandle(), + syncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + } + EXPECT_EQ(4, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // should trigger the first deferred transaction, but not the second one + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + sFakeComposer->runVSyncAndWait(); + EXPECT_EQ(5, sFakeComposer->getFrameCount()); + + referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f; + referenceFrame[SYNC_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // should show up immediately since it's not deferred + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(1.0)); + } + referenceFrame[FG_LAYER].mPlaneAlpha = 1.f; + EXPECT_EQ(6, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // trigger the second deferred transaction + fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY); + sFakeComposer->runVSyncAndWait(); + // TODO: Compute from layer size? + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64}; + referenceFrame[SYNC_LAYER].mSwapCount++; + EXPECT_EQ(7, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(TransactionTest, SetRelativeLayer) { + constexpr int RELATIVE_LAYER = 2; + auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64, + PIXEL_FORMAT_RGBA_8888, 0); + fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED); + + // Now we stack the surface above the foreground surface and make sure it is visible. + { + GlobalTransactionScope gts(*sFakeComposer); + relativeSurfaceControl->setPosition(64, 64); + relativeSurfaceControl->show(); + relativeSurfaceControl->setRelativeLayer(mFGSurfaceControl->getHandle(), 1); + } + auto referenceFrame = mBaseFrame; + // NOTE: All three layers will be visible as the surfaces are + // transparent because of the RGBA format. + referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64)); + referenceFrame[RELATIVE_LAYER].mSwapCount = 1; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + // A call to setLayer will override a call to setRelativeLayer + { + GlobalTransactionScope gts(*sFakeComposer); + relativeSurfaceControl->setLayer(0); + } + + // Previous top layer will now appear at the bottom. + auto referenceFrame2 = mBaseFrame; + referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]); + EXPECT_EQ(3, sFakeComposer->getFrameCount()); + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +class ChildLayerTest : public TransactionTest { +protected: + constexpr static int CHILD_LAYER = 2; + + void SetUp() override { + TransactionTest::SetUp(); + mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + fillSurfaceRGBA8(mChild, LIGHT_GRAY); + + sFakeComposer->runVSyncAndWait(); + mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10)); + mBaseFrame[CHILD_LAYER].mSwapCount = 1; + ASSERT_EQ(2, sFakeComposer->getFrameCount()); + ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + } + void TearDown() override { + mChild = 0; + TransactionTest::TearDown(); + } + + sp<SurfaceControl> mChild; +}; + +TEST_F(ChildLayerTest, Positioning) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + // Move to the same position as in the original setup. + mFGSurfaceControl->setPosition(64, 64); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setPosition(0, 0)); + } + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Cropping) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + mFGSurfaceControl->setCrop(Rect(0, 0, 5, 5)); + } + // NOTE: The foreground surface would be occluded by the child + // now, but is included in the stack because the child is + // transparent. + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, FinalCropping) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + mFGSurfaceControl->setFinalCrop(Rect(0, 0, 5, 5)); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Constraints) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mFGSurfaceControl->setPosition(0, 0); + mChild->setPosition(63, 63); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Scaling) { + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setPosition(0, 0); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setMatrix(2.0, 0, 0, 2.0); + } + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, LayerAlpha) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + ASSERT_EQ(NO_ERROR, mChild->setAlpha(0.5)); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + ASSERT_EQ(NO_ERROR, mFGSurfaceControl->setAlpha(0.5)); + } + + auto referenceFrame2 = referenceFrame; + referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f; + referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, ReparentChildren) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + mFGSurfaceControl->setPosition(64, 64); + } + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->reparentChildren(mBGSurfaceControl->getHandle()); + } + + auto referenceFrame2 = referenceFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, DetachChildren) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(10, 10); + mFGSurfaceControl->setPosition(64, 64); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64}; + referenceFrame[CHILD_LAYER].mDisplayFrame = + hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->detachChildren(); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->hide(); + } + + // Nothing should have changed. The child control becomes a no-op + // zombie on detach. See comments for detachChildren in the + // SurfaceControl.h file. + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setOverrideScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // We cause scaling by 2. + mFGSurfaceControl->setSize(128, 128); + } + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f}; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20}; + referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +// Regression test for b/37673612 +TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->show(); + mChild->setPosition(0, 0); + mFGSurfaceControl->setPosition(0, 0); + } + + // We set things up as in b/37673612 so that there is a mismatch between the buffer size and + // the WM specified state size. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 64); + } + + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 64, 128); + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + + // The child should still be in the same place and not have any strange scaling as in + // b/37673612. + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64}; + referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f}; + referenceFrame[FG_LAYER].mSwapCount++; + referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10}; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +TEST_F(ChildLayerTest, Bug36858924) { + // Destroy the child layer + mChild.clear(); + + // Now recreate it as hidden + mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden, + mFGSurfaceControl.get()); + + // Show the child layer in a deferred transaction + { + GlobalTransactionScope gts(*sFakeComposer); + mChild->deferTransactionUntil(mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); + mChild->show(); + } + + // Render the foreground surface a few times + // + // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third + // frame because SurfaceFlinger would never process the deferred transaction and would therefore + // never acquire/release the first buffer + ALOGI("Filling 1"); + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 2"); + fillSurfaceRGBA8(mFGSurfaceControl, BLUE); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 3"); + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + ALOGI("Filling 4"); + fillSurfaceRGBA8(mFGSurfaceControl, GREEN); + sFakeComposer->runVSyncAndWait(); +} + +class LatchingTest : public TransactionTest { +protected: + void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); } + + void unlockFGBuffer() { + sp<Surface> s = mFGSurfaceControl->getSurface(); + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + sFakeComposer->runVSyncAndWait(); + } + + void completeFGResize() { + fillSurfaceRGBA8(mFGSurfaceControl, RED); + sFakeComposer->runVSyncAndWait(); + } + void restoreInitialState() { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(64, 64); + mFGSurfaceControl->setPosition(64, 64); + mFGSurfaceControl->setCrop(Rect(0, 0, 64, 64)); + mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1)); + } +}; + +TEST_F(LatchingTest, SurfacePositionLatching) { + // By default position can be updated even while + // a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(32, 32); + mFGSurfaceControl->setPosition(100, 100); + } + + // The size should not have updated as we have not provided a new buffer. + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + // Now we repeat with setGeometryAppliesWithResize + // and verify the position DOESN'T latch. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setSize(32, 32); + mFGSurfaceControl->setPosition(100, 100); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32}; + referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, CropLatching) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63)); + } + + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63}; + referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setCrop(Rect(0, 0, 63, 63)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63}; + referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, FinalCropLatching) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame1[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame2[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); +} + +// In this test we ensure that setGeometryAppliesWithResize actually demands +// a buffer of the new size, and not just any size. +TEST_F(LatchingTest, FinalCropLatchingBufferOldSize) { + // Normally the crop applies immediately even while a resize is pending. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + + auto referenceFrame1 = mBaseFrame; + referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame1[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame())); + + restoreInitialState(); + + // In order to prepare to submit a buffer at the wrong size, we acquire it prior to + // initiating the resize. + lockAndFillFGBuffer(); + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + // We now submit our old buffer, at the old size, and ensure it doesn't + // trigger geometry latching. + unlockFGBuffer(); + + auto referenceFrame2 = mBaseFrame; + referenceFrame2[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame())); + + completeFGResize(); + auto referenceFrame3 = referenceFrame2; + referenceFrame3[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 127, 127}; + referenceFrame3[FG_LAYER].mSourceCrop = + hwc_frect_t{0.f, 0.f, static_cast<float>(127 - 64), static_cast<float>(127 - 64)}; + referenceFrame3[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame3, sFakeComposer->getLatestFrame())); +} + +TEST_F(LatchingTest, FinalCropLatchingRegressionForb37531386) { + // In this scenario, we attempt to set the final crop a second time while the resize + // is still pending, and ensure we are successful. Success meaning the second crop + // is the one which eventually latches and not the first. + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setSize(128, 128); + mFGSurfaceControl->setGeometryAppliesWithResize(); + mFGSurfaceControl->setFinalCrop(Rect(64, 64, 127, 127)); + } + + { + GlobalTransactionScope gts(*sFakeComposer); + mFGSurfaceControl->setFinalCrop(Rect(0, 0, -1, -1)); + } + EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); + + completeFGResize(); + + auto referenceFrame = mBaseFrame; + referenceFrame[FG_LAYER].mSwapCount++; + EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame())); +} + +} // namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment; + ::testing::AddGlobalTestEnvironment(fakeEnvironment); + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} |