diff options
28 files changed, 1170 insertions, 361 deletions
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index a065a4ce28..916af699e0 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -116,7 +116,7 @@ struct InputWindowInfo { INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002, INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004, }; - + /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ @@ -165,6 +165,8 @@ struct InputWindowInfo { int32_t displayId; int32_t portalToDisplayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; + bool replaceTouchableRegionWithCrop; + wp<IBinder> touchableRegionCropHandle; void addTouchableRegion(const Rect& region); diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 0dd795a676..202d6d2e6a 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -133,7 +133,7 @@ struct AIBinder_DeathRecipient { // binderDied receipt only gives us information about the IBinder. struct TransferDeathRecipient : ::android::IBinder::DeathRecipient { TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie, - const AIBinder_DeathRecipient_onBinderDied& onDied) + const AIBinder_DeathRecipient_onBinderDied onDied) : mWho(who), mCookie(cookie), mOnDied(onDied) {} void binderDied(const ::android::wp<::android::IBinder>& who) override; @@ -144,7 +144,7 @@ struct AIBinder_DeathRecipient { private: ::android::wp<::android::IBinder> mWho; void* mCookie; - const AIBinder_DeathRecipient_onBinderDied& mOnDied; + const AIBinder_DeathRecipient_onBinderDied mOnDied; }; explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied); diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 5c5613df82..5a60347ed3 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -98,7 +98,8 @@ status_t InputWindowInfo::write(Parcel& output) const { output.writeInt32(portalToDisplayId); applicationInfo.write(output); output.write(touchableRegion); - + output.writeBool(replaceTouchableRegionWithCrop); + output.writeWeakBinder(touchableRegionCropHandle); return OK; } @@ -140,6 +141,8 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { ret.portalToDisplayId = from.readInt32(); ret.applicationInfo = InputApplicationInfo::read(from); from.read(ret.touchableRegion); + ret.replaceTouchableRegionWithCrop = from.readBool(); + ret.touchableRegionCropHandle = from.readWeakBinder(); return ret; } diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index 09dd72b13b..6db18abacf 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -37,6 +37,7 @@ TEST(InputWindowInfo, ParcellingWithoutToken) { } TEST(InputWindowInfo, Parcelling) { + sp<IBinder> touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); i.name = "Foobar"; @@ -62,6 +63,8 @@ TEST(InputWindowInfo, Parcelling) { i.inputFeatures = 29; i.displayId = 34; i.portalToDisplayId = 2; + i.replaceTouchableRegionWithCrop = true; + i.touchableRegionCropHandle = touchableRegionCropHandle; Parcel p; i.write(p); @@ -92,6 +95,8 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.inputFeatures, i2.inputFeatures); ASSERT_EQ(i.displayId, i2.displayId); ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId); + ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); + ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); } } // namespace test diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index bb18aa10db..4751e5f122 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -105,6 +105,10 @@ bool BufferLayer::isFixedSize() const { return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE; } +bool BufferLayer::usesSourceCrop() const { + return true; +} + static constexpr mat4 inverseOrientation(uint32_t transform) { const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 4f3ad413c1..dc103cbada 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -78,6 +78,8 @@ public: // isFixedSize - true if content has a fixed size bool isFixedSize() const override; + bool usesSourceCrop() const override; + bool isHdrY410() const override; void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h index bd83d1afc4..53d5b5b605 100644 --- a/services/surfaceflinger/ColorLayer.h +++ b/services/surfaceflinger/ColorLayer.h @@ -46,11 +46,10 @@ public: bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; } protected: - FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; } - bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer) override; + virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, + bool useIdentityTransform, Region& clearRegion, + const bool supportProtectedContent, + renderengine::LayerSettings& layer); private: std::shared_ptr<compositionengine::Layer> mCompositionLayer; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index 6cc87ba79f..9f635b9a35 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -24,12 +24,21 @@ class Fence; namespace compositionengine { +struct LayerFECompositionState; + // Defines the interface used by the CompositionEngine to make requests // of the front-end layer class LayerFE : public virtual RefBase { public: + // Latches the output-independent state. If includeGeometry is false, the + // geometry state can be skipped. + virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0; + // Called after the layer is displayed to update the presentation fence virtual void onLayerDisplayed(const sp<Fence>&) = 0; + + // Gets some kind of identifier for the layer for debug purposes. + virtual const char* getDebugName() const = 0; }; } // namespace compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 785a6d7fbe..e6ee078624 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -41,6 +41,22 @@ struct LayerFECompositionState { Region geomVisibleRegion; /* + * Geometry state + */ + + bool isSecure{false}; + bool geomUsesSourceCrop{false}; + bool geomBufferUsesDisplayInverseTransform{false}; + uint32_t geomBufferTransform{0}; + ui::Transform geomLayerTransform; + ui::Transform geomInverseLayerTransform; + Rect geomBufferSize; + Rect geomContentCrop; + Rect geomCrop; + Region geomActiveTransparentRegion; + FloatRect geomLayerBounds; + + /* * Presentation */ diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index e7a17c474e..cd63b57d86 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -64,6 +64,15 @@ public: // TODO(lpique): Make this protected once it is only internally called. virtual CompositionState& editState() = 0; + // Recalculates the state of the output layer from the output-independent + // layer. If includeGeometry is false, the geometry state can be skipped. + virtual void updateCompositionState(bool includeGeometry) = 0; + + // Writes the geometry state to the HWC, or does nothing if this layer does + // not use the HWC. If includeGeometry is false, the geometry state can be + // skipped. + virtual void writeStateToHWC(bool includeGeometry) const = 0; + // Debugging virtual void dump(std::string& result) const = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index d8f0cdd5e8..6a4818f10f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -21,6 +21,8 @@ #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> +#include <ui/FloatRect.h> +#include <ui/Rect.h> #include "DisplayHardware/DisplayIdentification.h" @@ -41,9 +43,18 @@ public: const OutputLayerCompositionState& getState() const override; OutputLayerCompositionState& editState() override; + void updateCompositionState(bool) override; + void writeStateToHWC(bool) const override; + void dump(std::string& result) const override; + virtual FloatRect calculateOutputSourceCrop() const; + virtual Rect calculateOutputDisplayFrame() const; + virtual uint32_t calculateOutputRelativeBufferTransform() const; + private: + Rect calculateInitialCrop() const; + const compositionengine::Output& mOutput; std::shared_ptr<compositionengine::Layer> mLayer; sp<compositionengine::LayerFE> mLayerFE; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index a0c2a63a73..aab18db3c9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -17,6 +17,7 @@ #pragma once #include <compositionengine/LayerFE.h> +#include <compositionengine/LayerFECompositionState.h> #include <gmock/gmock.h> #include <ui/Fence.h> @@ -29,7 +30,10 @@ public: LayerFE(); virtual ~LayerFE(); + MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool)); MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&)); + + MOCK_CONST_METHOD0(getDebugName, const char*()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 54c7407a98..29cd08a681 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -38,6 +38,9 @@ public: MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&()); MOCK_METHOD0(editState, impl::OutputLayerCompositionState&()); + MOCK_METHOD1(updateCompositionState, void(bool)); + MOCK_CONST_METHOD1(writeStateToHWC, void(bool)); + MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp index c49701315b..959843050c 100644 --- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp @@ -24,7 +24,7 @@ namespace android::compositionengine::impl { using android::base::StringAppendF; void dumpVal(std::string& out, const char* name, bool value) { - StringAppendF(&out, "%s=%c ", name, value ? 'T' : 'F'); + StringAppendF(&out, "%s=%s ", name, value ? "true" : "false"); } void dumpVal(std::string& out, const char* name, const void* value) { @@ -56,7 +56,7 @@ void dumpVal(std::string& out, const char* name, const std::string& value) { } void dumpVal(std::string& out, const char* name, const char* valueName, int value) { - StringAppendF(&out, "%s=%s (%d)", name, valueName, value); + StringAppendF(&out, "%s=%s (%d) ", name, valueName, value); } void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) { diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp index 109e9f8438..96e9731768 100644 --- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp @@ -52,7 +52,9 @@ LayerCompositionState& Layer::editState() { } void Layer::dump(std::string& out) const { - android::base::StringAppendF(&out, " Layer %p\n", this); + auto layerFE = getLayerFE(); + android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this, + layerFE ? layerFE->getDebugName() : "<unknown>"); mState.dump(out); } diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp index 517b641594..40c4da97a8 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp @@ -31,6 +31,25 @@ void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color va void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) { out.append(" "); + dumpVal(out, "isSecure", state.isSecure); + dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop); + dumpVal(out, "geomBufferUsesDisplayInverseTransform", + state.geomBufferUsesDisplayInverseTransform); + dumpVal(out, "geomLayerTransform", state.geomLayerTransform); + + out.append("\n "); + dumpVal(out, "geomBufferSize", state.geomBufferSize); + dumpVal(out, "geomContentCrop", state.geomContentCrop); + dumpVal(out, "geomCrop", state.geomCrop); + dumpVal(out, "geomBufferTransform", state.geomBufferTransform); + + out.append("\n "); + dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion); + + out.append(" "); + dumpVal(out, "geomLayerBounds", state.geomLayerBounds); + + out.append("\n "); dumpVal(out, "blend", toString(state.blendMode), state.blendMode); dumpVal(out, "alpha", state.alpha); @@ -61,7 +80,7 @@ void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) { } // namespace void LayerCompositionState::dump(std::string& out) const { - out.append(" frontend:\n"); + out.append(" frontend:\n"); dumpFrontEnd(out, frontEnd); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index ad4c7bf501..d22bdaf625 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -147,7 +147,7 @@ void Output::dumpBase(std::string& out) const { out.append(" No render surface!\n"); } - out.append("\n %d Layers", mOutputLayersOrderedByZ.size()); + android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size()); for (const auto& outputLayer : mOutputLayersOrderedByZ) { if (!outputLayer) { continue; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 78807ffa9f..9549054bd6 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -20,6 +20,7 @@ namespace android::compositionengine::impl { void OutputCompositionState::dump(std::string& out) const { + out.append(" "); dumpVal(out, "isEnabled", isEnabled); dumpVal(out, "isSecure", isSecure); @@ -37,7 +38,7 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "scissor", scissor); dumpVal(out, "needsFiltering", needsFiltering); - out.append("\n"); + out.append("\n "); dumpVal(out, "colorMode", toString(colorMode), colorMode); dumpVal(out, "renderIntent", toString(renderIntent), renderIntent); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 10da49dc5c..379ad87922 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -19,7 +19,10 @@ #include <compositionengine/Layer.h> #include <compositionengine/LayerFE.h> #include <compositionengine/Output.h> +#include <compositionengine/impl/LayerCompositionState.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayer.h> +#include <compositionengine/impl/OutputLayerCompositionState.h> #include "DisplayHardware/HWComposer.h" @@ -29,6 +32,18 @@ OutputLayer::~OutputLayer() = default; namespace impl { +namespace { + +FloatRect reduce(const FloatRect& win, const Region& exclude) { + if (CC_LIKELY(exclude.isEmpty())) { + return win; + } + // Convert through Rect (by rounding) for lack of FloatRegion + return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect(); +} + +} // namespace + std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId, const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer, @@ -77,10 +92,296 @@ OutputLayerCompositionState& OutputLayer::editState() { return mState; } +Rect OutputLayer::calculateInitialCrop() const { + const auto& layerState = mLayer->getState().frontEnd; + + // apply the projection's clipping to the window crop in + // layerstack space, and convert-back to layer space. + // if there are no window scaling involved, this operation will map to full + // pixels in the buffer. + + FloatRect activeCropFloat = + reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion); + + const Rect& viewport = mOutput.getState().viewport; + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; + // Transform to screen space. + activeCropFloat = layerTransform.transform(activeCropFloat); + activeCropFloat = activeCropFloat.intersect(viewport.toFloatRect()); + // Back to layer space to work with the content crop. + activeCropFloat = inverseLayerTransform.transform(activeCropFloat); + + // This needs to be here as transform.transform(Rect) computes the + // transformed rect and then takes the bounding box of the result before + // returning. This means + // transform.inverse().transform(transform.transform(Rect)) != Rect + // in which case we need to make sure the final rect is clipped to the + // display bounds. + Rect activeCrop{activeCropFloat}; + if (!activeCrop.intersect(layerState.geomBufferSize, &activeCrop)) { + activeCrop.clear(); + } + return activeCrop; +} + +FloatRect OutputLayer::calculateOutputSourceCrop() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + if (!layerState.geomUsesSourceCrop) { + return {}; + } + + // the content crop is the area of the content that gets scaled to the + // layer's size. This is in buffer space. + FloatRect crop = layerState.geomContentCrop.toFloatRect(); + + // In addition there is a WM-specified crop we pull from our drawing state. + Rect activeCrop = calculateInitialCrop(); + const Rect& bufferSize = layerState.geomBufferSize; + + int winWidth = bufferSize.getWidth(); + int winHeight = bufferSize.getHeight(); + + // The bufferSize for buffer state layers can be unbounded ([0, 0, -1, -1]) + // if display frame hasn't been set and the parent is an unbounded layer. + if (winWidth < 0 && winHeight < 0) { + return crop; + } + + // Transform the window crop to match the buffer coordinate system, + // which means using the inverse of the current transform set on the + // SurfaceFlingerConsumer. + uint32_t invTransform = layerState.geomBufferTransform; + if (layerState.geomBufferUsesDisplayInverseTransform) { + /* + * the code below applies the primary display's inverse transform to the + * buffer + */ + uint32_t invTransformOrient = outputState.orientation; + // calculate the inverse transform + if (invTransformOrient & HAL_TRANSFORM_ROT_90) { + invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + // and apply to the current transform + invTransform = + (ui::Transform(invTransformOrient) * ui::Transform(invTransform)).getOrientation(); + } + + if (invTransform & HAL_TRANSFORM_ROT_90) { + // If the activeCrop has been rotate the ends are rotated but not + // the space itself so when transforming ends back we can't rely on + // a modification of the axes of rotation. To account for this we + // need to reorient the inverse rotation in terms of the current + // axes of rotation. + bool is_h_flipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0; + bool is_v_flipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0; + if (is_h_flipped == is_v_flipped) { + invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + std::swap(winWidth, winHeight); + } + const Rect winCrop = + activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight()); + + // below, crop is intersected with winCrop expressed in crop's coordinate space + float xScale = crop.getWidth() / float(winWidth); + float yScale = crop.getHeight() / float(winHeight); + + float insetL = winCrop.left * xScale; + float insetT = winCrop.top * yScale; + float insetR = (winWidth - winCrop.right) * xScale; + float insetB = (winHeight - winCrop.bottom) * yScale; + + crop.left += insetL; + crop.top += insetT; + crop.right -= insetR; + crop.bottom -= insetB; + + return crop; +} + +Rect OutputLayer::calculateOutputDisplayFrame() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + // apply the layer's transform, followed by the display's global transform + // here we're guaranteed that the layer's transform preserves rects + Region activeTransparentRegion = layerState.geomActiveTransparentRegion; + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; + const Rect& bufferSize = layerState.geomBufferSize; + Rect activeCrop = layerState.geomCrop; + if (!activeCrop.isEmpty() && bufferSize.isValid()) { + activeCrop = layerTransform.transform(activeCrop); + if (!activeCrop.intersect(outputState.viewport, &activeCrop)) { + activeCrop.clear(); + } + activeCrop = inverseLayerTransform.transform(activeCrop, true); + // This needs to be here as transform.transform(Rect) computes the + // transformed rect and then takes the bounding box of the result before + // returning. This means + // transform.inverse().transform(transform.transform(Rect)) != Rect + // in which case we need to make sure the final rect is clipped to the + // display bounds. + if (!activeCrop.intersect(bufferSize, &activeCrop)) { + activeCrop.clear(); + } + // mark regions outside the crop as transparent + activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top)); + activeTransparentRegion.orSelf( + Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight())); + activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); + activeTransparentRegion.orSelf( + Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom)); + } + + // reduce uses a FloatRect to provide more accuracy during the + // transformation. We then round upon constructing 'frame'. + Rect frame{ + layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))}; + if (!frame.intersect(outputState.viewport, &frame)) { + frame.clear(); + } + const ui::Transform displayTransform{outputState.transform}; + + return displayTransform.transform(frame); +} + +uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + /* + * Transformations are applied in this order: + * 1) buffer orientation/flip/mirror + * 2) state transformation (window manager) + * 3) layer orientation (screen orientation) + * (NOTE: the matrices are multiplied in reverse order) + */ + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform displayTransform{outputState.orientation}; + const ui::Transform bufferTransform{layerState.geomBufferTransform}; + ui::Transform transform(displayTransform * layerTransform * bufferTransform); + + if (layerState.geomBufferUsesDisplayInverseTransform) { + /* + * the code below applies the primary display's inverse transform to the + * buffer + */ + uint32_t invTransform = outputState.orientation; + // calculate the inverse transform + if (invTransform & HAL_TRANSFORM_ROT_90) { + invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + + /* + * Here we cancel out the orientation component of the WM transform. + * The scaling and translate components are already included in our bounds + * computation so it's enough to just omit it in the composition. + * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why. + */ + transform = + ui::Transform(invTransform) * displayTransform * layerTransform * bufferTransform; + } + + // this gives us only the "orientation" component of the transform + return transform.getOrientation(); +} // namespace impl + +void OutputLayer::updateCompositionState(bool includeGeometry) { + if (includeGeometry) { + mState.displayFrame = calculateOutputDisplayFrame(); + mState.sourceCrop = calculateOutputSourceCrop(); + mState.bufferTransform = + static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform()); + + if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) || + (mState.bufferTransform & ui::Transform::ROT_INVALID)) { + mState.forceClientComposition = true; + } + } +} + +void OutputLayer::writeStateToHWC(bool includeGeometry) const { + // Skip doing this if there is no HWC interface + if (!mState.hwc) { + return; + } + + auto& hwcLayer = (*mState.hwc).hwcLayer; + if (!hwcLayer) { + ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s", + mLayerFE->getDebugName(), mOutput.getName().c_str()); + return; + } + + if (includeGeometry) { + // Output dependent state + + if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", + mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top, + mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " + "%s (%d)", + mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top, + mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z, + to_string(error).c_str(), static_cast<int32_t>(error)); + } + + if (auto error = + hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(), + toString(mState.bufferTransform).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + // Output independent state + + const auto& outputIndependentState = mLayer->getState().frontEnd; + + if (auto error = hwcLayer->setBlendMode( + static_cast<HWC2::BlendMode>(outputIndependentState.blendMode)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(), + toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(), + outputIndependentState.alpha, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = + hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + } +} + void OutputLayer::dump(std::string& out) const { using android::base::StringAppendF; - StringAppendF(&out, " Output Layer %p\n", this); + StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(), + mLayerFE->getDebugName()); mState.dump(out); } diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp index 10f27b86f9..861ea5757b 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp @@ -46,7 +46,7 @@ void OutputLayerCompositionState::dump(std::string& out) const { dumpVal(out, "clearClientTarget", clearClientTarget); dumpVal(out, "displayFrame", displayFrame); dumpVal(out, "sourceCrop", sourceCrop); - dumpVal(out, "bufferTransform%", toString(bufferTransform), bufferTransform); + dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform); dumpVal(out, "z-index", z); if (hwc) { diff --git a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h new file mode 100644 index 0000000000..6741cc9b7a --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 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 <string> + +#include <android-base/stringprintf.h> +#include <gmock/gmock.h> + +namespace { + +using android::base::StringAppendF; +using FloatRect = android::FloatRect; + +void dumpFloatRect(const FloatRect& rect, std::string& result, const char* name) { + StringAppendF(&result, "%s (%f %f %f %f) ", name, rect.left, rect.top, rect.right, rect.bottom); +} + +// Checks for a region match +MATCHER_P(FloatRectEq, expected, "") { + std::string buf; + buf.append("FloatRects are not equal\n"); + dumpFloatRect(expected, buf, "expected rect"); + dumpFloatRect(arg, buf, "actual rect"); + *result_listener << buf; + + const float TOLERANCE = 1e-3f; + return (std::fabs(expected.left - arg.left) < TOLERANCE) && + (std::fabs(expected.top - arg.top) < TOLERANCE) && + (std::fabs(expected.right - arg.right) < TOLERANCE) && + (std::fabs(expected.bottom - arg.bottom) < TOLERANCE); +} + +} // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index f7dcf6f08e..2504fa62ea 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -21,18 +21,40 @@ #include <compositionengine/mock/Output.h> #include <gtest/gtest.h> +#include "FloatRectMatcher.h" #include "MockHWC2.h" #include "MockHWComposer.h" +#include "RectMatcher.h" namespace android::compositionengine { namespace { +using testing::_; +using testing::Return; +using testing::ReturnRef; using testing::StrictMock; constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42}; +constexpr auto TR_IDENT = 0u; +constexpr auto TR_FLP_H = HAL_TRANSFORM_FLIP_H; +constexpr auto TR_FLP_V = HAL_TRANSFORM_FLIP_V; +constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90; +constexpr auto TR_ROT_180 = TR_FLP_H | TR_FLP_V; +constexpr auto TR_ROT_270 = TR_ROT_90 | TR_ROT_180; + +const std::string kOutputName{"Test Output"}; + class OutputLayerTest : public testing::Test { public: + OutputLayerTest() { + EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE")); + EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName)); + + EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState)); + EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState)); + } + ~OutputLayerTest() override = default; compositionengine::mock::Output mOutput; @@ -41,15 +63,18 @@ public: sp<compositionengine::mock::LayerFE> mLayerFE{ new StrictMock<compositionengine::mock::LayerFE>()}; impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE}; + + impl::LayerCompositionState mLayerState; + impl::OutputCompositionState mOutputState; }; -/* ------------------------------------------------------------------------ +/* * Basic construction */ TEST_F(OutputLayerTest, canInstantiateOutputLayer) {} -/* ------------------------------------------------------------------------ +/* * OutputLayer::initialize() */ @@ -71,15 +96,481 @@ TEST_F(OutputLayerTest, initializingOutputLayerWithHwcDisplayCreatesHwcLayer) { mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID); - const auto& state = mOutputLayer.getState(); - ASSERT_TRUE(state.hwc); + const auto& outputLayerState = mOutputLayer.getState(); + ASSERT_TRUE(outputLayerState.hwc); - const auto& hwcState = *state.hwc; + const auto& hwcState = *outputLayerState.hwc; EXPECT_EQ(&hwcLayer, hwcState.hwcLayer.get()); EXPECT_CALL(hwc, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer)); mOutputLayer.editState().hwc.reset(); } +/* + * OutputLayer::calculateOutputSourceCrop() + */ + +struct OutputLayerSourceCropTest : public OutputLayerTest { + OutputLayerSourceCropTest() { + // Set reasonable default values for a simple case. Each test will + // set one specific value to something different. + mLayerState.frontEnd.geomUsesSourceCrop = true; + mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomActiveTransparentRegion = Region{}; + mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; + mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT}; + mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomBufferTransform = TR_IDENT; + + mOutputState.viewport = Rect{0, 0, 1920, 1080}; + } + + FloatRect calculateOutputSourceCrop() { + mLayerState.frontEnd.geomInverseLayerTransform = + mLayerState.frontEnd.geomLayerTransform.inverse(); + + return mOutputLayer.calculateOutputSourceCrop(); + } +}; + +TEST_F(OutputLayerSourceCropTest, computesEmptyIfSourceCropNotUsed) { + mLayerState.frontEnd.geomUsesSourceCrop = false; + + const FloatRect expected{}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) { + const FloatRect expected{0.f, 0.f, 1920.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) { + mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f}; + + const FloatRect expected{0.f, 0.f, 1920.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) { + mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f}; + mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); + + const FloatRect expected{0.f, 0.f, 1080.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) { + struct Entry { + uint32_t bufferInvDisplay; + uint32_t buffer; + uint32_t display; + FloatRect expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 12> testData = { + // clang-format off + // inv buffer display expected + /* 0 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 1 */ Entry{false, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 2 */ Entry{false, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 3 */ Entry{false, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + /* 4 */ Entry{true, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 5 */ Entry{true, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 6 */ Entry{true, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 7 */ Entry{true, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + /* 8 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 9 */ Entry{false, TR_ROT_90, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 10 */ Entry{false, TR_ROT_180, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 11 */ Entry{false, TR_ROT_270, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay; + mLayerState.frontEnd.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(entry.expected)) << "entry " << i; + } +} + +TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) { + mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 960, 540}; + + const FloatRect expected{0.f, 0.f, 960.f, 540.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) { + mOutputState.viewport = Rect{0, 0, 960, 540}; + + const FloatRect expected{0.f, 0.f, 960.f, 540.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +/* + * OutputLayer::calculateOutputDisplayFrame() + */ + +struct OutputLayerDisplayFrameTest : public OutputLayerTest { + OutputLayerDisplayFrameTest() { + // Set reasonable default values for a simple case. Each test will + // set one specific value to something different. + + mLayerState.frontEnd.geomActiveTransparentRegion = Region{}; + mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT}; + mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false; + mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; + + mOutputState.viewport = Rect{0, 0, 1920, 1080}; + mOutputState.transform = ui::Transform{TR_IDENT}; + } + + Rect calculateOutputDisplayFrame() { + mLayerState.frontEnd.geomInverseLayerTransform = + mLayerState.frontEnd.geomLayerTransform.inverse(); + + return mOutputLayer.calculateOutputDisplayFrame(); + } +}; + +TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) { + const Rect expected{0, 0, 1920, 1080}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) { + mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}}; + const Rect expected{0, 0, 0, 0}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) { + mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500}; + const Rect expected{100, 200, 300, 500}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) { + mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500}; + mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); + const Rect expected{1420, 100, 1720, 300}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) { + mLayerState.frontEnd.geomCrop = Rect{}; + const Rect expected{0, 0, 1920, 1080}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) { + mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f}; + const Rect expected{0, 0, 960, 540}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) { + mOutputState.viewport = Rect{0, 0, 960, 540}; + const Rect expected{0, 0, 960, 540}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) { + mOutputState.transform = ui::Transform{HAL_TRANSFORM_ROT_90}; + const Rect expected{-1080, 0, 0, 1920}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +/* + * OutputLayer::calculateOutputRelativeBufferTransform() + */ + +TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) { + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false; + + struct Entry { + uint32_t layer; + uint32_t buffer; + uint32_t display; + uint32_t expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 24> testData = { + // clang-format off + // layer buffer display expected + /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT}, + /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_ROT_90}, + /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_ROT_180}, + /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_ROT_270}, + + /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_IDENT}, + /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_90}, + /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_180}, + /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_ROT_270}, + + /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V}, + /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_180}, + /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_IDENT}, + /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_180}, + + /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_90}, + /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_180}, + /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT ^ TR_ROT_270}, + /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_IDENT}, + + /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_ROT_180}, + /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT ^ TR_ROT_270}, + /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_IDENT}, + /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_90}, + + /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_270}, + /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_IDENT}, + /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_90}, + /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_180}, + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080); + mLayerState.frontEnd.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(); + EXPECT_EQ(entry.expected, actual) << "entry " << i; + } +} + +TEST_F(OutputLayerTest, + calculateOutputRelativeBufferTransformTestWithOfBufferUsesDisplayInverseTransform) { + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = true; + + struct Entry { + uint32_t layer; + uint32_t buffer; + uint32_t display; + uint32_t expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 24> testData = { + // clang-format off + // layer buffer display expected + /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT}, + /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_IDENT}, + /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_IDENT}, + /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_IDENT}, + + /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H}, + /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H}, + /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H}, + /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H}, + + /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V}, + /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_90}, + /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_ROT_180}, + /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_270}, + + /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_90}, + /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_90}, + /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT ^ TR_ROT_90}, + /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_ROT_90}, + + /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_ROT_180}, + /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT ^ TR_ROT_180}, + /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_180}, + /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_180}, + + /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_270}, + /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_270}, + /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_270}, + /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_270}, + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerState.frontEnd.geomLayerTransform = ui::Transform{entry.layer}; + mLayerState.frontEnd.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(); + EXPECT_EQ(entry.expected, actual) << "entry " << i; + } +} + +/* + * OutputLayer::updateCompositionState() + */ + +struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer { + OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output, + std::shared_ptr<compositionengine::Layer> layer, + sp<compositionengine::LayerFE> layerFE) + : impl::OutputLayer(output, layer, layerFE) {} + // Mock everything called by updateCompositionState to simplify testing it. + MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect()); + MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect()); + MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t()); +}; + +struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest { +public: + OutputLayerUpdateCompositionStateTest() { + EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState)); + EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState)); + } + + ~OutputLayerUpdateCompositionStateTest() = default; + + void setupGeometryChildCallValues() { + EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop)); + EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame)); + EXPECT_CALL(mOutputLayer, calculateOutputRelativeBufferTransform()) + .WillOnce(Return(mBufferTransform)); + } + + void validateComputedGeometryState() { + const auto& state = mOutputLayer.getState(); + EXPECT_EQ(kSourceCrop, state.sourceCrop); + EXPECT_EQ(kDisplayFrame, state.displayFrame); + EXPECT_EQ(static_cast<Hwc2::Transform>(mBufferTransform), state.bufferTransform); + } + + const FloatRect kSourceCrop{1.f, 2.f, 3.f, 4.f}; + const Rect kDisplayFrame{11, 12, 13, 14}; + uint32_t mBufferTransform{21}; + + using OutputLayer = OutputLayerPartialMockForUpdateCompositionState; + StrictMock<OutputLayer> mOutputLayer{mOutput, mLayer, mLayerFE}; +}; + +TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) { + mLayerState.frontEnd.isSecure = true; + mOutputState.isSecure = true; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true); + + validateComputedGeometryState(); + + EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + alsoSetsForceCompositionIfSecureLayerOnNonsecureOutput) { + mLayerState.frontEnd.isSecure = true; + mOutputState.isSecure = false; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true); + + validateComputedGeometryState(); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + alsoSetsForceCompositionIfUnsupportedBufferTransform) { + mLayerState.frontEnd.isSecure = true; + mOutputState.isSecure = true; + + mBufferTransform = ui::Transform::ROT_INVALID; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true); + + validateComputedGeometryState(); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) { + mOutputLayer.updateCompositionState(false); + + EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition); +} + +/* + * OutputLayer::writeStateToHWC() + */ + +struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { + static constexpr HWC2::Error kError = HWC2::Error::Unsupported; + static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f}; + static constexpr uint32_t kZOrder = 21u; + static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31); + static constexpr Hwc2::IComposerClient::BlendMode kBlendMode = + static_cast<Hwc2::IComposerClient::BlendMode>(41); + static constexpr float kAlpha = 51.f; + static constexpr uint32_t kType = 61u; + static constexpr uint32_t kAppId = 62u; + + static const Rect kDisplayFrame; + + OutputLayerWriteStateToHWCTest() { + auto& outputLayerState = mOutputLayer.editState(); + outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer); + + outputLayerState.displayFrame = kDisplayFrame; + outputLayerState.sourceCrop = kSourceCrop; + outputLayerState.z = kZOrder; + outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform); + + mLayerState.frontEnd.blendMode = kBlendMode; + mLayerState.frontEnd.alpha = kAlpha; + mLayerState.frontEnd.type = kType; + mLayerState.frontEnd.appId = kAppId; + } + + void expectGeometryCommonCalls() { + EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setTransform(static_cast<HWC2::Transform>(kBufferTransform))) + .WillOnce(Return(kError)); + + EXPECT_CALL(*mHwcLayer, setBlendMode(static_cast<HWC2::BlendMode>(kBlendMode))) + .WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError)); + } + + std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()}; +}; + +const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044}; + +TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) { + mOutputLayer.editState().hwc.reset(); + + mOutputLayer.writeStateToHWC(true); +} + +TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr); + + mOutputLayer.writeStateToHWC(true); +} + +TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) { + expectGeometryCommonCalls(); + + mOutputLayer.writeStateToHWC(true); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h new file mode 100644 index 0000000000..d4c76bc7e8 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h @@ -0,0 +1,45 @@ +/* + * Copyright 2019 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 <string> + +#include <android-base/stringprintf.h> +#include <gmock/gmock.h> + +namespace { + +using android::base::StringAppendF; +using Rect = android::Rect; + +void dumpRect(const Rect& rect, std::string& result, const char* name) { + StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom); +} + +// Checks for a region match +MATCHER_P(RectEq, expected, "") { + std::string buf; + buf.append("Rects are not equal\n"); + dumpRect(expected, buf, "expected rect"); + dumpRect(arg, buf, "actual rect"); + *result_listener << buf; + + return (expected.left == arg.left) && (expected.top == arg.top) && + (expected.right == arg.right) && (expected.bottom == arg.bottom); +} + +} // namespace diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index bdc2fa9c42..16e69b9b26 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -29,6 +29,7 @@ #include <android-base/stringprintf.h> #include <compositionengine/Display.h> #include <compositionengine/Layer.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -104,6 +105,7 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.api = -1; mCurrentState.hasColorTransform = false; mCurrentState.colorSpaceAgnostic = false; + mCurrentState.metadata = args.metadata; // drawing state & current state are identical mDrawingState = mCurrentState; @@ -338,8 +340,6 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) } } - - Rect Layer::getCroppedBufferSize(const State& s) const { Rect size = getBufferSize(s); Rect crop = getCrop(s); @@ -351,36 +351,6 @@ Rect Layer::getCroppedBufferSize(const State& s) const { return size; } -Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const { - // the crop is the area of the window that gets cropped, but not - // scaled in any ways. - const State& s(getDrawingState()); - - // apply the projection's clipping to the window crop in - // layerstack space, and convert-back to layer space. - // if there are no window scaling involved, this operation will map to full - // pixels in the buffer. - - FloatRect activeCropFloat = getBounds(); - ui::Transform t = getTransform(); - // Transform to screen space. - activeCropFloat = t.transform(activeCropFloat); - activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect()); - // Back to layer space to work with the content crop. - activeCropFloat = t.inverse().transform(activeCropFloat); - // This needs to be here as transform.transform(Rect) computes the - // transformed rect and then takes the bounding box of the result before - // returning. This means - // transform.inverse().transform(transform.transform(Rect)) != Rect - // in which case we need to make sure the final rect is clipped to the - // display bounds. - Rect activeCrop{activeCropFloat}; - if (!activeCrop.intersect(getBufferSize(s), &activeCrop)) { - activeCrop.clear(); - } - return activeCrop; -} - void Layer::setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const { // Translate win by the rounded corners rect coordinates, to have all values in @@ -398,189 +368,17 @@ void Layer::setupRoundedCornersCropCoordinates(Rect win, cropCoords[3] = vec2(win.right, win.top); } -FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const { - // the content crop is the area of the content that gets scaled to the - // layer's size. This is in buffer space. - FloatRect crop = getContentCrop().toFloatRect(); - - // In addition there is a WM-specified crop we pull from our drawing state. - const State& s(getDrawingState()); - - Rect activeCrop = computeInitialCrop(display); - Rect bufferSize = getBufferSize(s); - - int32_t winWidth = bufferSize.getWidth(); - int32_t winHeight = bufferSize.getHeight(); - - // The bufferSize for buffer state layers can be unbounded ([0, 0, -1, -1]) if display frame - // hasn't been set and the parent is an unbounded layer. - if (winWidth < 0 && winHeight < 0) { - return crop; - } - - // Transform the window crop to match the buffer coordinate system, - // which means using the inverse of the current transform set on the - // SurfaceFlingerConsumer. - uint32_t invTransform = mCurrentTransform; - if (getTransformToDisplayInverse()) { - /* - * the code below applies the primary display's inverse transform to the - * buffer - */ - uint32_t invTransformOrient = DisplayDevice::getPrimaryDisplayOrientationTransform(); - // calculate the inverse transform - if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) { - invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } - // and apply to the current transform - invTransform = (ui::Transform(invTransformOrient) * - ui::Transform(invTransform)).getOrientation(); - } - - if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - // If the activeCrop has been rotate the ends are rotated but not - // the space itself so when transforming ends back we can't rely on - // a modification of the axes of rotation. To account for this we - // need to reorient the inverse rotation in terms of the current - // axes of rotation. - bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0; - bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0; - if (is_h_flipped == is_v_flipped) { - invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } - std::swap(winWidth, winHeight); - } - const Rect winCrop = - activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight()); - - // below, crop is intersected with winCrop expressed in crop's coordinate space - float xScale = crop.getWidth() / float(winWidth); - float yScale = crop.getHeight() / float(winHeight); - - float insetL = winCrop.left * xScale; - float insetT = winCrop.top * yScale; - float insetR = (winWidth - winCrop.right) * xScale; - float insetB = (winHeight - winCrop.bottom) * yScale; - - crop.left += insetL; - crop.top += insetT; - crop.right -= insetR; - crop.bottom -= insetB; - - return crop; -} - -void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); - LOG_FATAL_IF(!outputLayer->getState().hwc); - auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer; - - if (!hasHwcLayer(display)) { - ALOGE("[%s] failed to setGeometry: no HWC layer found (%s)", mName.string(), - display->getDebugName().c_str()); - return; - } - - LOG_FATAL_IF(!getCompositionLayer()); - auto& commonCompositionState = getCompositionLayer()->editState().frontEnd; - auto& compositionState = outputLayer->editState(); - - // enable this layer - compositionState.forceClientComposition = false; - - if (isSecure() && !display->isSecure()) { - compositionState.forceClientComposition = true; - } - - // this gives us only the "orientation" component of the transform - const State& s(getDrawingState()); - const Rect bufferSize = getBufferSize(s); +void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const { + const auto& drawingState{getDrawingState()}; + auto alpha = static_cast<float>(getAlpha()); auto blendMode = HWC2::BlendMode::None; - if (!isOpaque(s) || getAlpha() != 1.0f) { + if (!isOpaque(drawingState) || alpha != 1.0f) { blendMode = mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage; } - auto error = hwcLayer->setBlendMode(blendMode); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set blend mode %s:" - " %s (%d)", - mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - commonCompositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); - // apply the layer's transform, followed by the display's global transform - // here we're guaranteed that the layer's transform preserves rects - Region activeTransparentRegion(getActiveTransparentRegion(s)); - ui::Transform t = getTransform(); - Rect activeCrop = getCrop(s); - if (!activeCrop.isEmpty() && bufferSize.isValid()) { - activeCrop = t.transform(activeCrop); - if (!activeCrop.intersect(display->getViewport(), &activeCrop)) { - activeCrop.clear(); - } - activeCrop = t.inverse().transform(activeCrop, true); - // This needs to be here as transform.transform(Rect) computes the - // transformed rect and then takes the bounding box of the result before - // returning. This means - // transform.inverse().transform(transform.transform(Rect)) != Rect - // in which case we need to make sure the final rect is clipped to the - // display bounds. - if (!activeCrop.intersect(bufferSize, &activeCrop)) { - activeCrop.clear(); - } - // mark regions outside the crop as transparent - activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top)); - activeTransparentRegion.orSelf( - Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight())); - activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); - activeTransparentRegion.orSelf( - Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom)); - } - - // getBounds returns a FloatRect to provide more accuracy during the - // transformation. We then round upon constructing 'frame'. - Rect frame{t.transform(getBounds(activeTransparentRegion))}; - if (!frame.intersect(display->getViewport(), &frame)) { - frame.clear(); - } - const ui::Transform& tr = display->getTransform(); - Rect transformedFrame = tr.transform(frame); - error = hwcLayer->setDisplayFrame(transformedFrame); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", mName.string(), - transformedFrame.left, transformedFrame.top, transformedFrame.right, - transformedFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error)); - } else { - compositionState.displayFrame = transformedFrame; - } - - FloatRect sourceCrop = computeCrop(display); - error = hwcLayer->setSourceCrop(sourceCrop); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " - "%s (%d)", - mName.string(), sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom, - to_string(error).c_str(), static_cast<int32_t>(error)); - } else { - compositionState.sourceCrop = sourceCrop; - } - - float alpha = static_cast<float>(getAlpha()); - error = hwcLayer->setPlaneAlpha(alpha); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set plane alpha %.3f: " - "%s (%d)", - mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error)); - commonCompositionState.alpha = alpha; - - error = hwcLayer->setZOrder(z); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z, - to_string(error).c_str(), static_cast<int32_t>(error)); - compositionState.z = z; - - int type = s.metadata.getInt32(METADATA_WINDOW_TYPE, 0); - int appId = s.metadata.getInt32(METADATA_OWNER_UID, 0); + int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0); + int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0); sp<Layer> parent = mDrawingParent.promote(); if (parent.get()) { auto& parentState = parent->getDrawingState(); @@ -592,62 +390,35 @@ void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { } } - error = hwcLayer->setInfo(type, appId); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(), - static_cast<int32_t>(error)); + compositionState.geomLayerTransform = getTransform(); + compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse(); + compositionState.geomBufferSize = getBufferSize(drawingState); + compositionState.geomContentCrop = getContentCrop(); + compositionState.geomCrop = getCrop(drawingState); + compositionState.geomBufferTransform = mCurrentTransform; + compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); + compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState); + compositionState.geomLayerBounds = mBounds; + compositionState.geomUsesSourceCrop = usesSourceCrop(); + compositionState.isSecure = isSecure(); - commonCompositionState.type = type; - commonCompositionState.appId = appId; - - /* - * Transformations are applied in this order: - * 1) buffer orientation/flip/mirror - * 2) state transformation (window manager) - * 3) layer orientation (screen orientation) - * (NOTE: the matrices are multiplied in reverse order) - */ - - const ui::Transform bufferOrientation(mCurrentTransform); - ui::Transform transform(tr * t * bufferOrientation); - - if (getTransformToDisplayInverse()) { - /* - * the code below applies the primary display's inverse transform to the - * buffer - */ - uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); - // calculate the inverse transform - if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } + compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); + compositionState.alpha = alpha; + compositionState.type = type; + compositionState.appId = appId; +} - /* - * Here we cancel out the orientation component of the WM transform. - * The scaling and translate components are already included in our bounds - * computation so it's enough to just omit it in the composition. - * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why. - */ - transform = ui::Transform(invTransform) * tr * bufferOrientation; - } - - // this gives us only the "orientation" component of the transform - const uint32_t orientation = transform.getOrientation(); - if (orientation & ui::Transform::ROT_INVALID) { - // we can only handle simple transformation - compositionState.forceClientComposition = true; - (*compositionState.hwc).hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT; - } else { - auto transform = static_cast<HWC2::Transform>(orientation); - auto error = hwcLayer->setTransform(transform); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set transform %s: " - "%s (%d)", - mName.string(), to_string(transform).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - compositionState.bufferTransform = static_cast<Hwc2::Transform>(transform); +void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState, + bool includeGeometry) const { + if (includeGeometry) { + latchGeometry(compositionState); } } +const char* Layer::getDebugName() const { + return mName.string(); +} + void Layer::forceClientComposition(const sp<DisplayDevice>& display) { const auto outputLayer = findOutputLayerForDisplay(display); LOG_FATAL_IF(!outputLayer); @@ -1266,8 +1037,8 @@ bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace da // create background color layer if one does not yet exist uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor; const String8& name = mName + "BackgroundColorLayer"; - mCurrentState.bgColorLayer = - new ColorLayer(LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags)); + mCurrentState.bgColorLayer = new ColorLayer( + LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata())); // add to child list addChild(mCurrentState.bgColorLayer); @@ -2025,8 +1796,24 @@ void Layer::commitChildList() { mDrawingParent = mCurrentParent; } +static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) { + if (weakBinderHandle == nullptr) { + return nullptr; + } + sp<IBinder> binderHandle = weakBinderHandle.promote(); + if (binderHandle == nullptr) { + return nullptr; + } + sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get()); + if (handle == nullptr) { + return nullptr; + } + return handle->owner; +} + void Layer::setInputInfo(const InputWindowInfo& info) { mCurrentState.inputInfo = info; + mCurrentState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle); mCurrentState.modified = true; mCurrentState.inputInfoChanged = true; setTransactionFlags(eTransactionNeeded); @@ -2215,6 +2002,18 @@ InputWindowInfo Layer::fillInputInfo() { // bounds. info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop); info.visible = canReceiveInput(); + + auto cropLayer = mDrawingState.touchableRegionCrop.promote(); + if (info.replaceTouchableRegionWithCrop) { + if (cropLayer == nullptr) { + info.touchableRegion = Region(Rect{mScreenBounds}); + } else { + info.touchableRegion = Region(Rect{cropLayer->mScreenBounds}); + } + } else if (cropLayer != nullptr) { + info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds}); + } + return info; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 79d2238d87..aba3ff2ff3 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -70,6 +70,7 @@ class LayerDebugInfo; namespace compositionengine { class Layer; class OutputLayer; +struct LayerFECompositionState; } namespace impl { @@ -80,8 +81,9 @@ class SurfaceInterceptor; struct LayerCreationArgs { LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags) - : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags) {} + uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata) + : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags), + metadata(std::move(metadata)) {} SurfaceFlinger* flinger; const sp<Client>& client; @@ -89,6 +91,7 @@ struct LayerCreationArgs { uint32_t w; uint32_t h; uint32_t flags; + LayerMetadata metadata; }; class Layer : public virtual compositionengine::LayerFE { @@ -181,6 +184,7 @@ public: bool inputInfoChanged; InputWindowInfo inputInfo; + wp<Layer> touchableRegionCrop; // dataspace is only used by BufferStateLayer and ColorLayer ui::Dataspace dataspace; @@ -411,6 +415,11 @@ public: */ virtual bool isFixedSize() const { return true; } + /* + * usesSourceCrop - true if content should use a source crop + */ + virtual bool usesSourceCrop() const { return false; } + // Most layers aren't created from the main thread, and therefore need to // grab the SF state lock to access HWC, but ContainerLayer does, so we need // to avoid grabbing the lock again to avoid deadlock @@ -447,13 +456,19 @@ public: /* * compositionengine::LayerFE overrides */ + void latchCompositionState(compositionengine::LayerFECompositionState&, + bool includeGeometry) const override; void onLayerDisplayed(const sp<Fence>& releaseFence) override; + const char* getDebugName() const override; +protected: + void latchGeometry(compositionengine::LayerFECompositionState& outState) const; + +public: virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {} virtual bool isHdrY410() const { return false; } - void setGeometry(const sp<const DisplayDevice>& display, uint32_t z); void forceClientComposition(const sp<DisplayDevice>& display); bool getForceClientComposition(const sp<DisplayDevice>& display); virtual void setPerFrameData(const sp<const DisplayDevice>& display, @@ -700,12 +715,6 @@ protected: uint32_t getEffectiveUsage(uint32_t usage) const; - virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const; - // Compute the initial crop as specified by parent layers and the - // SurfaceControl for this layer. Does not include buffer crop from the - // IGraphicBufferProducer client, as that should not affect child clipping. - // Returns in screen space. - Rect computeInitialCrop(const sp<const DisplayDevice>& display) const; /** * Setup rounded corners coordinates of this layer, taking into account the layer bounds and * crop coordinates, transforming them into layer space. diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 427f5defff..ca94e15895 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1758,24 +1758,30 @@ void SurfaceFlinger::calculateWorkingSet() { mGeometryInvalid = false; for (const auto& [token, displayDevice] : mDisplays) { auto display = displayDevice->getCompositionDisplay(); - const auto displayId = display->getId(); - if (!displayId) { - continue; - } - const Vector<sp<Layer>>& currentLayers = displayDevice->getVisibleLayersSortedByZ(); - for (size_t i = 0; i < currentLayers.size(); i++) { - const auto& layer = currentLayers[i]; + uint32_t zOrder = 0; - if (!layer->hasHwcLayer(displayDevice)) { - layer->forceClientComposition(displayDevice); - continue; + for (auto& layer : display->getOutputLayersOrderedByZ()) { + auto& compositionState = layer->editState(); + compositionState.forceClientComposition = false; + if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) { + compositionState.forceClientComposition = true; } - layer->setGeometry(displayDevice, i); - if (mDebugDisableHWC || mDebugRegion) { - layer->forceClientComposition(displayDevice); - } + // The output Z order is set here based on a simple counter. + compositionState.z = zOrder++; + + // Update the display independent composition state. This goes + // to the general composition layer state structure. + // TODO: Do this once per compositionengine::CompositionLayer. + layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd, + true); + + // Recalculate the geometry state of the output layer. + layer->updateCompositionState(true); + + // Write the updated geometry state to the HWC + layer->writeStateToHWC(true); } } } @@ -3994,14 +4000,27 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie String8 uniqueName = getUniqueLayerName(name); + bool primaryDisplayOnly = false; + + // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java + // TODO b/64227542 + if (metadata.has(METADATA_WINDOW_TYPE)) { + int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0); + if (windowType == 441731) { + metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL + primaryDisplayOnly = true; + } + } + switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: - result = createBufferQueueLayer(client, uniqueName, w, h, flags, format, handle, gbp, - &layer); + result = createBufferQueueLayer(client, uniqueName, w, h, flags, std::move(metadata), + format, handle, gbp, &layer); break; case ISurfaceComposerClient::eFXSurfaceBufferState: - result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer); + result = createBufferStateLayer(client, uniqueName, w, h, flags, std::move(metadata), + handle, &layer); break; case ISurfaceComposerClient::eFXSurfaceColor: // check if buffer size is set for color layer. @@ -4011,9 +4030,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return BAD_VALUE; } - result = createColorLayer(client, - uniqueName, w, h, flags, - handle, &layer); + result = createColorLayer(client, uniqueName, w, h, flags, std::move(metadata), handle, + &layer); break; case ISurfaceComposerClient::eFXSurfaceContainer: // check if buffer size is set for container layer. @@ -4022,9 +4040,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie int(w), int(h)); return BAD_VALUE; } - result = createContainerLayer(client, - uniqueName, w, h, flags, - handle, &layer); + result = createContainerLayer(client, uniqueName, w, h, flags, std::move(metadata), + handle, &layer); break; default: result = BAD_VALUE; @@ -4035,18 +4052,10 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return result; } - // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java - // TODO b/64227542 - if (metadata.has(METADATA_WINDOW_TYPE)) { - int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0); - if (windowType == 441731) { - metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL - layer->setPrimaryDisplayOnly(); - } + if (primaryDisplayOnly) { + layer->setPrimaryDisplayOnly(); } - layer->setMetadata(metadata); - bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess(); result = addClientLayer(client, *handle, *gbp, layer, *parent, addToCurrentState); @@ -4089,7 +4098,8 @@ String8 SurfaceFlinger::getUniqueLayerName(const String8& name) status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, - PixelFormat& format, sp<IBinder>* handle, + LayerMetadata metadata, PixelFormat& format, + sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) { // initialize the surfaces @@ -4103,8 +4113,8 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const break; } - sp<BufferQueueLayer> layer = - getFactory().createBufferQueueLayer(LayerCreationArgs(this, client, name, w, h, flags)); + sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); status_t err = layer->setDefaultBufferProperties(w, h, format); if (err == NO_ERROR) { *handle = layer->getHandle(); @@ -4118,30 +4128,31 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) { - sp<BufferStateLayer> layer = - getFactory().createBufferStateLayer(LayerCreationArgs(this, client, name, w, h, flags)); + LayerMetadata metadata, sp<IBinder>* handle, + sp<Layer>* outLayer) { + sp<BufferStateLayer> layer = getFactory().createBufferStateLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = layer->getHandle(); *outLayer = layer; return NO_ERROR; } -status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) -{ - *outLayer = getFactory().createColorLayer(LayerCreationArgs(this, client, name, w, h, flags)); +status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* handle, sp<Layer>* outLayer) { + *outLayer = getFactory().createColorLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = (*outLayer)->getHandle(); return NO_ERROR; } -status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) -{ - *outLayer = - getFactory().createContainerLayer(LayerCreationArgs(this, client, name, w, h, flags)); +status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags, + LayerMetadata metadata, sp<IBinder>* handle, + sp<Layer>* outLayer) { + *outLayer = getFactory().createContainerLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = (*outLayer)->getHandle(); return NO_ERROR; } @@ -4377,6 +4388,14 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, return NO_ERROR; } +status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) { + if (asProto && mTracing.isEnabled()) { + mTracing.writeToFileAsync(); + } + + return doDump(fd, DumpArgs(), asProto); +} + void SurfaceFlinger::listLayersLocked(std::string& result) const { mCurrentState.traverseInZOrder( [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); }); @@ -4699,6 +4718,14 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n"); } + { + StringAppendF(&result, "Composition layers\n"); + mDrawingState.traverseInZOrder([&](Layer* layer) { + auto compositionLayer = layer->getCompositionLayer(); + if (compositionLayer) compositionLayer->dump(result); + }); + } + /* * Dump Display state */ @@ -5378,7 +5405,8 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, Rect bounds = getBounds(); screenshotParentLayer = mFlinger->getFactory().createContainerLayer( LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"), - bounds.getWidth(), bounds.getHeight(), 0)); + bounds.getWidth(), bounds.getHeight(), 0, + LayerMetadata())); ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop); drawLayers(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 118253f067..0d39cb58dd 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -604,21 +604,21 @@ private: sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent); status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags, PixelFormat& format, - sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, - sp<Layer>* outLayer); + uint32_t h, uint32_t flags, LayerMetadata metadata, + PixelFormat& format, sp<IBinder>* outHandle, + sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer); status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* outHandle, sp<Layer>* outLayer); - status_t createColorLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + status_t createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, + uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle, + sp<Layer>* outLayer); - status_t createContainerLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + status_t createContainerLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* outHandle, sp<Layer>* outLayer); String8 getUniqueLayerName(const String8& name); @@ -905,9 +905,7 @@ private: status_t doDump(int fd, const DumpArgs& args, bool asProto); - status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override { - return doDump(fd, DumpArgs(), asProto); - } + status_t dumpCritical(int fd, const DumpArgs&, bool asProto); status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override { return doDump(fd, args, asProto); diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 30ae7647c0..ea2818d532 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -22,6 +22,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/IProducerListener.h> +#include <gui/LayerMetadata.h> #include <log/log.h> #include <renderengine/mock/Framebuffer.h> #include <renderengine/mock/Image.h> @@ -806,7 +807,7 @@ struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> { return new ColorLayer(LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), String8("test-layer"), LayerProperties::WIDTH, LayerProperties::HEIGHT, - LayerProperties::LAYER_FLAGS)); + LayerProperties::LAYER_FLAGS, LayerMetadata())); }); auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); @@ -847,7 +848,7 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), String8("test-layer"), LayerProperties::WIDTH, LayerProperties::HEIGHT, - LayerProperties::LAYER_FLAGS)); + LayerProperties::LAYER_FLAGS, LayerMetadata())); }); LayerProperties::setupLayerState(test, layer); |