SF: Move state out of DisplayDevice to a new Output class
CompositionEngine::Output holds the composition state of an output. A
CompositionEngine::Display is an output, so it derives from it.
The state is removed from DisplayDevice, with some (temporary) accessors
left behind as there are more changes coming.
The composition related code in SurfaceFlinger is adjusted to however
use the output state.
Test: atest libsurfaceflinger_unittest libcompositionengine_test
Bug: 121291683
Change-Id: Idae2d3d96315538d006b27b56e0a8b566ce0e3b8
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 5209204..3862d8b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -36,6 +36,9 @@
srcs: [
"src/CompositionEngine.cpp",
"src/Display.cpp",
+ "src/DumpHelpers.cpp",
+ "src/Output.cpp",
+ "src/OutputCompositionState.cpp",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -47,6 +50,7 @@
srcs: [
"mock/CompositionEngine.cpp",
"mock/Display.cpp",
+ "mock/Output.cpp",
],
static_libs: [
"libgtest",
@@ -64,6 +68,7 @@
srcs: [
"tests/CompositionEngineTest.cpp",
"tests/DisplayTest.cpp",
+ "tests/OutputTest.cpp",
"tests/MockHWComposer.cpp",
],
static_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
index 9965d89..979fdad 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h
@@ -21,13 +21,15 @@
#include "DisplayHardware/DisplayIdentification.h"
+#include <compositionengine/Output.h>
+
namespace android::compositionengine {
/**
* A display is a composition target which may be backed by a hardware composer
* display device
*/
-class Display {
+class Display : public virtual Output {
public:
// Gets the HWC DisplayId for the display if there is one
virtual const std::optional<DisplayId>& getId() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
new file mode 100644
index 0000000..731189f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -0,0 +1,97 @@
+/*
+ * 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 <cstdint>
+#include <string>
+
+#include <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine {
+
+namespace impl {
+struct OutputCompositionState;
+} // namespace impl
+
+/**
+ * Encapsulates all the state involved with composing layers for an output
+ */
+class Output {
+public:
+ // Returns true if the output is valid. This is meant to be checked post-
+ // construction and prior to use, as not everything is set up by the
+ // constructor.
+ virtual bool isValid() const = 0;
+
+ // Enables (or disables) composition on this output
+ virtual void setCompositionEnabled(bool) = 0;
+
+ // Sets the projection state to use
+ virtual void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+ const Rect& viewport, const Rect& scissor, bool needsFiltering) = 0;
+ // Sets the bounds to use
+ virtual void setBounds(const Rect&) = 0;
+
+ // Sets the layer stack filter for this output. If singleLayerStack is true,
+ // this output displays just the single layer stack specified by
+ // singleLayerStackId. Otherwise all layer stacks will be visible on this
+ // output.
+ virtual void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) = 0;
+
+ // Sets the color transform matrix to use
+ virtual void setColorTransform(const mat4&) = 0;
+
+ // Sets the output color mode
+ virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0;
+
+ // Outputs a string with a state dump
+ virtual void dump(std::string&) const = 0;
+
+ // Gets the debug name for the output
+ virtual const std::string& getName() const = 0;
+
+ // Sets a debug name for the output
+ virtual void setName(const std::string&) = 0;
+
+ using OutputCompositionState = compositionengine::impl::OutputCompositionState;
+
+ // Gets the raw composition state data for the output
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual const OutputCompositionState& getState() const = 0;
+
+ // Allows mutable access to the raw composition state data for the output.
+ // This is meant to be used by the various functions that are part of the
+ // composition process.
+ // TODO(lpique): Make this protected once it is only internally called.
+ virtual OutputCompositionState& editState() = 0;
+
+ // Gets the physical space dirty region. If repaintEverything is true, this
+ // will be the full display bounds. Internally the dirty region is stored in
+ // logical (aka layer stack) space.
+ virtual Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const = 0;
+
+ // Tests whether a given layerStackId belongs in this output
+ virtual bool belongsInOutput(uint32_t layerStackId) const = 0;
+
+protected:
+ ~Output() = default;
+};
+
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index ad03054..7c38c49 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -19,6 +19,7 @@
#include <memory>
#include <compositionengine/Display.h>
+#include <compositionengine/impl/Output.h>
#include "DisplayHardware/DisplayIdentification.h"
@@ -30,19 +31,23 @@
namespace impl {
-class Display : public compositionengine::Display {
+class Display : public compositionengine::impl::Output, public compositionengine::Display {
public:
Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&);
virtual ~Display();
+ // compositionengine::Output overrides
+ void dump(std::string&) const override;
+ void setColorTransform(const mat4&) override;
+ void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+ // compositionengine::Display overrides
const std::optional<DisplayId>& getId() const override;
bool isSecure() const override;
bool isVirtual() const override;
void disconnect() override;
private:
- const CompositionEngine& mCompositionEngine;
- const bool mIsSecure;
const bool mIsVirtual;
std::optional<DisplayId> mId;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
new file mode 100644
index 0000000..663010f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DumpHelpers.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <cstdint>
+#include <string>
+#include <type_traits>
+
+#include <math/mat4.h>
+#include <ui/FloatRect.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android::compositionengine::impl {
+
+void dumpVal(std::string& out, const char* name, bool);
+void dumpVal(std::string& out, const char* name, const void*);
+void dumpVal(std::string& out, const char* name, int);
+void dumpVal(std::string& out, const char* name, float);
+void dumpVal(std::string& out, const char* name, uint32_t);
+void dumpHex(std::string& out, const char* name, uint64_t);
+void dumpVal(std::string& out, const char* name, const char* value);
+void dumpVal(std::string& out, const char* name, const std::string& value);
+
+// For enums with named values
+void dumpVal(std::string& out, const char* name, const char*, int);
+void dumpVal(std::string& out, const char* name, const std::string&, int);
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const char* valueName, EnumType value) {
+ dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+template <typename EnumType>
+void dumpVal(std::string& out, const char* name, const std::string& valueName, EnumType value) {
+ dumpVal(out, name, valueName, static_cast<std::underlying_type_t<EnumType>>(value));
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect);
+void dumpVal(std::string& out, const char* name, const Rect& rect);
+void dumpVal(std::string& out, const char* name, const Region& region);
+void dumpVal(std::string& out, const char* name, const ui::Transform&);
+
+void dumpVal(std::string& out, const char* name, const mat4& tr);
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
new file mode 100644
index 0000000..60b94a4
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -0,0 +1,72 @@
+/*
+ * 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 <memory>
+
+#include <compositionengine/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine {
+
+class CompositionEngine;
+
+namespace impl {
+
+class Output : public virtual compositionengine::Output {
+public:
+ Output(const CompositionEngine&);
+ virtual ~Output();
+
+ bool isValid() const override;
+
+ void setCompositionEnabled(bool) override;
+ void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame,
+ const Rect& viewport, const Rect& scissor, bool needsFiltering) override;
+ void setBounds(const Rect&) override;
+ void setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) override;
+
+ void setColorTransform(const mat4&) override;
+ void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override;
+
+ void dump(std::string&) const override;
+
+ const std::string& getName() const override;
+ void setName(const std::string&) override;
+
+ const OutputCompositionState& getState() const override;
+ OutputCompositionState& editState() override;
+
+ Region getPhysicalSpaceDirtyRegion(bool repaintEverything) const override;
+ bool belongsInOutput(uint32_t) const override;
+
+protected:
+ const CompositionEngine& getCompositionEngine() const;
+ void dumpBase(std::string&) const;
+
+private:
+ void dirtyEntireOutput();
+
+ const CompositionEngine& mCompositionEngine;
+
+ std::string mName;
+
+ OutputCompositionState mState;
+};
+
+} // namespace impl
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
new file mode 100644
index 0000000..96a0e0f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -0,0 +1,96 @@
+/*
+ * 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 <cstdint>
+
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+
+namespace compositionengine::impl {
+
+struct OutputCompositionState {
+ // If false, composition will not per performed for this display
+ bool isEnabled{false};
+
+ // If false, this output is not considered secure
+ bool isSecure{false};
+
+ // If true, only layer stacks with a matching layerStack value will be
+ // displayed. If false, all layer stacks will be displayed.
+ bool singleLayerStack{true};
+
+ // The layer stack to display on this display
+ uint32_t singleLayerStackId{~0u};
+
+ // The physical space screen bounds
+ Rect bounds;
+
+ // The logical to physical transformation to use
+ ui::Transform transform;
+
+ // The physical orientation of the display, expressed as ui::Transform
+ // orientation flags.
+ uint32_t orientation{0};
+
+ // The logical space user visible bounds
+ Rect frame;
+
+ // The logical space user viewport rectangle
+ Rect viewport;
+
+ // The physical space scissor rectangle
+ Rect scissor;
+
+ // If true, RenderEngine filtering should be enabled
+ bool needsFiltering{false};
+
+ // The logical coordinates for the dirty region for the display.
+ // dirtyRegion is semi-persistent state. Dirty rectangles are added to it
+ // by the FE until composition happens, at which point it is cleared.
+ Region dirtyRegion;
+
+ // The logical coordinates for the undefined region for the display.
+ // The undefined region is internal to the composition engine. It is
+ // updated every time the geometry changes.
+ Region undefinedRegion;
+
+ // True if the last composition frame had visible layers
+ bool lastCompositionHadVisibleLayers{false};
+
+ // The color transform to apply
+ android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY};
+
+ // Current active color mode
+ ui::ColorMode colorMode{ui::ColorMode::NATIVE};
+
+ // Current active render intent
+ ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC};
+
+ // Current active dstaspace
+ ui::Dataspace dataspace{ui::Dataspace::UNKNOWN};
+
+ // Debugging
+ void dump(std::string& result) const;
+};
+
+} // namespace compositionengine::impl
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
index 2371bec..22eb8f5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h
@@ -16,15 +16,15 @@
#pragma once
-#include <gmock/gmock.h>
-
#include <compositionengine/Display.h>
+#include <compositionengine/mock/Output.h>
+#include <gmock/gmock.h>
#include "DisplayHardware/DisplayIdentification.h"
namespace android::compositionengine::mock {
-class Display : public compositionengine::Display {
+class Display : public compositionengine::mock::Output, public compositionengine::Display {
public:
Display();
virtual ~Display();
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
new file mode 100644
index 0000000..ccdf804
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -0,0 +1,52 @@
+/*
+ * 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 <compositionengine/Output.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+#include <gmock/gmock.h>
+
+namespace android::compositionengine::mock {
+
+class Output : public virtual compositionengine::Output {
+public:
+ Output();
+ virtual ~Output();
+
+ MOCK_CONST_METHOD0(isValid, bool());
+
+ MOCK_METHOD1(setCompositionEnabled, void(bool));
+ MOCK_METHOD6(setProjection,
+ void(const ui::Transform&, int32_t, const Rect&, const Rect&, const Rect&, bool));
+ MOCK_METHOD1(setBounds, void(const Rect&));
+ MOCK_METHOD2(setLayerStackFilter, void(bool, uint32_t));
+
+ MOCK_METHOD1(setColorTransform, void(const mat4&));
+ MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent));
+
+ MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD0(getName, const std::string&());
+ MOCK_METHOD1(setName, void(const std::string&));
+
+ MOCK_CONST_METHOD0(getState, const OutputCompositionState&());
+ MOCK_METHOD0(editState, OutputCompositionState&());
+
+ MOCK_CONST_METHOD1(getPhysicalSpaceDirtyRegion, Region(bool));
+ MOCK_CONST_METHOD1(belongsInOutput, bool(uint32_t));
+};
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/mock/Output.cpp b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
new file mode 100644
index 0000000..44df4c3
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/mock/Output.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#include <compositionengine/mock/Output.h>
+
+namespace android::compositionengine::mock {
+
+// The Google Mock documentation recommends explicit non-header instantiations
+// for better compile time performance.
+Output::Output() = default;
+Output::~Output() = default;
+
+} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index a0b277c..fb783e7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-#include <cinttypes>
-
+#include <android-base/stringprintf.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/DumpHelpers.h>
#include "DisplayHardware/HWComposer.h"
@@ -31,10 +31,11 @@
}
Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args)
- : mCompositionEngine(compositionEngine),
- mIsSecure(args.isSecure),
+ : compositionengine::impl::Output(compositionEngine),
mIsVirtual(args.isVirtual),
- mId(args.displayId) {}
+ mId(args.displayId) {
+ editState().isSecure = args.isSecure;
+}
Display::~Display() = default;
@@ -43,7 +44,7 @@
}
bool Display::isSecure() const {
- return mIsSecure;
+ return getState().isSecure;
}
bool Display::isVirtual() const {
@@ -55,9 +56,55 @@
return;
}
- auto& hwc = mCompositionEngine.getHwComposer();
+ auto& hwc = getCompositionEngine().getHwComposer();
hwc.disconnectDisplay(*mId);
mId.reset();
}
+void Display::setColorTransform(const mat4& transform) {
+ Output::setColorTransform(transform);
+
+ auto& hwc = getCompositionEngine().getHwComposer();
+ status_t result = hwc.setColorTransform(*mId, transform);
+ ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",
+ mId ? to_string(*mId).c_str() : "", result);
+}
+
+void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+ ui::RenderIntent renderIntent) {
+ if (mode == getState().colorMode && dataspace == getState().dataspace &&
+ renderIntent == getState().renderIntent) {
+ return;
+ }
+
+ if (mIsVirtual) {
+ ALOGW("%s: Invalid operation on virtual display", __FUNCTION__);
+ return;
+ }
+
+ Output::setColorMode(mode, dataspace, renderIntent);
+
+ auto& hwc = getCompositionEngine().getHwComposer();
+ hwc.setActiveColorMode(*mId, mode, renderIntent);
+}
+
+void Display::dump(std::string& out) const {
+ using android::base::StringAppendF;
+
+ StringAppendF(&out, " Composition Display State: [\"%s\"]", getName().c_str());
+
+ out.append("\n ");
+
+ dumpVal(out, "isVirtual", mIsVirtual);
+ if (mId) {
+ dumpVal(out, "hwcId", to_string(*mId));
+ } else {
+ StringAppendF(&out, "no hwcId, ");
+ }
+
+ out.append("\n");
+
+ Output::dumpBase(out);
+}
+
} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
new file mode 100644
index 0000000..ba86be7
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include <cinttypes>
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/impl/DumpHelpers.h>
+
+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');
+}
+
+void dumpVal(std::string& out, const char* name, const void* value) {
+ StringAppendF(&out, "%s=%p ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, int value) {
+ StringAppendF(&out, "%s=%d ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, float value) {
+ StringAppendF(&out, "%s=%f ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, uint32_t value) {
+ StringAppendF(&out, "%s=%u ", name, value);
+}
+
+void dumpHex(std::string& out, const char* name, uint64_t value) {
+ StringAppendF(&out, "%s=0x08%" PRIx64 " ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const char* value) {
+ StringAppendF(&out, "%s=%s ", name, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& value) {
+ dumpVal(out, name, value.c_str());
+}
+
+void dumpVal(std::string& out, const char* name, const char* valueName, int value) {
+ StringAppendF(&out, "%s=%s (%d)", name, valueName, value);
+}
+
+void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) {
+ dumpVal(out, name, valueName.c_str(), value);
+}
+
+void dumpVal(std::string& out, const char* name, const FloatRect& rect) {
+ StringAppendF(&out, "%s=[%f %f %f %f] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Rect& rect) {
+ StringAppendF(&out, "%s=[%d %d %d %d] ", name, rect.left, rect.top, rect.right, rect.bottom);
+}
+
+void dumpVal(std::string& out, const char* name, const Region& region) {
+ region.dump(out, name, 0);
+}
+
+void dumpVal(std::string& out, const char* name, const ui::Transform& transform) {
+ transform.dump(out, name);
+}
+
+void dumpVal(std::string& out, const char* name, const mat4& tr) {
+ StringAppendF(&out,
+ "%s=["
+ /* clang-format off */
+ "[%0.3f,%0.3f,%0.3f,%0.3f]"
+ "[%0.3f,%0.3f,%0.3f,%0.3f]"
+ "[%0.3f,%0.3f,%0.3f,%0.3f]"
+ "[%0.3f,%0.3f,%0.3f,%0.3f]]",
+ name,
+ tr[0][0], tr[1][0], tr[2][0], tr[3][0],
+ tr[0][1], tr[1][1], tr[2][1], tr[3][1],
+ tr[0][2], tr[1][2], tr[2][2], tr[3][2],
+ tr[0][3], tr[1][3], tr[2][3], tr[3][3]
+ ); /* clang-format on */
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
new file mode 100644
index 0000000..8b2441f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#include <android-base/stringprintf.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/DebugUtils.h>
+
+namespace android::compositionengine::impl {
+
+Output::Output(const CompositionEngine& compositionEngine)
+ : mCompositionEngine(compositionEngine) {}
+
+Output::~Output() = default;
+
+const CompositionEngine& Output::getCompositionEngine() const {
+ return mCompositionEngine;
+}
+
+bool Output::isValid() const {
+ return true;
+}
+
+const std::string& Output::getName() const {
+ return mName;
+}
+
+void Output::setName(const std::string& name) {
+ mName = name;
+}
+
+void Output::setCompositionEnabled(bool enabled) {
+ if (mState.isEnabled == enabled) {
+ return;
+ }
+
+ mState.isEnabled = enabled;
+ dirtyEntireOutput();
+}
+
+void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame,
+ const Rect& viewport, const Rect& scissor, bool needsFiltering) {
+ mState.transform = transform;
+ mState.orientation = orientation;
+ mState.scissor = scissor;
+ mState.frame = frame;
+ mState.viewport = viewport;
+ mState.needsFiltering = needsFiltering;
+
+ dirtyEntireOutput();
+}
+
+void Output::setBounds(const Rect& bounds) {
+ mState.bounds = bounds;
+
+ dirtyEntireOutput();
+}
+
+void Output::setLayerStackFilter(bool singleLayerStack, uint32_t singleLayerStackId) {
+ mState.singleLayerStack = singleLayerStack;
+ mState.singleLayerStackId = singleLayerStackId;
+
+ dirtyEntireOutput();
+}
+
+void Output::setColorTransform(const mat4& transform) {
+ const bool isIdentity = (transform == mat4());
+
+ mState.colorTransform =
+ isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX;
+}
+
+void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace,
+ ui::RenderIntent renderIntent) {
+ mState.colorMode = mode;
+ mState.dataspace = dataspace;
+ mState.renderIntent = renderIntent;
+
+ ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)",
+ decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(),
+ renderIntent);
+}
+
+void Output::dump(std::string& out) const {
+ using android::base::StringAppendF;
+
+ StringAppendF(&out, " Composition Output State: [\"%s\"]", mName.c_str());
+
+ out.append("\n ");
+
+ dumpBase(out);
+}
+
+void Output::dumpBase(std::string& out) const {
+ mState.dump(out);
+}
+
+const OutputCompositionState& Output::getState() const {
+ return mState;
+}
+
+OutputCompositionState& Output::editState() {
+ return mState;
+}
+
+Region Output::getPhysicalSpaceDirtyRegion(bool repaintEverything) const {
+ Region dirty;
+ if (repaintEverything) {
+ dirty.set(mState.bounds);
+ } else {
+ dirty = mState.transform.transform(mState.dirtyRegion);
+ dirty.andSelf(mState.bounds);
+ }
+ return dirty;
+}
+
+bool Output::belongsInOutput(uint32_t layerStackId) const {
+ return !mState.singleLayerStack || (layerStackId == mState.singleLayerStackId);
+}
+
+void Output::dirtyEntireOutput() {
+ mState.dirtyRegion.set(mState.bounds);
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
new file mode 100644
index 0000000..7d8765f
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include <compositionengine/impl/DumpHelpers.h>
+#include <compositionengine/impl/OutputCompositionState.h>
+
+namespace android::compositionengine::impl {
+
+void OutputCompositionState::dump(std::string& out) const {
+ dumpVal(out, "isEnabled", isEnabled);
+ dumpVal(out, "isSecure", isSecure);
+ if (singleLayerStack) {
+ out.append("layerStack=<any>");
+ } else {
+ dumpVal(out, "layerStack", singleLayerStackId);
+ }
+
+ out.append("\n ");
+
+ dumpVal(out, "transform", transform);
+
+ out.append("\n ");
+
+ dumpVal(out, "frame", frame);
+ dumpVal(out, "viewport", viewport);
+ dumpVal(out, "scissor", scissor);
+ dumpVal(out, "needsFiltering", needsFiltering);
+
+ out.append("\n");
+
+ dumpVal(out, "colorMode", toString(colorMode), colorMode);
+ dumpVal(out, "renderIntent", toString(renderIntent), renderIntent);
+ dumpVal(out, "dataspace", toString(dataspace), dataspace);
+ dumpVal(out, "colorTransform", colorTransform);
+
+ out.append("\n");
+}
+
+} // namespace android::compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 11f9910..a04c2aa 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
-
#include <cmath>
#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/impl/Display.h>
#include <compositionengine/mock/CompositionEngine.h>
+#include <gtest/gtest.h>
#include "MockHWComposer.h"
@@ -101,5 +100,77 @@
EXPECT_FALSE(mDisplay.getId());
}
+/* ------------------------------------------------------------------------
+ * Display::setColorTransform()
+ */
+
+TEST_F(DisplayTest, setColorTransformSetsTransform) {
+ // Identity matrix sets an identity state value
+ const mat4 identity;
+
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+ EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1);
+
+ mDisplay.setColorTransform(identity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform);
+
+ // Non-identity matrix sets a non-identity state value
+ const mat4 nonIdentity = mat4() * 2;
+
+ EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1);
+
+ mDisplay.setColorTransform(nonIdentity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Display::setColorMode()
+ */
+
+TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+ // These values are expected to be the initial state.
+ ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+ ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+ ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+ // Otherwise if the values are unchanged, nothing happens
+ mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent);
+
+ // Otherwise if the values are different, updates happen
+ EXPECT_CALL(mHwComposer,
+ setActiveColorMode(DEFAULT_DISPLAY_ID, ui::ColorMode::BT2100_PQ,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC))
+ .Times(1);
+
+ mDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::BT2100_PQ, mDisplay.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::SRGB, mDisplay.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent);
+}
+
+TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
+ impl::Display virtualDisplay{mCompositionEngine,
+ DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}};
+
+ virtualDisplay.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
new file mode 100644
index 0000000..0cd0d23
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+#include <cmath>
+
+#include <compositionengine/impl/Output.h>
+#include <compositionengine/mock/CompositionEngine.h>
+#include <gtest/gtest.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include "RegionMatcher.h"
+#include "TransformMatcher.h"
+
+namespace android::compositionengine {
+namespace {
+
+using testing::ReturnRef;
+using testing::StrictMock;
+
+class OutputTest : public testing::Test {
+public:
+ ~OutputTest() override = default;
+
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ impl::Output mOutput{mCompositionEngine};
+};
+
+/* ------------------------------------------------------------------------
+ * Basic construction
+ */
+
+TEST_F(OutputTest, canInstantiateOutput) {}
+
+/* ------------------------------------------------------------------------
+ * Output::setCompositionEnabled()
+ */
+
+TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) {
+ const Rect displaySize{100, 200};
+ mOutput.editState().bounds = displaySize;
+ mOutput.editState().isEnabled = true;
+
+ mOutput.setCompositionEnabled(true);
+
+ EXPECT_TRUE(mOutput.getState().isEnabled);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region()));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) {
+ const Rect displaySize{100, 200};
+ mOutput.editState().bounds = displaySize;
+ mOutput.editState().isEnabled = false;
+
+ mOutput.setCompositionEnabled(true);
+
+ EXPECT_TRUE(mOutput.getState().isEnabled);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) {
+ const Rect displaySize{100, 200};
+ mOutput.editState().bounds = displaySize;
+ mOutput.editState().isEnabled = true;
+
+ mOutput.setCompositionEnabled(false);
+
+ EXPECT_FALSE(mOutput.getState().isEnabled);
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setProjection()
+ */
+
+TEST_F(OutputTest, setProjectionTriviallyWorks) {
+ const ui::Transform transform{ui::Transform::ROT_180};
+ const int32_t orientation = 123;
+ const Rect frame{1, 2, 3, 4};
+ const Rect viewport{5, 6, 7, 8};
+ const Rect scissor{9, 10, 11, 12};
+ const bool needsFiltering = true;
+
+ mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
+
+ EXPECT_THAT(mOutput.getState().transform, TransformEq(transform));
+ EXPECT_EQ(orientation, mOutput.getState().orientation);
+ EXPECT_EQ(frame, mOutput.getState().frame);
+ EXPECT_EQ(viewport, mOutput.getState().viewport);
+ EXPECT_EQ(scissor, mOutput.getState().scissor);
+ EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setBounds()
+ */
+
+TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) {
+ const Rect displaySize{100, 200};
+ mOutput.setBounds(displaySize);
+
+ EXPECT_EQ(displaySize, mOutput.getState().bounds);
+
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setLayerStackFilter()
+ */
+
+TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) {
+ const Rect displaySize{100, 200};
+ mOutput.editState().bounds = displaySize;
+
+ const uint32_t layerStack = 123u;
+ mOutput.setLayerStackFilter(true, layerStack);
+
+ EXPECT_TRUE(mOutput.getState().singleLayerStack);
+ EXPECT_EQ(layerStack, mOutput.getState().singleLayerStackId);
+
+ EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(displaySize)));
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorTransform
+ */
+
+TEST_F(OutputTest, setColorTransformSetsTransform) {
+ // Identity matrix sets an identity state value
+ const mat4 identity;
+
+ mOutput.setColorTransform(identity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform);
+
+ // Non-identity matrix sets a non-identity state value
+ const mat4 nonIdentity = mat4() * 2;
+
+ mOutput.setColorTransform(nonIdentity);
+
+ EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::setColorMode
+ */
+
+TEST_F(OutputTest, setColorModeSetsModeUnlessNoChange) {
+ mOutput.setColorMode(ui::ColorMode::BT2100_PQ, ui::Dataspace::SRGB,
+ ui::RenderIntent::TONE_MAP_COLORIMETRIC);
+
+ EXPECT_EQ(ui::ColorMode::BT2100_PQ, mOutput.getState().colorMode);
+ EXPECT_EQ(ui::Dataspace::SRGB, mOutput.getState().dataspace);
+ EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent);
+}
+
+/* ------------------------------------------------------------------------
+ * Output::getPhysicalSpaceDirtyRegion()
+ */
+
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingTrue) {
+ const Rect displaySize{100, 200};
+ mOutput.editState().bounds = displaySize;
+ mOutput.editState().dirtyRegion.set(50, 300);
+
+ {
+ Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
+
+ EXPECT_THAT(result, RegionEq(Region(displaySize)));
+ }
+
+ // For repaint everything == true, the returned value does not depend on the display
+ // rotation.
+ mOutput.editState().transform.set(ui::Transform::ROT_90, 0, 0);
+
+ {
+ Region result = mOutput.getPhysicalSpaceDirtyRegion(true);
+
+ EXPECT_THAT(result, RegionEq(Region(displaySize)));
+ }
+}
+
+TEST_F(OutputTest, getPhysicalSpaceDirtyRegionWithRepaintEverythingFalse) {
+ const Rect displaySize{100, 200};
+ mOutput.editState().bounds = displaySize;
+ mOutput.editState().dirtyRegion.set(50, 300);
+
+ {
+ Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+
+ // The dirtyRegion should be clipped to the display bounds.
+ EXPECT_THAT(result, RegionEq(Region(Rect(50, 200))));
+ }
+
+ mOutput.editState().transform.set(ui::Transform::ROT_90, displaySize.getWidth(),
+ displaySize.getHeight());
+
+ {
+ Region result = mOutput.getPhysicalSpaceDirtyRegion(false);
+
+ // The dirtyRegion should be rotated and clipped to the display bounds.
+ EXPECT_THAT(result, RegionEq(Region(Rect(100, 50))));
+ }
+}
+
+} // namespace
+} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
new file mode 100644
index 0000000..5a4efa9
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <gmock/gmock.h>
+
+namespace {
+
+// Checks for a region match
+MATCHER_P(RegionEq, expected, "") {
+ std::string buf;
+ buf.append("Regions are not equal\n");
+ expected.dump(buf, "expected region");
+ arg.dump(buf, "actual region");
+ *result_listener << buf;
+
+ size_t expectedRectCount = 0;
+ android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
+ size_t actualRectCount = 0;
+ android::Rect const* actualRects = arg.getArray(&actualRectCount);
+
+ if (expectedRectCount != actualRectCount) return false;
+ for (size_t i = 0; i < expectedRectCount; i++) {
+ if (expectedRects[i] != actualRects[i]) return false;
+ }
+ return true;
+}
+
+} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
new file mode 100644
index 0000000..ea07bed
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <gmock/gmock.h>
+
+namespace {
+
+// Check for a transform match
+MATCHER_P(TransformEq, expected, "") {
+ std::string buf;
+ buf.append("Transforms are not equal\n");
+ expected.dump(buf, "expected transform");
+ arg.dump(buf, "actual transform");
+ *result_listener << buf;
+
+ const float TOLERANCE = 1e-3f;
+
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (std::fabs(expected[i][j] - arg[i][j]) > TOLERANCE) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace