Merge "Check whether channel has been removed before sending cancelation."
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index abdf168..5597bcd 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -364,7 +364,7 @@
}
if (err != OK) {
- aerr << "Error dumping service info status_t: (" << err << ") "
+ aerr << "Error dumping service info status_t: " << statusToString(err) << " "
<< serviceName << endl;
}
});
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 95957a0..f5a53f1 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -2595,6 +2595,8 @@
std::getline(in, ignored);
if (android::base::GetBoolProperty(kFuseProp, false)) {
+ // TODO(b/146139106): Use sdcardfs mounts on devices running sdcardfs so we don't bypass
+ // it's VFS cache
if (target.compare(0, 17, "/mnt/pass_through") == 0) {
LOG(DEBUG) << "Found storage mount " << source << " at " << target;
mStorageMounts[source] = target;
@@ -2616,6 +2618,17 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ // TODO(b/146139106): This is only safe on devices not running sdcardfs where there is no
+ // risk of bypassing the sdcardfs VFS cache
+
+ // Always use the lower filesystem path on FUSE enabled devices not running sdcardfs
+ // The upper filesystem path, /mnt/pass_through/<userid>/<vol>/ which was a bind mount
+ // to the lower filesytem may have been unmounted already when a user is
+ // removed and the path will now be pointing to a tmpfs without content
+ return StringPrintf("%s/%u", path.c_str(), userid);
+ }
+
auto resolved = mStorageMounts[path];
if (resolved.empty()) {
LOG(WARNING) << "Failed to find storage mount for " << path;
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index eaa562b..b327d76 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -405,13 +405,12 @@
{ nullptr, 0 }
};
-static const InputEventLabel FLAGS[] = {
- DEFINE_FLAG(VIRTUAL),
- DEFINE_FLAG(FUNCTION),
- DEFINE_FLAG(GESTURE),
+static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
+ DEFINE_FLAG(FUNCTION),
+ DEFINE_FLAG(GESTURE),
+ DEFINE_FLAG(WAKE),
- { nullptr, 0 }
-};
+ {nullptr, 0}};
static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
while (list->literal) {
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 916af69..f852cd0 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -63,7 +63,6 @@
FLAG_DISMISS_KEYGUARD = 0x00400000,
FLAG_SPLIT_TOUCH = 0x00800000,
FLAG_SLIPPERY = 0x20000000,
- FLAG_NEEDS_MENU_KEY = 0x40000000,
};
// Window types from WindowManager.LayoutParams
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 0330dac..fd91b15 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -25,15 +25,19 @@
using android::hardware::hidl_vec;
-using ::aidl::android::hardware::graphics::common::BlendMode;
-using ::aidl::android::hardware::graphics::common::Dataspace;
-using ::aidl::android::hardware::graphics::common::PlaneLayout;
-using ::aidl::android::hardware::graphics::common::PlaneLayoutComponent;
-using ::aidl::android::hardware::graphics::common::ExtendableType;
-using ::aidl::android::hardware::graphics::common::Rect;
-using ::aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
-using MetadataType = ::android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
namespace android {
@@ -545,12 +549,9 @@
/**
* Public API functions
*/
-bool isStandardMetadataType(const MetadataType& metadataType) {
- return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
-}
-
-StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
- return static_cast<StandardMetadataType>(metadataType.value);
+PlaneLayoutComponentType getStandardPlaneLayoutComponentTypeValue(
+ const ExtendableType& planeLayoutComponentType) {
+ return static_cast<PlaneLayoutComponentType>(planeLayoutComponentType.value);
}
status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
@@ -691,6 +692,119 @@
return decode(blendMode, reinterpret_cast<int32_t*>(outBlendMode), decodeInteger);
}
+bool isStandardMetadataType(const MetadataType& metadataType) {
+ return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE,
+ metadataType.name.size());
+}
+
+bool isStandardCompression(const ExtendableType& compression) {
+ return !std::strncmp(compression.name.c_str(), GRALLOC4_STANDARD_COMPRESSION,
+ compression.name.size());
+}
+
+bool isStandardInterlaced(const ExtendableType& interlaced) {
+ return !std::strncmp(interlaced.name.c_str(), GRALLOC4_STANDARD_INTERLACED,
+ interlaced.name.size());
+}
+
+bool isStandardChromaSiting(const ExtendableType& chromaSiting) {
+ return !std::strncmp(chromaSiting.name.c_str(), GRALLOC4_STANDARD_CHROMA_SITING,
+ chromaSiting.name.size());
+}
+
+bool isStandardPlaneLayoutComponentType(const ExtendableType& planeLayoutComponentType) {
+ return !std::strncmp(planeLayoutComponentType.name.c_str(), GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ planeLayoutComponentType.name.size());
+}
+
+StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
+ return static_cast<StandardMetadataType>(metadataType.value);
+}
+
+Compression getStandardCompressionValue(const ExtendableType& compression) {
+ return static_cast<Compression>(compression.value);
+}
+
+Interlaced getStandardInterlacedValue(const ExtendableType& interlaced) {
+ return static_cast<Interlaced>(interlaced.value);
+}
+
+ChromaSiting getStandardChromaSitingValue(const ExtendableType& chromaSiting) {
+ return static_cast<ChromaSiting>(chromaSiting.value);
+}
+
+std::string getCompressionName(const ExtendableType& compression) {
+ if (!isStandardCompression(compression)) {
+ std::ostringstream stream;
+ stream << compression.name << "#" << compression.value;
+ return stream.str();
+ }
+ switch (getStandardCompressionValue(compression)) {
+ case Compression::NONE:
+ return "None";
+ case Compression::DISPLAY_STREAM_COMPRESSION:
+ return "DisplayStreamCompression";
+ }
+}
+
+std::string getInterlacedName(const ExtendableType& interlaced) {
+ if (!isStandardInterlaced(interlaced)) {
+ std::ostringstream stream;
+ stream << interlaced.name << "#" << interlaced.value;
+ return stream.str();
+ }
+ switch (getStandardInterlacedValue(interlaced)) {
+ case Interlaced::NONE:
+ return "None";
+ case Interlaced::TOP_BOTTOM:
+ return "TopBottom";
+ case Interlaced::RIGHT_LEFT:
+ return "RightLeft";
+ }
+}
+
+std::string getChromaSitingName(const ExtendableType& chromaSiting) {
+ if (!isStandardChromaSiting(chromaSiting)) {
+ std::ostringstream stream;
+ stream << chromaSiting.name << "#" << chromaSiting.value;
+ return stream.str();
+ }
+ switch (getStandardChromaSitingValue(chromaSiting)) {
+ case ChromaSiting::NONE:
+ return "None";
+ case ChromaSiting::UNKNOWN:
+ return "Unknown";
+ case ChromaSiting::SITED_INTERSTITIAL:
+ return "SitedInterstitial";
+ case ChromaSiting::COSITED_HORIZONTAL:
+ return "CositedHorizontal";
+ }
+}
+
+std::string getPlaneLayoutComponentTypeName(const ExtendableType& planeLayoutComponentType) {
+ if (!isStandardPlaneLayoutComponentType(planeLayoutComponentType)) {
+ std::ostringstream stream;
+ stream << planeLayoutComponentType.name << "#" << planeLayoutComponentType.value;
+ return stream.str();
+ }
+ switch (getStandardPlaneLayoutComponentTypeValue(planeLayoutComponentType)) {
+ case PlaneLayoutComponentType::Y:
+ return "Y";
+ case PlaneLayoutComponentType::CB:
+ return "Cb";
+ case PlaneLayoutComponentType::CR:
+ return "Cr";
+ case PlaneLayoutComponentType::R:
+ return "R";
+ case PlaneLayoutComponentType::G:
+ return "G";
+ case PlaneLayoutComponentType::B:
+ return "B";
+ case PlaneLayoutComponentType::A:
+ return "A";
+ }
+}
+
} // namespace gralloc4
} // namespace android
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 80588cd..a5d3bb2 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -34,10 +34,11 @@
namespace gralloc4 {
#define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
-#define GRALLOC4_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
-#define GRALLOC4_COMPRESSION "android.hardware.graphics.common.Compression"
-#define GRALLOC4_INTERLACED "android.hardware.graphics.common.Interlaced"
-#define GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE "android.hardware.graphics.common.PlaneLayoutComponentType"
+#define GRALLOC4_STANDARD_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
+#define GRALLOC4_STANDARD_COMPRESSION "android.hardware.graphics.common.Compression"
+#define GRALLOC4_STANDARD_INTERLACED "android.hardware.graphics.common.Interlaced"
+#define GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE \
+ "android.hardware.graphics.common.PlaneLayoutComponentType"
/*---------------------------------------------------------------------------------------------*/
/**
@@ -118,13 +119,15 @@
* Definitions of the standard compression strategies. It is recommended that everyone uses
* these definitions directly for standard compression strategies.
*/
-static const aidl::android::hardware::graphics::common::ExtendableType Compression_None = {
- GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_None =
+ {GRALLOC4_STANDARD_COMPRESSION,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)};
-static const aidl::android::hardware::graphics::common::ExtendableType Compression_DisplayStreamCompression = {
- GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::DISPLAY_STREAM_COMPRESSION)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+ Compression_DisplayStreamCompression =
+ {GRALLOC4_STANDARD_COMPRESSION,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::
+ DISPLAY_STREAM_COMPRESSION)};
/*---------------------------------------------------------------------------------------------*/
@@ -132,17 +135,17 @@
* Definitions of the standard interlaced strategies. It is recommended that everyone uses
* these definitions directly for standard interlaced strategies.
*/
-static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None = {
- GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None =
+ {GRALLOC4_STANDARD_INTERLACED,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)};
-static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom = {
- GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom =
+ {GRALLOC4_STANDARD_INTERLACED,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)};
-static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft = {
- GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft =
+ {GRALLOC4_STANDARD_INTERLACED,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)};
/*---------------------------------------------------------------------------------------------*/
@@ -150,21 +153,25 @@
* Definitions of the standard chroma siting. It is recommended that everyone uses
* these definitions directly for standard chroma siting.
*/
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None = {
- GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None =
+ {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)};
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown = {
- GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown =
+ {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)};
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_SitedInterstitial = {
- GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::SITED_INTERSTITIAL)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+ ChromaSiting_SitedInterstitial = {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ ChromaSiting::SITED_INTERSTITIAL)};
-static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_CositedHorizontal = {
- GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::COSITED_HORIZONTAL)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+ ChromaSiting_CositedHorizontal = {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ ChromaSiting::COSITED_HORIZONTAL)};
/*---------------------------------------------------------------------------------------------*/
@@ -172,44 +179,44 @@
* Definitions of the standard plane layout component types. It is recommended that everyone uses
* these definitions directly for standard plane layout component types
*/
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y = {
- GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)};
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB = {
- GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)};
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR = {
- GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)};
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R = {
- GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)};
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G = {
- GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)};
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B = {
- GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)};
-static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A = {
- GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A =
+ {GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)};
/*---------------------------------------------------------------------------------------------*/
/**
- * The functions below can be used to parse a StandardMetadataType.
- */
-bool isStandardMetadataType(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
-
-aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
-
-/**
* The functions below encode and decode standard metadata into a byte stream. It is STRONGLY
* recommended that both the vendor and system partitions use these functions when getting
* and setting metadata through gralloc 4 (IMapper 4.0).
@@ -265,6 +272,44 @@
status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
+/**
+ * The functions below can be used to parse extendable types.
+ */
+bool isStandardMetadataType(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+bool isStandardCompression(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+bool isStandardInterlaced(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+bool isStandardChromaSiting(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+bool isStandardPlaneLayoutComponentType(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+aidl::android::hardware::graphics::common::Compression getStandardCompressionValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+aidl::android::hardware::graphics::common::Interlaced getStandardInterlacedValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+aidl::android::hardware::graphics::common::ChromaSiting getStandardChromaSitingValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+aidl::android::hardware::graphics::common::PlaneLayoutComponentType
+getStandardPlaneLayoutComponentTypeValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+/**
+ * The functions below return string representations of ExtendableTypes
+ */
+std::string getCompressionName(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+std::string getInterlacedName(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+std::string getChromaSitingName(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+std::string getPlaneLayoutComponentTypeName(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
} // namespace gralloc4
} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 546757b..ec5f374 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -525,6 +525,88 @@
return static_cast<status_t>(reply.readInt32());
}
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeInterfaceToken: %d", result);
+ return;
+ }
+
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to transact: %d", result);
+ return;
+ }
+ }
+
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeInterfaceToken: %d", result);
+ return;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_GAME_CONTENT_TYPE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to transact: %d", result);
+ }
+ }
+
virtual status_t clearAnimationFrameStats() {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -1407,6 +1489,75 @@
result = reply->writeInt32(result);
return result;
}
+
+ case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getAutoLowLatencyModeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_AUTO_LOW_LATENCY_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setAllm = false;
+ result = data.readBool(&setAllm);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readBool: %d", result);
+ return result;
+ }
+ setAutoLowLatencyMode(display, setAllm);
+ return result;
+ }
+
+ case GET_GAME_CONTENT_TYPE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getGameContentTypeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_GAME_CONTENT_TYPE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setGameContentTypeOn = false;
+ result = data.readBool(&setGameContentTypeOn);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readBool: %d", result);
+ return result;
+ }
+ setGameContentType(display, setGameContentTypeOn);
+ return result;
+ }
+
case CLEAR_ANIMATION_FRAME_STATS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
status_t result = clearAnimationFrameStats();
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e392bc5..e033f93 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -109,7 +109,7 @@
return err;
}
}
-
+ output.writeFloat(shadowRadius);
return NO_ERROR;
}
@@ -187,6 +187,7 @@
input.readInt64Vector(&callbackIds);
listeners.emplace_back(listener, callbackIds);
}
+ shadowRadius = input.readFloat();
return NO_ERROR;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9d7d7d0..524d7a9 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1656,6 +1656,26 @@
return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
}
+bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
+}
+
+bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setGameContentType(display, on);
+}
+
void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
int mode) {
ComposerService::getComposerService()->setPowerMode(token, mode);
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index abe1e3f..25ce1ca 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -286,36 +286,6 @@
virtual status_t attachBuffer(int* outSlot,
const sp<GraphicBuffer>& buffer) = 0;
- // queueBuffer indicates that the client has finished filling in the
- // contents of the buffer associated with slot and transfers ownership of
- // that slot back to the server.
- //
- // It is not valid to call queueBuffer on a slot that is not owned
- // by the client or one for which a buffer associated via requestBuffer
- // (an attempt to do so will fail with a return value of BAD_VALUE).
- //
- // In addition, the input must be described by the client (as documented
- // below). Any other properties (zero point, etc)
- // are client-dependent, and should be documented by the client.
- //
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
- //
- // Upon success, the output will be filled with meaningful values
- // (refer to the documentation below).
- //
- // Return of a value other than NO_ERROR means an error has occurred:
- // * NO_INIT - the buffer queue has been abandoned or the producer is not
- // connected.
- // * BAD_VALUE - one of the below conditions occurred:
- // * fence was NULL
- // * scaling mode was unknown
- // * both in async mode and buffer count was less than the
- // max numbers of buffers that can be allocated at once
- // * slot index was out of range (see above).
- // * the slot was not in the dequeued state
- // * the slot was enqueued without requesting a buffer
- // * crop rect is out of bounds of the buffer dimensions
-
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
friend class Flattenable<QueueBufferInput>;
explicit inline QueueBufferInput(const Parcel& parcel);
@@ -415,6 +385,35 @@
int maxBufferCount{0};
};
+ // queueBuffer indicates that the client has finished filling in the
+ // contents of the buffer associated with slot and transfers ownership of
+ // that slot back to the server.
+ //
+ // It is not valid to call queueBuffer on a slot that is not owned
+ // by the client or one for which a buffer associated via requestBuffer
+ // (an attempt to do so will fail with a return value of BAD_VALUE).
+ //
+ // In addition, the input must be described by the client (as documented
+ // below). Any other properties (zero point, etc)
+ // are client-dependent, and should be documented by the client.
+ //
+ // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ //
+ // Upon success, the output will be filled with meaningful values
+ // (refer to the documentation below).
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned or the producer is not
+ // connected.
+ // * BAD_VALUE - one of the below conditions occurred:
+ // * fence was NULL
+ // * scaling mode was unknown
+ // * both in async mode and buffer count was less than the
+ // max numbers of buffers that can be allocated at once
+ // * slot index was out of range (see above).
+ // * the slot was not in the dequeued state
+ // * the slot was enqueued without requesting a buffer
+ // * crop rect is out of bounds of the buffer dimensions
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
QueueBufferOutput* output) = 0;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 345425d..e539fcb 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -195,6 +195,37 @@
ui::ColorMode colorMode) = 0;
/**
+ * Returns true if the connected display reports support for HDMI 2.1 Auto
+ * Low Latency Mode.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * Switches Auto Low Latency Mode on/off on the connected display, if it is
+ * available. This should only be called if #getAutoLowLatencyMode returns
+ * true.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
+
+ /**
+ * Returns true if the connected display reports support for Game Content Type.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * This will start sending infoframes to the connected display with
+ * ContentType=Game (if on=true). This will switch the disply to Game mode.
+ * This should only be called if #getGameContentTypeSupport returns true.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
+
+ /**
* Capture the specified screen. This requires READ_FRAME_BUFFER
* permission. This function will fail if there is a secure window on
* screen.
@@ -532,6 +563,10 @@
CAPTURE_SCREEN_BY_ID,
NOTIFY_POWER_HINT,
SET_GLOBAL_SHADOW_SETTINGS,
+ GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+ SET_AUTO_LOW_LATENCY_MODE,
+ GET_GAME_CONTENT_TYPE_SUPPORT,
+ SET_GAME_CONTENT_TYPE,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2c0b143..b5ca029 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -160,6 +160,21 @@
static status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode);
+ // Reports whether the connected display supports Auto Low Latency Mode
+ static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
+
+ // Switches on/off Auto Low Latency Mode on the connected display. This should only be
+ // called if the connected display supports Auto Low Latency Mode as reported by
+ // #getAutoLowLatencyModeSupport
+ static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
+
+ // Reports whether the connected display supports Game content type
+ static bool getGameContentTypeSupport(const sp<IBinder>& display);
+
+ // Turns Game mode on/off on the connected display. This should only be called
+ // if the display supports Game content type, as reported by #getGameContentTypeSupport
+ static void setGameContentType(const sp<IBinder>& display, bool on);
+
/* Triggers screen on/off or low power mode and waits for it to complete */
static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3d90369..1730ec3 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -750,6 +750,16 @@
bool /*captureSecureLayers*/) override {
return NO_ERROR;
}
+ status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
+ status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
sp<GraphicBuffer>* /*outBuffer*/) override {
return NO_ERROR;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 136ad0d..348377e 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,6 +52,8 @@
"gl/GLExtensions.cpp",
"gl/GLFramebuffer.cpp",
"gl/GLImage.cpp",
+ "gl/GLShadowVertexGenerator.cpp",
+ "gl/GLSkiaShadowPort.cpp",
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
index f5387f2..ed2f45f 100644
--- a/libs/renderengine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -21,38 +21,46 @@
namespace android {
namespace renderengine {
-Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize)
+Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize,
+ size_t indexCount)
: mVertexCount(vertexCount),
mVertexSize(vertexSize),
mTexCoordsSize(texCoordSize),
- mPrimitive(primitive) {
+ mCropCoordsSize(cropCoordsSize),
+ mShadowColorSize(shadowColorSize),
+ mShadowParamsSize(shadowParamsSize),
+ mPrimitive(primitive),
+ mIndexCount(indexCount) {
if (vertexCount == 0) {
mVertices.resize(1);
mVertices[0] = 0.0f;
mStride = 0;
return;
}
-
- const size_t CROP_COORD_SIZE = 2;
- size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
+ size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize;
size_t remainder = (stride * vertexCount) / vertexCount;
// Since all of the input parameters are unsigned, if stride is less than
// either vertexSize or texCoordSize, it must have overflowed. remainder
// will be equal to stride as long as stride * vertexCount doesn't overflow.
if ((stride < vertexSize) || (remainder != stride)) {
- ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize,
- CROP_COORD_SIZE);
+ ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize,
+ texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize);
mVertices.resize(1);
mVertices[0] = 0.0f;
mVertexCount = 0;
mVertexSize = 0;
mTexCoordsSize = 0;
+ mCropCoordsSize = 0;
+ mShadowColorSize = 0;
+ mShadowParamsSize = 0;
mStride = 0;
return;
}
mVertices.resize(stride * vertexCount);
mStride = stride;
+ mIndices.resize(indexCount);
}
Mesh::Primitive Mesh::getPrimitive() const {
@@ -80,6 +88,28 @@
return mVertices.data() + mVertexSize + mTexCoordsSize;
}
+float const* Mesh::getShadowColor() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+float* Mesh::getShadowColor() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+
+float const* Mesh::getShadowParams() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+float* Mesh::getShadowParams() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+
+uint16_t const* Mesh::getIndices() const {
+ return mIndices.data();
+}
+
+uint16_t* Mesh::getIndices() {
+ return mIndices.data();
+}
+
size_t Mesh::getVertexCount() const {
return mVertexCount;
}
@@ -92,6 +122,14 @@
return mTexCoordsSize;
}
+size_t Mesh::getShadowColorSize() const {
+ return mShadowColorSize;
+}
+
+size_t Mesh::getShadowParamsSize() const {
+ return mShadowParamsSize;
+}
+
size_t Mesh::getByteStride() const {
return mStride * sizeof(float);
}
@@ -100,5 +138,9 @@
return mStride;
}
+size_t Mesh::getIndexCount() const {
+ return mIndexCount;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 394d05a..0748dfb 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -46,6 +46,7 @@
#include "GLExtensions.h"
#include "GLFramebuffer.h"
#include "GLImage.h"
+#include "GLShadowVertexGenerator.h"
#include "Program.h"
#include "ProgramCache.h"
@@ -566,7 +567,10 @@
float alpha) {
size_t c;
Rect const* r = region.getArray(&c);
- Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(c * 6 /* count */, 2 /* size */)
+ .build();
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
for (size_t i = 0; i < c; i++, r++) {
position[i * 6 + 0].x = r->left;
@@ -981,7 +985,12 @@
fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
}
- Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLE_FAN)
+ .setVertices(4 /* count */, 2 /* size */)
+ .setTexCoords(2 /* size */)
+ .setCropCoords(2 /* size */)
+ .build();
for (auto layer : layers) {
mState.maxMasteringLuminance = layer.source.buffer.maxMasteringLuminance;
mState.maxContentLuminance = layer.source.buffer.maxContentLuminance;
@@ -1037,10 +1046,14 @@
}
setSourceDataSpace(layer.sourceDataspace);
+ if (layer.shadow.length > 0.0f) {
+ handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
+ layer.shadow);
+ }
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
handleRoundedCorners(display, layer, mesh);
} else {
drawMesh(mesh);
@@ -1178,13 +1191,23 @@
mesh.getByteStride(), mesh.getCropCoords());
}
+ if (mState.drawShadows) {
+ glEnableVertexAttribArray(Program::shadowColor);
+ glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowColor());
+
+ glEnableVertexAttribArray(Program::shadowParams);
+ glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowParams());
+ }
+
+ Description managedState = mState;
// By default, DISPLAY_P3 is the only supported wide color output. However,
// when HDR content is present, hardware composer may be able to handle
// BT2020 data space, in that case, the output data space is set to be
// BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
// to respect this and convert non-HDR content to HDR format.
if (mUseColorManagement) {
- Description managedState = mState;
Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
Dataspace outputStandard =
@@ -1275,27 +1298,25 @@
managedState.outputTransferFunction =
Description::dataSpaceToTransferFunction(outputTransfer);
}
+ }
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
- : mEGLContext,
- managedState);
+ ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+ managedState);
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
- if (outputDebugPPMs) {
- static uint64_t managedColorFrameCount = 0;
- std::ostringstream out;
- out << "/data/texture_out" << managedColorFrameCount++;
- writePPM(out.str().c_str(), mVpWidth, mVpHeight);
- }
+ if (mState.drawShadows) {
+ glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
+ mesh.getIndices());
} else {
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
- : mEGLContext,
- mState);
-
glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
}
+ if (mUseColorManagement && outputDebugPPMs) {
+ static uint64_t managedColorFrameCount = 0;
+ std::ostringstream out;
+ out << "/data/texture_out" << managedColorFrameCount++;
+ writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+ }
+
if (mesh.getTexCoordsSize()) {
glDisableVertexAttribArray(Program::texCoords);
}
@@ -1303,6 +1324,11 @@
if (mState.cornerRadius > 0.0f) {
glDisableVertexAttribArray(Program::cropCoords);
}
+
+ if (mState.drawShadows) {
+ glDisableVertexAttribArray(Program::shadowColor);
+ glDisableVertexAttribArray(Program::shadowParams);
+ }
}
size_t GLESRenderEngine::getMaxTextureSize() const {
@@ -1537,6 +1563,35 @@
}
}
+void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& settings) {
+ ATRACE_CALL();
+ const float casterZ = settings.length / 2.0f;
+ const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
+ settings.casterIsTranslucent, settings.ambientColor,
+ settings.spotColor, settings.lightPos,
+ settings.lightRadius);
+
+ // setup mesh for both shadows
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(shadows.getVertexCount(), 2 /* size */)
+ .setShadowAttrs()
+ .setIndices(shadows.getIndexCount())
+ .build();
+
+ Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
+ Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
+ Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
+ shadows.fillVertices(position, shadowColor, shadowParams);
+ shadows.fillIndices(mesh.getIndicesArray());
+
+ mState.cornerRadius = 0.0f;
+ mState.drawShadows = true;
+ drawMesh(mesh);
+ mState.drawShadows = false;
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 397bc16..f41eda2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -148,6 +148,8 @@
bool waitFence(base::unique_fd fenceFd);
void clearWithColor(float red, float green, float blue, float alpha);
void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
+ void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& shadowSettings);
void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
const half4& color, float cornerRadius);
void setupLayerTexturing(const Texture& texture);
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
new file mode 100644
index 0000000..3181f9b
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.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 <renderengine/Mesh.h>
+
+#include <math/vec4.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include "GLShadowVertexGenerator.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition,
+ float lightRadius) {
+ mDrawAmbientShadow = ambientColor.a > 0.f;
+ mDrawSpotShadow = spotColor.a > 0.f;
+
+ // Generate geometries and find number of vertices to generate
+ if (mDrawAmbientShadow) {
+ mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
+ casterIsTranslucent, ambientColor);
+ mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
+ mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
+ } else {
+ mAmbientShadowVertexCount = 0;
+ mAmbientShadowIndexCount = 0;
+ }
+
+ if (mDrawSpotShadow) {
+ mSpotShadowGeometry =
+ getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
+ spotColor, lightPosition, lightRadius);
+ mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
+ mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
+ } else {
+ mSpotShadowVertexCount = 0;
+ mSpotShadowIndexCount = 0;
+ }
+}
+
+size_t GLShadowVertexGenerator::getVertexCount() const {
+ return mAmbientShadowVertexCount + mSpotShadowVertexCount;
+}
+
+size_t GLShadowVertexGenerator::getIndexCount() const {
+ return mAmbientShadowIndexCount + mSpotShadowIndexCount;
+}
+
+void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const {
+ if (mDrawAmbientShadow) {
+ fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
+ color, params);
+ }
+ if (mDrawSpotShadow) {
+ fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
+ Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
+ }
+}
+
+void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
+ if (mDrawAmbientShadow) {
+ fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
+ 0 /* starting vertex offset */, indices);
+ }
+ if (mDrawSpotShadow) {
+ fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
+ mAmbientShadowVertexCount /* starting vertex offset */,
+ &(indices[mAmbientShadowIndexCount]));
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h
new file mode 100644
index 0000000..112f976
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <math/vec4.h>
+#include <ui/Rect.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * Generates gl attributes required to draw shadow spot and/or ambient shadows.
+ *
+ * Each shadow can support different colors. This class generates three vertex attributes for
+ * each shadow, its position, color and shadow params(offset and distance). These can be sent
+ * using a single glDrawElements call.
+ */
+class GLShadowVertexGenerator {
+public:
+ GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition, float lightRadius);
+ ~GLShadowVertexGenerator() = default;
+
+ size_t getVertexCount() const;
+ size_t getIndexCount() const;
+ void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const;
+ void fillIndices(uint16_t* indices) const;
+
+private:
+ bool mDrawAmbientShadow;
+ std::unique_ptr<Geometry> mAmbientShadowGeometry;
+ int mAmbientShadowVertexCount = 0;
+ int mAmbientShadowIndexCount = 0;
+
+ bool mDrawSpotShadow;
+ std::unique_ptr<Geometry> mSpotShadowGeometry;
+ int mSpotShadowVertexCount = 0;
+ int mSpotShadowIndexCount = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
new file mode 100644
index 0000000..224ce6c
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.cpp
@@ -0,0 +1,649 @@
+/*
+ * 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 <math/vec4.h>
+
+#include <renderengine/Mesh.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include <utils/Log.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+static inline float divide_and_pin(float numer, float denom, float min, float max) {
+ if (denom == 0.0f) return min;
+ return std::clamp(numer / denom, min, max);
+}
+
+static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
+static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
+static constexpr auto kAmbientGeomFactor = 64.0f;
+// Assuming that we have a light height of 600 for the spot shadow,
+// the spot values will reach their maximum at a height of approximately 292.3077.
+// We'll round up to 300 to keep it simple.
+static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
+
+inline float AmbientBlurRadius(float height) {
+ return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
+}
+inline float AmbientRecipAlpha(float height) {
+ return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Data
+//
+// We have two possible cases for geometry for a circle:
+
+// In the case of a normal fill, we draw geometry for the circle as an octagon.
+static const uint16_t gFillCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 8, 1, 2, 8,
+ 2, 3, 8, 3, 4, 8,
+ 4, 5, 8, 5, 6, 8,
+ 6, 7, 8, 7, 0, 8,
+ // clang-format on
+};
+
+// For stroked circles, we use two nested octagons.
+static const uint16_t gStrokeCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 9, 0, 9, 8,
+ 1, 2, 10, 1, 10, 9,
+ 2, 3, 11, 2, 11, 10,
+ 3, 4, 12, 3, 12, 11,
+ 4, 5, 13, 4, 13, 12,
+ 5, 6, 14, 5, 14, 13,
+ 6, 7, 15, 6, 15, 14,
+ 7, 0, 8, 7, 8, 15,
+ // clang-format on
+};
+
+#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
+static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
+static const int kVertsPerStrokeCircle = 16;
+static const int kVertsPerFillCircle = 9;
+
+static int circle_type_to_vert_count(bool stroked) {
+ return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
+}
+
+static int circle_type_to_index_count(bool stroked) {
+ return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
+}
+
+static const uint16_t* circle_type_to_indices(bool stroked) {
+ return stroked ? gStrokeCircleIndices : gFillCircleIndices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RoundRect Data
+//
+// The geometry for a shadow roundrect is similar to a 9-patch:
+// ____________
+// |_|________|_|
+// | | | |
+// | | | |
+// | | | |
+// |_|________|_|
+// |_|________|_|
+//
+// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
+// shows the upper part of the upper left corner. The bottom triangle would similarly be split
+// into two triangles.)
+// ________
+// |\ \ |
+// | \ \ |
+// | \\ |
+// | \|
+// --------
+//
+// The center of the fan handles the curve of the corner. For roundrects where the stroke width
+// is greater than the corner radius, the outer triangles blend from the curve to the straight
+// sides. Otherwise these triangles will be degenerate.
+//
+// In the case where the stroke width is greater than the corner radius and the
+// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
+// This rectangle extends the coverage values of the center edges of the 9-patch.
+// ____________
+// |_|________|_|
+// | |\ ____ /| |
+// | | | | | |
+// | | |____| | |
+// |_|/______\|_|
+// |_|________|_|
+//
+// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
+
+static const uint16_t gRRectIndices[] = {
+ // clang-format off
+ // overstroke quads
+ // we place this at the beginning so that we can skip these indices when rendering as filled
+ 0, 6, 25, 0, 25, 24,
+ 6, 18, 27, 6, 27, 25,
+ 18, 12, 26, 18, 26, 27,
+ 12, 0, 24, 12, 24, 26,
+
+ // corners
+ 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
+ 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
+ 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
+ 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
+
+ // edges
+ 0, 5, 11, 0, 11, 6,
+ 6, 7, 19, 6, 19, 18,
+ 18, 23, 17, 18, 17, 12,
+ 12, 13, 1, 12, 1, 0,
+
+ // fill quad
+ // we place this at the end so that we can skip these indices when rendering as stroked
+ 0, 6, 18, 0, 18, 12,
+ // clang-format on
+};
+
+// overstroke count
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
+// simple stroke count skips overstroke indices
+static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
+// fill count adds final quad to stroke count
+static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
+static const int kVertsPerStrokeRRect = 24;
+static const int kVertsPerOverstrokeRRect = 28;
+static const int kVertsPerFillRRect = 24;
+
+static int rrect_type_to_vert_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kVertsPerFillRRect;
+ case kStroke_RRectType:
+ return kVertsPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kVertsPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static int rrect_type_to_index_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kIndicesPerFillRRect;
+ case kStroke_RRectType:
+ return kIndicesPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kIndicesPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static const uint16_t* rrect_type_to_indices(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ case kStroke_RRectType:
+ return gRRectIndices + 6 * 4;
+ case kOverstroke_RRectType:
+ return gRRectIndices;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return nullptr;
+}
+
+static void fillInCircleVerts(const Geometry& args, bool isStroked,
+ Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+ float innerRadius = args.fInnerRadius;
+ float blurRadius = args.fBlurRadius;
+ float distanceCorrection = outerRadius / blurRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ // The inner radius in the vertex data must be specified in normalized space.
+ innerRadius = innerRadius / outerRadius;
+
+ vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
+ float halfWidth = 0.5f * bounds.getWidth();
+ float octOffset = 0.41421356237f; // sqrt(2) - 1
+ int vertexCount = 0;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ if (isStroked) {
+ // compute the inner ring
+
+ // cosine and sine of pi/8
+ float c = 0.923579533f;
+ float s = 0.382683432f;
+ float r = args.fInnerRadius;
+
+ position[vertexCount] = center + vec2(-s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+ } else {
+ // filled
+ position[vertexCount] = center;
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ float umbraInset = args.fUmbraInset;
+ float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
+ if (umbraInset > minDim) {
+ umbraInset = minDim;
+ }
+
+ float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
+ bounds.left + umbraInset, bounds.right - umbraInset};
+ float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
+ bounds.left + outerRadius, bounds.right - outerRadius};
+ float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
+ float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
+ bounds.bottom - umbraInset};
+ float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
+ bounds.bottom - outerRadius, bounds.bottom - outerRadius};
+ float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
+
+ float blurRadius = args.fBlurRadius;
+
+ // In the case where we have to inset more for the umbra, our two triangles in the
+ // corner get skewed to a diamond rather than a square. To correct for that,
+ // we also skew the vectors we send to the shader that help define the circle.
+ // By doing so, we end up with a quarter circle in the corner rather than the
+ // elliptical curve.
+
+ // This is a bit magical, but it gives us the correct results at extrema:
+ // a) umbraInset == outerRadius produces an orthogonal vector
+ // b) outerRadius == 0 produces a diagonal vector
+ // And visually the corner looks correct.
+ vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
+ outerVec = normalize(outerVec);
+ // We want the circle edge to fall fractionally along the diagonal at
+ // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
+ //
+ // Setting the components of the diagonal offset to the following value will give us that.
+ float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
+ vec2 diagVec = vec2(diagVal, diagVal);
+ float distanceCorrection = umbraInset / blurRadius;
+
+ int vertexCount = 0;
+ // build corner by corner
+ for (int i = 0; i < 4; ++i) {
+ // inner point
+ position[vertexCount] = vec2(xInner[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // outer points
+ position[vertexCount] = vec2(xOuter[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yMid[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xMid[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xInner[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+ }
+
+ // Add the additional vertices for overstroked rrects.
+ // Effectively this is an additional stroked rrect, with its
+ // parameters equal to those in the center of the 9-patch. This will
+ // give constant values across this inner ring.
+ if (kOverstroke_RRectType == args.fType) {
+ float inset = umbraInset + args.fInnerRadius;
+
+ // TL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // TR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_vert_count(shadowGeometry.fType);
+ }
+
+ return rrect_type_to_vert_count(shadowGeometry.fType);
+}
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
+ }
+
+ return rrect_type_to_index_count(shadowGeometry.fType);
+}
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams) {
+ if (shadowGeometry.fIsCircle) {
+ fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
+ shadowParams);
+ } else {
+ fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
+ }
+}
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices) {
+ if (shadowGeometry.fIsCircle) {
+ const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ } else {
+ const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ }
+}
+
+inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
+ float lightRadius, float& blurRadius, float& scale, vec2& translate) {
+ float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
+ blurRadius = lightRadius * zRatio;
+ scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
+ translate.x = -zRatio * lightX;
+ translate.y = -zRatio * lightY;
+}
+
+static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
+ float devRadius, float blurRadius,
+ float insetWidth) {
+ // An insetWidth > 1/2 rect width or height indicates a simple fill.
+ const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
+
+ FloatRect bounds = devRect;
+ float innerRadius = 0.0f;
+ float outerRadius = devRadius;
+ float umbraInset;
+
+ RRectType type = kFill_RRectType;
+ if (isCircle) {
+ umbraInset = 0;
+ } else {
+ umbraInset = std::max(outerRadius, blurRadius);
+ }
+
+ // If stroke is greater than width or height, this is still a fill,
+ // otherwise we compute stroke params.
+ if (isCircle) {
+ innerRadius = devRadius - insetWidth;
+ type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+ } else {
+ if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
+ // We don't worry about a real inner radius, we just need to know if we
+ // need to create overstroke vertices.
+ innerRadius = std::max(insetWidth - umbraInset, 0.0f);
+ type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+ }
+ }
+ const bool isStroked = (kStroke_RRectType == type);
+ return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
+ blurRadius, bounds, type, isCircle, isStroked});
+}
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor) {
+ float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
+ const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
+ const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
+
+ // Outset the shadow rrect to the border of the penumbra
+ float ambientPathOutset = devSpaceInsetWidth;
+ FloatRect outsetRect(casterRect);
+ outsetRect.left -= ambientPathOutset;
+ outsetRect.top -= ambientPathOutset;
+ outsetRect.right += ambientPathOutset;
+ outsetRect.bottom += ambientPathOutset;
+
+ float outsetRad = casterCornerRadius + ambientPathOutset;
+ if (casterIsTranslucent) {
+ // set a large inset to force a fill
+ devSpaceInsetWidth = outsetRect.getWidth();
+ }
+
+ return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
+ std::abs(devSpaceInsetWidth));
+}
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius) {
+ float devSpaceSpotBlur;
+ float spotScale;
+ vec2 spotOffset;
+ GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
+ devSpaceSpotBlur, spotScale, spotOffset);
+ // handle scale of radius due to CTM
+ const float srcSpaceSpotBlur = devSpaceSpotBlur;
+
+ // Adjust translate for the effect of the scale.
+ spotOffset.x += spotScale;
+ spotOffset.y += spotScale;
+
+ // Compute the transformed shadow rect
+ ui::Transform shadowTransform;
+ shadowTransform.set(spotOffset.x, spotOffset.y);
+ shadowTransform.set(spotScale, 0, 0, spotScale);
+ FloatRect spotShadowRect = shadowTransform.transform(casterRect);
+ float spotShadowRadius = casterCornerRadius * spotScale;
+
+ // Compute the insetWidth
+ float blurOutset = srcSpaceSpotBlur;
+ float insetWidth = blurOutset;
+ if (casterIsTranslucent) {
+ // If transparent, just do a fill
+ insetWidth += spotShadowRect.getWidth();
+ } else {
+ // For shadows, instead of using a stroke we specify an inset from the penumbra
+ // border. We want to extend this inset area so that it meets up with the caster
+ // geometry. The inset geometry will by default already be inset by the blur width.
+ //
+ // We compare the min and max corners inset by the radius between the original
+ // rrect and the shadow rrect. The distance between the two plus the difference
+ // between the scaled radius and the original radius gives the distance from the
+ // transformed shadow shape to the original shape in that corner. The max
+ // of these gives the maximum distance we need to cover.
+ //
+ // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+ // that to get the full insetWidth.
+ float maxOffset;
+ if (casterCornerRadius <= 0.f) {
+ // Manhattan distance works better for rects
+ maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
+ std::abs(spotShadowRect.top - casterRect.top)),
+ std::max(std::abs(spotShadowRect.right - casterRect.right),
+ std::abs(spotShadowRect.bottom - casterRect.bottom)));
+ } else {
+ float dr = spotShadowRadius - casterCornerRadius;
+ vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
+ spotShadowRect.top - casterRect.top + dr);
+ vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
+ spotShadowRect.bottom - casterRect.bottom - dr);
+ maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
+ dot(lowerRightOffset, lowerRightOffset))) +
+ dr;
+ }
+ insetWidth += std::max(blurOutset, maxOffset);
+ }
+
+ // Outset the shadow rrect to the border of the penumbra
+ spotShadowRadius += blurOutset;
+ spotShadowRect.left -= blurOutset;
+ spotShadowRect.top -= blurOutset;
+ spotShadowRect.right += blurOutset;
+ spotShadowRect.bottom += blurOutset;
+
+ return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
+ 2.0f * devSpaceSpotBlur, std::abs(insetWidth));
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
new file mode 100644
index 0000000..e7d1861
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <math/vec4.h>
+#include <ui/Rect.h>
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+enum RRectType {
+ kFill_RRectType,
+ kStroke_RRectType,
+ kOverstroke_RRectType,
+};
+
+struct Geometry {
+ vec4 fColor;
+ float fOuterRadius;
+ float fUmbraInset;
+ float fInnerRadius;
+ float fBlurRadius;
+ FloatRect fDevBounds;
+ RRectType fType;
+ bool fIsCircle;
+ bool fIsStroked;
+};
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius);
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor);
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry);
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry);
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams);
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices);
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index 4eb5eb6..f4fbf35 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -37,6 +37,8 @@
glBindAttribLocation(programId, position, "position");
glBindAttribLocation(programId, texCoords, "texCoords");
glBindAttribLocation(programId, cropCoords, "cropCoords");
+ glBindAttribLocation(programId, shadowColor, "shadowColor");
+ glBindAttribLocation(programId, shadowParams, "shadowParams");
glLinkProgram(programId);
GLint status;
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index c9beb68..fc3755e 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -44,7 +44,13 @@
texCoords = 1,
/* Crop coordinates, in pixels */
- cropCoords = 2
+ cropCoords = 2,
+
+ /* Shadow color */
+ shadowColor = 3,
+
+ /* Shadow params */
+ shadowParams = 4,
};
Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index e2757e1..ba0e4ad 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -174,16 +174,15 @@
.set(Key::OPACITY_MASK,
description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
.set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
- description.hasInputTransformMatrix()
- ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
+ description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
+ : Key::INPUT_TRANSFORM_MATRIX_OFF)
.set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
- description.cornerRadius > 0
- ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
-
+ description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
+ .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
needs.set(Key::Y410_BT2020_MASK,
description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
@@ -559,6 +558,12 @@
vs << "attribute lowp vec4 cropCoords;";
vs << "varying lowp vec2 outCropCoords;";
}
+ if (needs.drawShadows()) {
+ vs << "attribute vec4 shadowColor;";
+ vs << "varying vec4 outShadowColor;";
+ vs << "attribute vec4 shadowParams;";
+ vs << "varying vec3 outShadowParams;";
+ }
vs << "attribute vec4 position;"
<< "uniform mat4 projection;"
<< "uniform mat4 texture;"
@@ -569,6 +574,10 @@
if (needs.hasRoundedCorners()) {
vs << "outCropCoords = cropCoords.st;";
}
+ if (needs.drawShadows()) {
+ vs << "outShadowColor = shadowColor;";
+ vs << "outShadowParams = shadowParams.xyz;";
+ }
vs << dedent << "}";
return vs.getString();
}
@@ -614,6 +623,26 @@
)__SHADER__";
}
+ if (needs.drawShadows()) {
+ fs << R"__SHADER__(
+ varying vec4 outShadowColor;
+ varying vec3 outShadowParams;
+
+ /**
+ * Returns the shadow color.
+ */
+ vec4 getShadowColor()
+ {
+ // exponential falloff function provided by UX
+ float d = length(outShadowParams.xy);
+ float distance = outShadowParams.z * (1.0 - d);
+ float factor = 1.0 - clamp(distance, 0.0, 1.0);
+ factor = exp(-factor * factor * 4.0) - 0.018;
+ return outShadowColor * factor;
+ }
+ )__SHADER__";
+ }
+
if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
fs << "uniform vec4 color;";
}
@@ -677,25 +706,29 @@
}
fs << "void main(void) {" << indent;
- if (needs.isTexturing()) {
- fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
- if (needs.isY410BT2020()) {
- fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
- }
+ if (needs.drawShadows()) {
+ fs << "gl_FragColor = getShadowColor();";
} else {
- fs << "gl_FragColor.rgb = color.rgb;";
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.isOpaque()) {
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.hasAlpha()) {
- // modulate the current alpha value with alpha set
- if (needs.isPremultiplied()) {
- // ... and the color too if we're premultiplied
- fs << "gl_FragColor *= color.a;";
+ if (needs.isTexturing()) {
+ fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+ if (needs.isY410BT2020()) {
+ fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+ }
} else {
- fs << "gl_FragColor.a *= color.a;";
+ fs << "gl_FragColor.rgb = color.rgb;";
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.isOpaque()) {
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.hasAlpha()) {
+ // modulate the current alpha value with alpha set
+ if (needs.isPremultiplied()) {
+ // ... and the color too if we're premultiplied
+ fs << "gl_FragColor *= color.a;";
+ } else {
+ fs << "gl_FragColor.a *= color.a;";
+ }
}
}
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 175c6e8..c8b6da7 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -112,6 +112,11 @@
Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
+
+ SHADOW_SHIFT = 13,
+ SHADOW_MASK = 1 << SHADOW_SHIFT,
+ SHADOW_OFF = 0 << SHADOW_SHIFT,
+ SHADOW_ON = 1 << SHADOW_SHIFT,
};
inline Key() : mKey(0) {}
@@ -130,6 +135,7 @@
inline bool hasRoundedCorners() const {
return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
}
+ inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
inline bool hasInputTransformMatrix() const {
return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
}
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 9c9884a..c4a29a9 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -16,6 +16,8 @@
#pragma once
+#include <iosfwd>
+
#include <math/mat4.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -62,5 +64,31 @@
uint32_t orientation = ui::Transform::ROT_0;
};
+static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
+ return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
+ lhs.globalTransform == rhs.globalTransform && lhs.maxLuminance == rhs.maxLuminance &&
+ lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) {
+ *os << "DisplaySettings {";
+ *os << "\n .physicalDisplay = ";
+ PrintTo(settings.physicalDisplay, os);
+ *os << "\n .clip = ";
+ PrintTo(settings.clip, os);
+ *os << "\n .globalTransform = " << settings.globalTransform;
+ *os << "\n .maxLuminance = " << settings.maxLuminance;
+ *os << "\n .outputDataspace = ";
+ PrintTo(settings.outputDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .clearRegion = ";
+ PrintTo(settings.clearRegion, os);
+ *os << "\n .orientation = " << settings.orientation;
+ *os << "\n}";
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index d890ccd..5aa3f3b 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,8 @@
#pragma once
+#include <iosfwd>
+
#include <math/mat4.h>
#include <math/vec3.h>
#include <renderengine/Texture.h>
@@ -149,5 +151,102 @@
ShadowSettings shadow;
};
+static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
+ lhs.textureName == rhs.textureName &&
+ lhs.useTextureFiltering == rhs.useTextureFiltering &&
+ lhs.textureTransform == rhs.textureTransform &&
+ lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+ lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+ lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+ lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
+ return lhs.boundaries == rhs.boundaries && lhs.positionTransform == rhs.positionTransform &&
+ lhs.roundedCornersRadius == rhs.roundedCornersRadius &&
+ lhs.roundedCornersCrop == rhs.roundedCornersCrop;
+}
+
+static inline bool operator==(const PixelSource& lhs, const PixelSource& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
+}
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
+ lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
+ lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+ return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
+ lhs.sourceDataspace == rhs.sourceDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+}
+
+// Defining PrintTo helps with Google Tests.
+
+static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
+ *os << "Buffer {";
+ *os << "\n .buffer = " << settings.buffer.get();
+ *os << "\n .fence = " << settings.fence.get();
+ *os << "\n .textureName = " << settings.textureName;
+ *os << "\n .useTextureFiltering = " << settings.useTextureFiltering;
+ *os << "\n .textureTransform = " << settings.textureTransform;
+ *os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
+ *os << "\n .isOpaque = " << settings.isOpaque;
+ *os << "\n .isY410BT2020 = " << settings.isY410BT2020;
+ *os << "\n .maxMasteringLuminance = " << settings.maxMasteringLuminance;
+ *os << "\n .maxContentLuminance = " << settings.maxContentLuminance;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const Geometry& settings, ::std::ostream* os) {
+ *os << "Geometry {";
+ *os << "\n .boundaries = ";
+ PrintTo(settings.boundaries, os);
+ *os << "\n .positionTransform = " << settings.positionTransform;
+ *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersCrop = ";
+ PrintTo(settings.roundedCornersCrop, os);
+ *os << "\n}";
+}
+
+static inline void PrintTo(const PixelSource& settings, ::std::ostream* os) {
+ *os << "PixelSource {";
+ *os << "\n .buffer = ";
+ PrintTo(settings.buffer, os);
+ *os << "\n .solidColor = " << settings.solidColor;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
+ *os << "ShadowSettings {";
+ *os << "\n .ambientColor = " << settings.ambientColor;
+ *os << "\n .spotColor = " << settings.spotColor;
+ *os << "\n .lightPos = " << settings.lightPos;
+ *os << "\n .lightRadius = " << settings.lightRadius;
+ *os << "\n .length = " << settings.length;
+ *os << "\n .casterIsTranslucent = " << settings.casterIsTranslucent;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
+ *os << "LayerSettings {";
+ *os << "\n .geometry = ";
+ PrintTo(settings.geometry, os);
+ *os << "\n .source = ";
+ PrintTo(settings.source, os);
+ *os << "\n .alpha = " << settings.alpha;
+ *os << "\n .sourceDataspace = ";
+ PrintTo(settings.sourceDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .disableBlending = " << settings.disableBlending;
+ *os << "\n .shadow = ";
+ PrintTo(settings.shadow, os);
+ *os << "\n}";
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
index 7618424..167f13f 100644
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -26,13 +26,14 @@
class Mesh {
public:
+ class Builder;
+
enum Primitive {
TRIANGLES = 0x0004, // GL_TRIANGLES
TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN
};
- Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0);
~Mesh() = default;
/*
@@ -43,12 +44,20 @@
friend class Mesh;
float* mData;
size_t mStride;
+ size_t mOffset = 0;
VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
public:
- TYPE& operator[](size_t index) { return *reinterpret_cast<TYPE*>(&mData[index * mStride]); }
+ // Returns a vertex array at an offset so its easier to append attributes from
+ // multiple sources.
+ VertexArray(VertexArray<TYPE>& other, size_t offset)
+ : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
+
+ TYPE& operator[](size_t index) {
+ return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
+ }
TYPE const& operator[](size_t index) const {
- return *reinterpret_cast<TYPE const*>(&mData[index * mStride]);
+ return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]);
}
};
@@ -67,6 +76,18 @@
return VertexArray<TYPE>(getCropCoords(), mStride);
}
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowColorArray() {
+ return VertexArray<TYPE>(getShadowColor(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowParamsArray() {
+ return VertexArray<TYPE>(getShadowParams(), mStride);
+ }
+
+ uint16_t* getIndicesArray() { return getIndices(); }
+
Primitive getPrimitive() const;
// returns a pointer to the vertices positions
@@ -78,6 +99,15 @@
// returns a pointer to the vertices crop coordinates
float const* getCropCoords() const;
+ // returns a pointer to colors
+ float const* getShadowColor() const;
+
+ // returns a pointer to the shadow params
+ float const* getShadowParams() const;
+
+ // returns a pointer to indices
+ uint16_t const* getIndices() const;
+
// number of vertices in this mesh
size_t getVertexCount() const;
@@ -87,6 +117,12 @@
// dimension of texture coordinates
size_t getTexCoordsSize() const;
+ size_t getShadowParamsSize() const;
+
+ size_t getShadowColorSize() const;
+
+ size_t getIndexCount() const;
+
// return stride in bytes
size_t getByteStride() const;
@@ -94,6 +130,8 @@
size_t getStride() const;
private:
+ Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount);
Mesh(const Mesh&);
Mesh& operator=(const Mesh&);
Mesh const& operator=(const Mesh&) const;
@@ -101,13 +139,65 @@
float* getPositions();
float* getTexCoords();
float* getCropCoords();
+ float* getShadowColor();
+ float* getShadowParams();
+ uint16_t* getIndices();
std::vector<float> mVertices;
size_t mVertexCount;
size_t mVertexSize;
size_t mTexCoordsSize;
+ size_t mCropCoordsSize;
+ size_t mShadowColorSize;
+ size_t mShadowParamsSize;
size_t mStride;
Primitive mPrimitive;
+ std::vector<uint16_t> mIndices;
+ size_t mIndexCount;
+};
+
+class Mesh::Builder {
+public:
+ Builder& setPrimitive(Primitive primitive) {
+ mPrimitive = primitive;
+ return *this;
+ };
+ Builder& setVertices(size_t vertexCount, size_t vertexSize) {
+ mVertexCount = vertexCount;
+ mVertexSize = vertexSize;
+ return *this;
+ };
+ Builder& setTexCoords(size_t texCoordsSize) {
+ mTexCoordsSize = texCoordsSize;
+ return *this;
+ };
+ Builder& setCropCoords(size_t cropCoordsSize) {
+ mCropCoordsSize = cropCoordsSize;
+ return *this;
+ };
+ Builder& setShadowAttrs() {
+ mShadowParamsSize = 3;
+ mShadowColorSize = 4;
+ return *this;
+ };
+ Builder& setIndices(size_t indexCount) {
+ mIndexCount = indexCount;
+ return *this;
+ };
+ Mesh build() const {
+ return Mesh{mPrimitive, mVertexCount, mVertexSize, mTexCoordsSize,
+ mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount};
+ }
+
+private:
+ size_t mVertexCount = 0;
+ size_t mVertexSize = 0;
+ size_t mTexCoordsSize = 0;
+ size_t mCropCoordsSize = 0;
+ size_t mShadowColorSize = 0;
+ size_t mShadowParamsSize = 0;
+ size_t mIndexCount = 0;
+ Primitive mPrimitive;
};
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index bad64c2..a62161a 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -81,6 +81,9 @@
mat4 colorMatrix;
mat4 inputTransformMatrix;
mat4 outputTransformMatrix;
+
+ // True if this layer will draw a shadow.
+ bool drawShadows = false;
};
} // namespace renderengine
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d01c740..ba5a3f5 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -16,6 +16,7 @@
#include <chrono>
#include <condition_variable>
+#include <fstream>
#include <gtest/gtest.h>
#include <renderengine/RenderEngine.h>
@@ -26,6 +27,7 @@
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
namespace android {
@@ -68,21 +70,80 @@
RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
~RenderEngineTest() {
+ if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
+ writeBufferToFile("/data/texture_out_");
+ }
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
}
}
- void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
- uint8_t tolerance = 0) {
+ void writeBufferToFile(const char* basename) {
+ std::string filename(basename);
+ filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name());
+ filename.append(".ppm");
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ if (!file.is_open()) {
+ ALOGE("Unable to open file: %s", filename.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+ "surfaceflinger to write debug images");
+ return;
+ }
+
uint8_t* pixels;
mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
reinterpret_cast<void**>(&pixels));
- auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
- uint8_t tmp = a >= b ? a - b : b - a;
- return tmp <= tolerance;
+ file << "P6\n";
+ file << mBuffer->getWidth() << "\n";
+ file << mBuffer->getHeight() << "\n";
+ file << 255 << "\n";
+
+ std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+ auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
+
+ for (int32_t j = 0; j < mBuffer->getHeight(); j++) {
+ const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
+ for (int32_t i = 0; i < mBuffer->getWidth(); i++) {
+ // Only copy R, G and B components
+ outPtr[0] = src[0];
+ outPtr[1] = src[1];
+ outPtr[2] = src[2];
+ outPtr += 3;
+
+ src += 4;
+ }
+ }
+ file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+ mBuffer->unlock();
+ }
+
+ void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ size_t c;
+ Rect const* rect = region.getArray(&c);
+ for (size_t i = 0; i < c; i++, rect++) {
+ expectBufferColor(*rect, r, g, b, a);
+ }
+ }
+
+ void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
+ auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
+ uint8_t tmp = a >= b ? a - b : b - a;
+ return tmp <= tolerance;
+ };
+ return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
};
+
+ expectBufferColor(rect, r, g, b, a, colorCompare);
+ }
+
+ void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
+ uint8_t* pixels;
+ mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
int32_t maxFails = 10;
int32_t fails = 0;
for (int32_t j = 0; j < region.getHeight(); j++) {
@@ -90,7 +151,7 @@
pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
for (int32_t i = 0; i < region.getWidth(); i++) {
const uint8_t expected[4] = {r, g, b, a};
- bool equal = std::equal(src, src + 4, expected, colorCompare);
+ bool equal = colorCompare(src, expected);
EXPECT_TRUE(equal)
<< "pixel @ (" << region.left + i << ", " << region.top + j << "): "
<< "expected (" << static_cast<uint32_t>(r) << ", "
@@ -111,6 +172,64 @@
mBuffer->unlock();
}
+ void expectAlpha(const Rect& rect, uint8_t a) {
+ auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
+ return colorA[3] == colorB[3];
+ };
+ expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+ }
+
+ void expectShadowColor(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor) {
+ const Rect casterRect(castingLayer.geometry.boundaries);
+ Region casterRegion = Region(casterRect);
+ const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ if (casterCornerRadius > 0.0f) {
+ // ignore the corners if a corner radius is set
+ Rect cornerRect(casterCornerRadius, casterCornerRadius);
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.left, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.right - casterCornerRadius, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.left, casterRect.bottom - casterCornerRadius));
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.right - casterCornerRadius,
+ casterRect.bottom - casterCornerRadius));
+ }
+
+ const float shadowInset = shadow.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region shadowRegion = Region(casterWithShadow).subtractSelf(casterRect);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+
+ // verify casting layer
+ expectBufferColor(casterRegion, casterColor.r, casterColor.g, casterColor.b, casterColor.a);
+
+ // verify shadows by testing just the alpha since its difficult to validate the shadow color
+ size_t c;
+ Rect const* r = shadowRegion.getArray(&c);
+ for (size_t i = 0; i < c; i++, r++) {
+ expectAlpha(*r, 255);
+ }
+
+ // verify background
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+ }
+
+ static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+ bool casterIsTranslucent) {
+ renderengine::ShadowSettings shadow;
+ shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
+ shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
+ shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
+ shadow.lightRadius = 0.0f;
+ shadow.length = shadowLength;
+ shadow.casterIsTranslucent = casterIsTranslucent;
+ return shadow;
+ }
+
static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
static Rect offsetRect() {
@@ -225,6 +344,11 @@
void clearRegion();
+ template <typename SourceVariant>
+ void drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor);
+
// Keep around the same renderengine object to save on initialization time.
// For now, exercise the GL backend directly so that some caching specifics
// can be tested without changing the interface.
@@ -766,6 +890,40 @@
0, 0, 0, 0);
}
+template <typename SourceVariant>
+void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow,
+ const ubyte4& casterColor, const ubyte4& backgroundColor) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ // add background layer
+ renderengine::LayerSettings bgLayer;
+ bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+ backgroundColor.b / 255.0f, this);
+ bgLayer.alpha = backgroundColor.a / 255.0f;
+ layers.push_back(bgLayer);
+
+ // add shadow layer
+ renderengine::LayerSettings shadowLayer;
+ shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
+ shadowLayer.alpha = castingLayer.alpha;
+ shadowLayer.shadow = shadow;
+ layers.push_back(shadowLayer);
+
+ // add layer casting the shadow
+ renderengine::LayerSettings layer = castingLayer;
+ SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
+ casterColor.b / 255.0f, this);
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
drawEmptyLayers();
}
@@ -1083,4 +1241,101 @@
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(1, 1);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 0.5f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ true /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+
+ // verify only the background since the shadow will draw behind the caster
+ const float shadowInset = settings.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterBounds).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+}
+
} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 3775e2b..124bda2 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -89,14 +89,19 @@
"android.hardware.graphics.mapper@4.0",
"libbase",
"libcutils",
+ "libgralloctypes",
"libhidlbase",
"libsync",
"libutils",
"liblog",
+ "vintf-graphics-common-ndk_platform",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.mapper@4.0",
+ "libgralloctypes",
+ "vintf-graphics-common-ndk_platform",
],
static_libs: [
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 0e23ddf..040a62b 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -351,12 +351,6 @@
return releaseFence;
}
-status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/,
- android::PixelFormat /*format*/, uint32_t /*layerCount*/,
- uint64_t /*usage*/, bool* /*outSupported*/) const {
- return INVALID_OPERATION;
-}
-
Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
@@ -369,7 +363,7 @@
return mAllocator != nullptr;
}
-std::string Gralloc2Allocator::dumpDebugInfo() const {
+std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const {
std::string debugInfo;
mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -379,10 +373,10 @@
return debugInfo;
}
-status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers) const {
+status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index e189281..882674f 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -352,7 +352,7 @@
return mAllocator != nullptr;
}
-std::string Gralloc3Allocator::dumpDebugInfo() const {
+std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const {
std::string debugInfo;
mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
@@ -360,10 +360,10 @@
return debugInfo;
}
-status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers) const {
+status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index afe26b7..2c897cf 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -27,11 +27,20 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using android::hardware::hidl_vec;
using android::hardware::graphics::allocator::V4_0::IAllocator;
using android::hardware::graphics::common::V1_2::BufferUsage;
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
+using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
+using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataTypeDescription =
+ android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription;
namespace android {
@@ -59,10 +68,10 @@
outRect.height = rect.height();
return outRect;
}
-static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height,
- android::PixelFormat format, uint32_t layerCount,
- uint64_t usage,
+static inline void sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+ outDescriptorInfo->name = name;
outDescriptorInfo->width = width;
outDescriptorInfo->height = height;
outDescriptorInfo->layerCount = layerCount;
@@ -151,11 +160,12 @@
}
status_t Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
- uint32_t height, android::PixelFormat format,
+ uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
uint32_t stride) const {
IMapper::BufferDescriptorInfo descriptorInfo;
- sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+ sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount, usage,
+ &descriptorInfo);
auto buffer = const_cast<native_handle_t*>(bufferHandle);
auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
@@ -189,14 +199,36 @@
status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
int acquireFence, void** outData, int32_t* outBytesPerPixel,
int32_t* outBytesPerStride) const {
- // In Gralloc 4 we can get this info per plane. Clients should check per plane.
- if (outBytesPerPixel) {
- // TODO add support to check per plane
- *outBytesPerPixel = -1;
- }
- if (outBytesPerStride) {
- // TODO add support to check per plane
- *outBytesPerStride = -1;
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t err = getPlaneLayouts(bufferHandle, &planeLayouts);
+
+ if (err != NO_ERROR && !planeLayouts.empty()) {
+ if (outBytesPerPixel) {
+ int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits;
+ for (const auto& planeLayout : planeLayouts) {
+ if (bitsPerPixel != planeLayout.sampleIncrementInBits) {
+ bitsPerPixel = -1;
+ }
+ }
+ if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) {
+ *outBytesPerPixel = bitsPerPixel / 8;
+ } else {
+ *outBytesPerPixel = -1;
+ }
+ }
+ if (outBytesPerStride) {
+ int32_t bytesPerStride = planeLayouts.front().strideInBytes;
+ for (const auto& planeLayout : planeLayouts) {
+ if (bytesPerStride != planeLayout.strideInBytes) {
+ bytesPerStride = -1;
+ }
+ }
+ if (bytesPerStride >= 0) {
+ *outBytesPerStride = bytesPerStride;
+ } else {
+ *outBytesPerStride = -1;
+ }
+ }
}
auto buffer = const_cast<native_handle_t*>(bufferHandle);
@@ -234,10 +266,104 @@
return static_cast<status_t>(error);
}
-status_t Gralloc4Mapper::lock(buffer_handle_t /*bufferHandle*/, uint64_t /*usage*/,
- const Rect& /*bounds*/, int /*acquireFence*/,
- android_ycbcr* /*ycbcr*/) const {
- // TODO add lockYCbCr support
+status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* outYcbcr) const {
+ if (!outYcbcr) {
+ return BAD_VALUE;
+ }
+
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t error = getPlaneLayouts(bufferHandle, &planeLayouts);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ void* data = nullptr;
+ error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ android_ycbcr ycbcr;
+
+ ycbcr.y = nullptr;
+ ycbcr.cb = nullptr;
+ ycbcr.cr = nullptr;
+ ycbcr.ystride = 0;
+ ycbcr.cstride = 0;
+ ycbcr.chroma_step = 0;
+
+ for (const auto& planeLayout : planeLayouts) {
+ for (const auto& planeLayoutComponent : planeLayout.components) {
+ if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
+ continue;
+ }
+ if (0 != planeLayoutComponent.offsetInBits % 8) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes +
+ (planeLayoutComponent.offsetInBits / 8);
+ uint64_t sampleIncrementInBytes;
+
+ auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
+ switch (type) {
+ case PlaneLayoutComponentType::Y:
+ if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) ||
+ (planeLayout.sampleIncrementInBits != 8)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.y = tmpData;
+ ycbcr.ystride = planeLayout.strideInBytes;
+ break;
+
+ case PlaneLayoutComponentType::CB:
+ case PlaneLayoutComponentType::CR:
+ if (planeLayout.sampleIncrementInBits % 8 != 0) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
+ if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) {
+ ycbcr.cstride = planeLayout.strideInBytes;
+ ycbcr.chroma_step = sampleIncrementInBytes;
+ } else {
+ if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) ||
+ (ycbcr.chroma_step != sampleIncrementInBytes)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ }
+
+ if (type == PlaneLayoutComponentType::CB) {
+ if (ycbcr.cb != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cb = tmpData;
+ } else {
+ if (ycbcr.cr != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cr = tmpData;
+ }
+ break;
+ default:
+ break;
+ };
+ }
+ }
+
+ *outYcbcr = ycbcr;
return static_cast<status_t>(Error::UNSUPPORTED);
}
@@ -275,11 +401,11 @@
return releaseFence;
}
-status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
bool* outSupported) const {
IMapper::BufferDescriptorInfo descriptorInfo;
- sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+ sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, &descriptorInfo);
Error error;
auto ret = mMapper->isSupported(descriptorInfo,
@@ -305,6 +431,605 @@
return static_cast<status_t>(error);
}
+template <class T>
+status_t Gralloc4Mapper::get(buffer_handle_t bufferHandle, const MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const {
+ if (!outMetadata) {
+ return BAD_VALUE;
+ }
+
+ hidl_vec<uint8_t> vec;
+ Error error;
+ auto ret = mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType,
+ [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
+ error = tmpError;
+ vec = tmpVec;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("get(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+ metadataType.value, error);
+ return static_cast<status_t>(error);
+ }
+
+ return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const {
+ return get(bufferHandle, gralloc4::MetadataType_BufferId, gralloc4::decodeBufferId,
+ outBufferId);
+}
+
+status_t Gralloc4Mapper::getName(buffer_handle_t bufferHandle, std::string* outName) const {
+ return get(bufferHandle, gralloc4::MetadataType_Name, gralloc4::decodeName, outName);
+}
+
+status_t Gralloc4Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const {
+ return get(bufferHandle, gralloc4::MetadataType_Width, gralloc4::decodeWidth, outWidth);
+}
+
+status_t Gralloc4Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const {
+ return get(bufferHandle, gralloc4::MetadataType_Height, gralloc4::decodeHeight, outHeight);
+}
+
+status_t Gralloc4Mapper::getLayerCount(buffer_handle_t bufferHandle,
+ uint64_t* outLayerCount) const {
+ return get(bufferHandle, gralloc4::MetadataType_LayerCount, gralloc4::decodeLayerCount,
+ outLayerCount);
+}
+
+status_t Gralloc4Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested,
+ gralloc4::decodePixelFormatRequested, outPixelFormatRequested);
+}
+
+status_t Gralloc4Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC,
+ gralloc4::decodePixelFormatFourCC, outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier,
+ gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const {
+ return get(bufferHandle, gralloc4::MetadataType_Usage, gralloc4::decodeUsage, outUsage);
+}
+
+status_t Gralloc4Mapper::getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) const {
+ return get(bufferHandle, gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+ outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) const {
+ return get(bufferHandle, gralloc4::MetadataType_ProtectedContent,
+ gralloc4::decodeProtectedContent, outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+ ExtendableType* outCompression) const {
+ return get(bufferHandle, gralloc4::MetadataType_Compression, gralloc4::decodeCompression,
+ outCompression);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) const {
+ if (!outCompression) {
+ return BAD_VALUE;
+ }
+ ExtendableType compression;
+ status_t error = getCompression(bufferHandle, &compression);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardCompression(compression)) {
+ return BAD_TYPE;
+ }
+ *outCompression = gralloc4::getStandardCompressionValue(compression);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+ ExtendableType* outInterlaced) const {
+ return get(bufferHandle, gralloc4::MetadataType_Interlaced, gralloc4::decodeInterlaced,
+ outInterlaced);
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) const {
+ if (!outInterlaced) {
+ return BAD_VALUE;
+ }
+ ExtendableType interlaced;
+ status_t error = getInterlaced(bufferHandle, &interlaced);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardInterlaced(interlaced)) {
+ return BAD_TYPE;
+ }
+ *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ExtendableType* outChromaSiting) const {
+ return get(bufferHandle, gralloc4::MetadataType_ChromaSiting, gralloc4::decodeChromaSiting,
+ outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) const {
+ if (!outChromaSiting) {
+ return BAD_VALUE;
+ }
+ ExtendableType chromaSiting;
+ status_t error = getChromaSiting(bufferHandle, &chromaSiting);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+ return BAD_TYPE;
+ }
+ *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+ return get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, gralloc4::decodePlaneLayouts,
+ outPlaneLayouts);
+}
+
+status_t Gralloc4Mapper::getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace* outDataspace) const {
+ if (!outDataspace) {
+ return BAD_VALUE;
+ }
+ aidl::android::hardware::graphics::common::Dataspace dataspace;
+ status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
+ &dataspace);
+ if (error) {
+ return error;
+ }
+
+ // Gralloc4 uses stable AIDL dataspace but the rest of the system still uses HIDL dataspace
+ *outDataspace = static_cast<ui::Dataspace>(dataspace);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode* outBlendMode) const {
+ return get(bufferHandle, gralloc4::MetadataType_BlendMode, gralloc4::decodeBlendMode,
+ outBlendMode);
+}
+
+template <class T>
+status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ const MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const {
+ if (!outMetadata) {
+ return BAD_VALUE;
+ }
+
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, &descriptorInfo);
+
+ hidl_vec<uint8_t> vec;
+ Error error;
+ auto ret = mMapper->getFromBufferDescriptorInfo(descriptorInfo, metadataType,
+ [&](const auto& tmpError,
+ const hidl_vec<uint8_t>& tmpVec) {
+ error = tmpError;
+ vec = tmpVec;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("getDefault(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+ metadataType.value, error);
+ return static_cast<status_t>(error);
+ }
+
+ return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint32_t* outPixelFormatFourCC) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_PixelFormatFourCC, gralloc4::decodePixelFormatFourCC,
+ outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outPixelFormatModifier) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_PixelFormatModifier,
+ gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outAllocationSize) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+ outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outProtectedContent) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_ProtectedContent, gralloc4::decodeProtectedContent,
+ outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outCompression) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Compression,
+ gralloc4::decodeCompression, outCompression);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression) const {
+ if (!outCompression) {
+ return BAD_VALUE;
+ }
+ ExtendableType compression;
+ status_t error = getDefaultCompression(width, height, format, layerCount, usage, &compression);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardCompression(compression)) {
+ return BAD_TYPE;
+ }
+ *outCompression = gralloc4::getStandardCompressionValue(compression);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outInterlaced) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Interlaced,
+ gralloc4::decodeInterlaced, outInterlaced);
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced) const {
+ if (!outInterlaced) {
+ return BAD_VALUE;
+ }
+ ExtendableType interlaced;
+ status_t error = getDefaultInterlaced(width, height, format, layerCount, usage, &interlaced);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardInterlaced(interlaced)) {
+ return BAD_TYPE;
+ }
+ *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outChromaSiting) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_ChromaSiting,
+ gralloc4::decodeChromaSiting, outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) const {
+ if (!outChromaSiting) {
+ return BAD_VALUE;
+ }
+ ExtendableType chromaSiting;
+ status_t error =
+ getDefaultChromaSiting(width, height, format, layerCount, usage, &chromaSiting);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+ return BAD_TYPE;
+ }
+ *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultPlaneLayouts(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_PlaneLayouts,
+ gralloc4::decodePlaneLayouts, outPlaneLayouts);
+}
+
+std::vector<MetadataTypeDescription> Gralloc4Mapper::listSupportedMetadataTypes() const {
+ hidl_vec<MetadataTypeDescription> descriptions;
+ Error error;
+ auto ret = mMapper->listSupportedMetadataTypes(
+ [&](const auto& tmpError, const auto& tmpDescriptions) {
+ error = tmpError;
+ descriptions = tmpDescriptions;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("listSupportedMetadataType() failed with %d", error);
+ return {};
+ }
+
+ return static_cast<std::vector<MetadataTypeDescription>>(descriptions);
+}
+
+template <class T>
+status_t Gralloc4Mapper::metadataDumpHelper(const BufferDump& bufferDump,
+ StandardMetadataType metadataType,
+ DecodeFunction<T> decodeFunction, T* outT) const {
+ const auto& metadataDump = bufferDump.metadataDump;
+
+ auto itr =
+ std::find_if(metadataDump.begin(), metadataDump.end(),
+ [&](const MetadataDump& tmpMetadataDump) {
+ if (!gralloc4::isStandardMetadataType(tmpMetadataDump.metadataType)) {
+ return false;
+ }
+ return metadataType ==
+ gralloc4::getStandardMetadataTypeValue(
+ tmpMetadataDump.metadataType);
+ });
+ if (itr == metadataDump.end()) {
+ return BAD_VALUE;
+ }
+
+ return decodeFunction(itr->metadata, outT);
+}
+
+status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ostringstream* outDump,
+ uint64_t* outAllocationSize, bool less) const {
+ uint64_t bufferId;
+ std::string name;
+ uint64_t width;
+ uint64_t height;
+ uint64_t layerCount;
+ ui::PixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ uint64_t pixelFormatModifier;
+ uint64_t usage;
+ uint64_t allocationSize;
+ uint64_t protectedContent;
+ ExtendableType compression;
+ ExtendableType interlaced;
+ ExtendableType chromaSiting;
+ std::vector<ui::PlaneLayout> planeLayouts;
+
+ status_t error = metadataDumpHelper(bufferDump, StandardMetadataType::BUFFER_ID,
+ gralloc4::decodeBufferId, &bufferId);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::NAME, gralloc4::decodeName, &name);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::WIDTH, gralloc4::decodeWidth,
+ &width);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::HEIGHT, gralloc4::decodeHeight,
+ &height);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::LAYER_COUNT,
+ gralloc4::decodeLayerCount, &layerCount);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+ gralloc4::decodePixelFormatRequested, &pixelFormatRequested);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_FOURCC,
+ gralloc4::decodePixelFormatFourCC, &pixelFormatFourCC);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_MODIFIER,
+ gralloc4::decodePixelFormatModifier, &pixelFormatModifier);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::USAGE, gralloc4::decodeUsage,
+ &usage);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
+ gralloc4::decodeAllocationSize, &allocationSize);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PROTECTED_CONTENT,
+ gralloc4::decodeProtectedContent, &protectedContent);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::COMPRESSION,
+ gralloc4::decodeCompression, &compression);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::INTERLACED,
+ gralloc4::decodeInterlaced, &interlaced);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::CHROMA_SITING,
+ gralloc4::decodeChromaSiting, &chromaSiting);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PLANE_LAYOUTS,
+ gralloc4::decodePlaneLayouts, &planeLayouts);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ if (outAllocationSize) {
+ *outAllocationSize = allocationSize;
+ }
+ double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
+
+ *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
+ << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
+ << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+ << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+ << ", compressed: ";
+
+ if (less) {
+ bool isCompressed = !gralloc4::isStandardCompression(compression) ||
+ (gralloc4::getStandardCompressionValue(compression) != ui::Compression::NONE);
+ *outDump << std::boolalpha << isCompressed << "\n";
+ } else {
+ *outDump << gralloc4::getCompressionName(compression) << "\n";
+ }
+
+ bool firstPlane = true;
+ for (const auto& planeLayout : planeLayouts) {
+ if (firstPlane) {
+ firstPlane = false;
+ *outDump << "\tplanes: ";
+ } else {
+ *outDump << "\t ";
+ }
+
+ for (size_t i = 0; i < planeLayout.components.size(); i++) {
+ const auto& planeLayoutComponent = planeLayout.components[i];
+ *outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+ if (i < planeLayout.components.size() - 1) {
+ *outDump << "/";
+ } else {
+ *outDump << ":\t";
+ }
+ }
+ *outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+ << ", stride:" << planeLayout.strideInBytes
+ << " bytes, size:" << planeLayout.totalSizeInBytes;
+ if (!less) {
+ *outDump << ", inc:" << planeLayout.sampleIncrementInBits
+ << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+ << planeLayout.verticalSubsampling;
+ }
+ *outDump << "\n";
+ }
+
+ if (!less) {
+ *outDump << "\tlayer cnt: " << layerCount << ", protected content: " << protectedContent
+ << ", interlaced: " << gralloc4::getInterlacedName(interlaced)
+ << ", chroma siting:" << gralloc4::getChromaSitingName(chromaSiting) << "\n";
+ }
+
+ return NO_ERROR;
+}
+
+std::string Gralloc4Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ BufferDump bufferDump;
+ Error error;
+ auto ret = mMapper->dumpBuffer(buffer, [&](const auto& tmpError, const auto& tmpBufferDump) {
+ error = tmpError;
+ bufferDump = tmpBufferDump;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("dumpBuffer() failed with %d", error);
+ return "";
+ }
+
+ std::ostringstream stream;
+ stream.precision(2);
+
+ status_t err = bufferDumpHelper(bufferDump, &stream, nullptr, less);
+ if (err != NO_ERROR) {
+ ALOGE("bufferDumpHelper() failed with %d", err);
+ return "";
+ }
+
+ return stream.str();
+}
+
+std::string Gralloc4Mapper::dumpBuffers(bool less) const {
+ hidl_vec<BufferDump> bufferDumps;
+ Error error;
+ auto ret = mMapper->dumpBuffers([&](const auto& tmpError, const auto& tmpBufferDump) {
+ error = tmpError;
+ bufferDumps = tmpBufferDump;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("dumpBuffer() failed with %d", error);
+ return "";
+ }
+
+ uint64_t totalAllocationSize = 0;
+ std::ostringstream stream;
+ stream.precision(2);
+
+ stream << "Imported gralloc buffers:\n";
+
+ for (const auto& bufferDump : bufferDumps) {
+ uint64_t allocationSize = 0;
+ status_t err = bufferDumpHelper(bufferDump, &stream, &allocationSize, less);
+ if (err != NO_ERROR) {
+ ALOGE("bufferDumpHelper() failed with %d", err);
+ return "";
+ }
+ totalAllocationSize += allocationSize;
+ }
+
+ double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+ stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+ return stream.str();
+}
+
Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
@@ -317,20 +1042,16 @@
return mAllocator != nullptr;
}
-std::string Gralloc4Allocator::dumpDebugInfo() const {
- std::string debugInfo;
-
- mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
-
- return debugInfo;
+std::string Gralloc4Allocator::dumpDebugInfo(bool less) const {
+ return mMapper.dumpBuffers(less);
}
-status_t Gralloc4Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers) const {
+status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
- sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+ sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, &descriptorInfo);
BufferDescriptor descriptor;
status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 3a90a98..943d13e 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -78,11 +78,11 @@
return total;
}
-void GraphicBufferAllocator::dump(std::string& result) const {
+void GraphicBufferAllocator::dump(std::string& result, bool less) const {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
uint64_t total = 0;
- result.append("Allocated buffers:\n");
+ result.append("GraphicBufferAllocator buffers:\n");
const size_t c = list.size();
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
@@ -99,15 +99,15 @@
}
total += rec.size;
}
- StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", static_cast<double>(total) / 1024.0);
+ StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
+ static_cast<double>(total) / 1024.0);
- result.append(mAllocator->dumpDebugInfo());
+ result.append(mAllocator->dumpDebugInfo(less));
}
-void GraphicBufferAllocator::dumpToSystemLog()
-{
+void GraphicBufferAllocator::dumpToSystemLog(bool less) {
std::string s;
- GraphicBufferAllocator::getInstance().dump(s);
+ GraphicBufferAllocator::getInstance().dump(s, less);
ALOGD("%s", s.c_str());
}
@@ -137,8 +137,8 @@
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
- status_t error = mAllocator->allocate(width, height, format, layerCount, usage, 1, stride,
- handle, importBuffer);
+ status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
+ 1, stride, handle, importBuffer);
if (error != NO_ERROR) {
ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
"usage %" PRIx64 ": %d",
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 4d087d1..8540fd3 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -71,6 +71,17 @@
LOG_ALWAYS_FATAL("gralloc-mapper is missing");
}
+void GraphicBufferMapper::dumpBuffer(buffer_handle_t bufferHandle, std::string& result,
+ bool less) const {
+ result.append(mMapper->dumpBuffer(bufferHandle, less));
+}
+
+void GraphicBufferMapper::dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less) {
+ std::string s;
+ GraphicBufferMapper::getInstance().dumpBuffer(bufferHandle, s, less);
+ ALOGD("%s", s.c_str());
+}
+
status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
uint32_t width, uint32_t height, uint32_t layerCount,
PixelFormat format, uint64_t usage, uint32_t stride,
@@ -177,5 +188,182 @@
uint64_t usage, bool* outSupported) {
return mMapper->isSupported(width, height, format, layerCount, usage, outSupported);
}
+
+status_t GraphicBufferMapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) {
+ return mMapper->getBufferId(bufferHandle, outBufferId);
+}
+
+status_t GraphicBufferMapper::getName(buffer_handle_t bufferHandle, std::string* outName) {
+ return mMapper->getName(bufferHandle, outName);
+}
+
+status_t GraphicBufferMapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) {
+ return mMapper->getWidth(bufferHandle, outWidth);
+}
+
+status_t GraphicBufferMapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) {
+ return mMapper->getHeight(bufferHandle, outHeight);
+}
+
+status_t GraphicBufferMapper::getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) {
+ return mMapper->getLayerCount(bufferHandle, outLayerCount);
+}
+
+status_t GraphicBufferMapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) {
+ return mMapper->getPixelFormatRequested(bufferHandle, outPixelFormatRequested);
+}
+
+status_t GraphicBufferMapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) {
+ return mMapper->getPixelFormatFourCC(bufferHandle, outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) {
+ return mMapper->getPixelFormatModifier(bufferHandle, outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) {
+ return mMapper->getUsage(bufferHandle, outUsage);
+}
+
+status_t GraphicBufferMapper::getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) {
+ return mMapper->getAllocationSize(bufferHandle, outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) {
+ return mMapper->getProtectedContent(bufferHandle, outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getCompression(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+ return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) {
+ return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getInterlaced(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+ return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) {
+ return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+ return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) {
+ return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+ return mMapper->getPlaneLayouts(bufferHandle, outPlaneLayouts);
+}
+
+status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace* outDataspace) {
+ return mMapper->getDataspace(bufferHandle, outDataspace);
+}
+
+status_t GraphicBufferMapper::getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode* outBlendMode) {
+ return mMapper->getBlendMode(bufferHandle, outBlendMode);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint32_t* outPixelFormatFourCC) {
+ return mMapper->getDefaultPixelFormatFourCC(width, height, format, layerCount, usage,
+ outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outPixelFormatModifier) {
+ return mMapper->getDefaultPixelFormatModifier(width, height, format, layerCount, usage,
+ outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outAllocationSize) {
+ return mMapper->getDefaultAllocationSize(width, height, format, layerCount, usage,
+ outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outProtectedContent) {
+ return mMapper->getDefaultProtectedContent(width, height, format, layerCount, usage,
+ outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+ return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::Compression* outCompression) {
+ return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+ return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, ui::Interlaced* outInterlaced) {
+ return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+ return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+ outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) {
+ return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+ outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultPlaneLayouts(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+ return mMapper->getDefaultPlaneLayouts(width, height, format, layerCount, usage,
+ outPlaneLayouts);
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 83ebeca..bf487c4 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -280,6 +280,20 @@
return begin() == region.begin();
}
+bool Region::hasSameRects(const Region& other) const {
+ size_t thisRectCount = 0;
+ android::Rect const* thisRects = getArray(&thisRectCount);
+ size_t otherRectCount = 0;
+ android::Rect const* otherRects = other.getArray(&otherRectCount);
+
+ if (thisRectCount != otherRectCount) return false;
+
+ for (size_t i = 0; i < thisRectCount; i++) {
+ if (thisRects[i] != otherRects[i]) return false;
+ }
+ return true;
+}
+
// ----------------------------------------------------------------------------
void Region::addRectUnchecked(int l, int t, int r, int b)
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 85abb38..06b6bfe 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -49,6 +49,15 @@
return isZero(fabs(f) - 1.0f);
}
+bool Transform::operator==(const Transform& other) const {
+ return mMatrix[0][0] == other.mMatrix[0][0] && mMatrix[0][1] == other.mMatrix[0][1] &&
+ mMatrix[0][2] == other.mMatrix[0][2] && mMatrix[1][0] == other.mMatrix[1][0] &&
+ mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
+ mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
+ mMatrix[2][2] == other.mMatrix[2][2];
+ ;
+}
+
Transform Transform::operator * (const Transform& rhs) const
{
if (CC_LIKELY(mType == IDENTITY))
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 4cd9a0b..bec2552 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ostream>
+
namespace android {
class FloatRect {
@@ -52,4 +54,9 @@
return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
}
+static inline void PrintTo(const FloatRect& rect, ::std::ostream* os) {
+ *os << "FloatRect(" << rect.left << ", " << rect.top << ", " << rect.right << ", "
+ << rect.bottom << ")";
+}
+
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index c28f7a5..fcd959c 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -17,13 +17,15 @@
#ifndef ANDROID_UI_GRALLOC_H
#define ANDROID_UI_GRALLOC_H
-#include <string>
-
+#include <gralloctypes/Gralloc4.h>
#include <hidl/HidlSupport.h>
+#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include <string>
+
namespace android {
// A wrapper to IMapper
@@ -33,6 +35,10 @@
virtual bool isLoaded() const = 0;
+ virtual std::string dumpBuffer(buffer_handle_t /*bufferHandle*/, bool /*less*/) const {
+ return "";
+ }
+
virtual status_t createDescriptor(void* bufferDescriptorInfo,
void* outBufferDescriptor) const = 0;
@@ -74,8 +80,163 @@
// allocated if resources are available. If false, a buffer with the given specifications will
// never successfully allocate on this device. Note that this function is not guaranteed to be
// supported on all devices, in which case a status_t of INVALID_OPERATION will be returned.
- virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0;
+ virtual status_t isSupported(uint32_t /*width*/, uint32_t /*height*/,
+ android::PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/, bool* /*outSupported*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t getBufferId(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outBufferId*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getName(buffer_handle_t /*bufferHandle*/, std::string* /*outName*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getWidth(buffer_handle_t /*bufferHandle*/, uint64_t* /*outWidth*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getHeight(buffer_handle_t /*bufferHandle*/, uint64_t* /*outHeight*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getLayerCount(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outLayerCount*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatRequested(buffer_handle_t /*bufferHandle*/,
+ ui::PixelFormat* /*outPixelFormatRequested*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatFourCC(buffer_handle_t /*bufferHandle*/,
+ uint32_t* /*outPixelFormatFourCC*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatModifier(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outPixelFormatModifier*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getUsage(buffer_handle_t /*bufferHandle*/, uint64_t* /*outUsage*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getAllocationSize(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outAllocationSize*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getProtectedContent(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outProtectedContent*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCompression(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCompression(buffer_handle_t /*bufferHandle*/,
+ ui::Compression* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getInterlaced(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getInterlaced(buffer_handle_t /*bufferHandle*/,
+ ui::Interlaced* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getChromaSiting(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getChromaSiting(buffer_handle_t /*bufferHandle*/,
+ ui::ChromaSiting* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPlaneLayouts(buffer_handle_t /*bufferHandle*/,
+ std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDataspace(buffer_handle_t /*bufferHandle*/,
+ ui::Dataspace* /*outDataspace*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getBlendMode(buffer_handle_t /*bufferHandle*/,
+ ui::BlendMode* /*outBlendMode*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint32_t* /*outPixelFormatFourCC*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultPixelFormatModifier(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outPixelFormatModifier*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultAllocationSize(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outAllocationSize*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultProtectedContent(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outProtectedContent*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultCompression(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultCompression(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::Compression* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultInterlaced(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultInterlaced(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::Interlaced* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultChromaSiting(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultChromaSiting(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::ChromaSiting* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultPlaneLayouts(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+ listSupportedMetadataTypes() const {
+ return {};
+ }
};
// A wrapper to IAllocator
@@ -85,16 +246,17 @@
virtual bool isLoaded() const = 0;
- virtual std::string dumpDebugInfo() const = 0;
+ virtual std::string dumpDebugInfo(bool less = true) const = 0;
/*
* The returned buffers are already imported and must not be imported
* again. outBufferHandles must point to a space that can contain at
* least "bufferCount" buffer_handle_t.
*/
- virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles,
bool importBuffers = true) const = 0;
};
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 12c772a..f570c42 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -61,9 +61,6 @@
int unlock(buffer_handle_t bufferHandle) const override;
- status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
-
private:
// Determines whether the passed info is compatible with the mapper.
status_t validateBufferDescriptorInfo(
@@ -81,11 +78,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index bfbc2aa..93a5077 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -79,11 +79,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 60115f9..af7c076 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -17,16 +17,17 @@
#ifndef ANDROID_UI_GRALLOC4_H
#define ANDROID_UI_GRALLOC4_H
-#include <string>
-
#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <gralloctypes/Gralloc4.h>
#include <ui/Gralloc.h>
-#include <ui/PixelFormat.h>
+#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include <string>
+
namespace android {
class Gralloc4Mapper : public GrallocMapper {
@@ -37,6 +38,9 @@
bool isLoaded() const override;
+ std::string dumpBuffer(buffer_handle_t bufferHandle, bool less = true) const override;
+ std::string dumpBuffers(bool less = true) const;
+
status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
status_t importBuffer(const hardware::hidl_handle& rawHandle,
@@ -45,7 +49,7 @@
void freeBuffer(buffer_handle_t bufferHandle) const override;
status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
- android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
uint32_t stride) const override;
void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
@@ -60,14 +64,117 @@
int unlock(buffer_handle_t bufferHandle) const override;
- status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
+ status_t isSupported(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, bool* outSupported) const override;
+
+ status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const override;
+ status_t getName(buffer_handle_t bufferHandle, std::string* outName) const override;
+ status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const override;
+ status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const override;
+ status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) const override;
+ status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) const override;
+ status_t getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) const override;
+ status_t getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) const override;
+ status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const override;
+ status_t getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) const override;
+ status_t getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) const override;
+ status_t getCompression(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outCompression) const override;
+ status_t getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) const override;
+ status_t getInterlaced(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced)
+ const override;
+ status_t getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) const override;
+ status_t getChromaSiting(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outChromaSiting) const override;
+ status_t getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) const override;
+ status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+ status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) const override;
+ status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode) const override;
+
+ status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t* outPixelFormatFourCC) const override;
+ status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outPixelFormatModifier) const override;
+ status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outAllocationSize) const override;
+ status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outProtectedContent) const override;
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outCompression) const override;
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression) const override;
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outInterlaced) const override;
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced) const override;
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outChromaSiting) const override;
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) const override;
+ status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+
+ std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+ listSupportedMetadataTypes() const;
private:
+ friend class GraphicBufferAllocator;
+
// Determines whether the passed info is compatible with the mapper.
status_t validateBufferDescriptorInfo(
hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+ template <class T>
+ using DecodeFunction = status_t (*)(const hardware::hidl_vec<uint8_t>& input, T* output);
+
+ template <class T>
+ status_t get(
+ buffer_handle_t bufferHandle,
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+ template <class T>
+ status_t getDefault(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+ template <class T>
+ status_t metadataDumpHelper(
+ const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+ aidl::android::hardware::graphics::common::StandardMetadataType metadataType,
+ DecodeFunction<T> decodeFunction, T* outT) const;
+ status_t bufferDumpHelper(
+ const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+ std::ostringstream* outDump, uint64_t* outAllocationSize, bool less) const;
+
sp<hardware::graphics::mapper::V4_0::IMapper> mMapper;
};
@@ -79,11 +186,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc4Mapper& mMapper;
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index a182104..3ed988c 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2009, 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.
-*/
+ *
+ * Copyright 2009, 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.
+ */
#ifndef ANDROID_BUFFER_ALLOCATOR_H
#define ANDROID_BUFFER_ALLOCATOR_H
@@ -74,8 +74,8 @@
uint64_t getTotalSize() const;
- void dump(std::string& res) const;
- static void dumpToSystemLog();
+ void dump(std::string& res, bool less = true) const;
+ static void dumpToSystemLog(bool less = true);
protected:
struct alloc_rec_t {
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 83fb144..77c00ae 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -22,11 +22,11 @@
#include <memory>
+#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/Singleton.h>
-
// Needed by code that still uses the GRALLOC_USAGE_* constants.
// when/if we get rid of gralloc, we should provide aliases or fix call sites.
#include <hardware/gralloc.h>
@@ -49,6 +49,9 @@
static void preloadHal();
static inline GraphicBufferMapper& get() { return getInstance(); }
+ void dumpBuffer(buffer_handle_t bufferHandle, std::string& result, bool less = true) const;
+ static void dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less = true);
+
// The imported outHandle must be freed with freeBuffer when no longer
// needed. rawHandle is owned by the caller.
status_t importBuffer(buffer_handle_t rawHandle,
@@ -86,6 +89,82 @@
status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
uint32_t layerCount, uint64_t usage, bool* outSupported);
+ /**
+ * Gets the gralloc metadata associated with the buffer.
+ *
+ * These functions are supported by gralloc 4.0+.
+ */
+ status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId);
+ status_t getName(buffer_handle_t bufferHandle, std::string* outName);
+ status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth);
+ status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight);
+ status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount);
+ status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested);
+ status_t getPixelFormatFourCC(buffer_handle_t bufferHandle, uint32_t* outPixelFormatFourCC);
+ status_t getPixelFormatModifier(buffer_handle_t bufferHandle, uint64_t* outPixelFormatModifier);
+ status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage);
+ status_t getAllocationSize(buffer_handle_t bufferHandle, uint64_t* outAllocationSize);
+ status_t getProtectedContent(buffer_handle_t bufferHandle, uint64_t* outProtectedContent);
+ status_t getCompression(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+ status_t getCompression(buffer_handle_t bufferHandle, ui::Compression* outCompression);
+ status_t getInterlaced(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+ status_t getInterlaced(buffer_handle_t bufferHandle, ui::Interlaced* outInterlaced);
+ status_t getChromaSiting(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+ status_t getChromaSiting(buffer_handle_t bufferHandle, ui::ChromaSiting* outChromaSiting);
+ status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts);
+ status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace);
+ status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode);
+
+ /**
+ * Gets the default metadata for a gralloc buffer allocated with the given parameters.
+ *
+ * These functions are supported by gralloc 4.0+.
+ */
+ status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t* outPixelFormatFourCC);
+ status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outPixelFormatModifier);
+ status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outAllocationSize);
+ status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outProtectedContent);
+ status_t getDefaultCompression(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression);
+ status_t getDefaultInterlaced(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced);
+ status_t getDefaultChromaSiting(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting);
+ status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts);
+
const GrallocMapper& getGrallocMapper() const {
return reinterpret_cast<const GrallocMapper&>(*mMapper);
}
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index d7411ea..ad5ee80 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,21 +16,47 @@
#pragma once
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <system/graphics.h>
namespace android {
-// android::ui::* in this header file will alias different types as
-// the HIDL interface is updated.
+/**
+ * android::ui::* in this header file will alias different types as
+ * the HIDL and stable AIDL interfaces are updated.
+ */
namespace ui {
+/**
+ * The following HIDL types should be moved to their stable AIDL
+ * equivalents when composer moves to stable AIDL.
+ */
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
+/**
+ * Stable AIDL types
+ */
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+
+/**
+ * The following stable AIDL types below have standard platform definitions
+ * that can be extended by vendors. The extensions are not defined here
+ * because they cannot be understood by the framework.
+ */
+using ChromaSiting = aidl::android::hardware::graphics::common::ChromaSiting;
+using Compression = aidl::android::hardware::graphics::common::Compression;
+using Interlaced = aidl::android::hardware::graphics::common::Interlaced;
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 1768805..2f2229e 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_UI_RECT
#define ANDROID_UI_RECT
+#include <ostream>
+
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
@@ -214,6 +216,12 @@
}
};
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
+ *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
+ << ")";
+}
+
ANDROID_BASIC_TYPES_TRAITS(Rect)
}; // namespace android
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 79642ae..2db3b10 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <ostream>
#include <utils/Vector.h>
@@ -119,6 +120,8 @@
// returns true if the regions share the same underlying storage
bool isTriviallyEqual(const Region& region) const;
+ // returns true if the regions consist of the same rectangle sequence
+ bool hasSameRects(const Region& region) const;
/* various ways to access the rectangle list */
@@ -213,6 +216,21 @@
Region& Region::operator += (const Point& pt) {
return translateSelf(pt.x, pt.y);
}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Region& region, ::std::ostream* os) {
+ Region::const_iterator head = region.begin();
+ Region::const_iterator const tail = region.end();
+ bool first = true;
+ while (head != tail) {
+ *os << (first ? "Region(" : ", ");
+ PrintTo(*head, os);
+ head++;
+ first = false;
+ }
+ *os << ")";
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index fab2d9e..de07684 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <ostream>
#include <string>
#include <hardware/hardware.h>
@@ -63,6 +64,7 @@
bool preserveRects() const;
uint32_t getType() const;
uint32_t getOrientation() const;
+ bool operator==(const Transform& other) const;
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
@@ -115,6 +117,12 @@
mutable uint32_t mType;
};
+static inline void PrintTo(const Transform& t, ::std::ostream* os) {
+ std::string out;
+ t.dump(out, "ui::Transform");
+ *os << out;
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/include_vndk/ui/Gralloc2.h b/libs/ui/include_vndk/ui/Gralloc2.h
deleted file mode 120000
index 66098c4..0000000
--- a/libs/ui/include_vndk/ui/Gralloc2.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/Gralloc2.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index d2ad242..ff55bc2 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -38,8 +38,9 @@
"libgmock",
],
shared_libs: [
+ "libhidlbase",
"liblog",
- "libui",
+ "libui",
],
srcs: [
"GraphicBufferAllocator_test.cpp",
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index efca083..f4c0afa 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
std::cout << "Setting expected stride to " << stride << std::endl;
EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
allocate)
- .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
+ .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
}
std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
};
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 7660e9f..d62e3e2 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -32,10 +32,10 @@
~MockGrallocAllocator() override;
MOCK_METHOD(bool, isLoaded, (), (const, override));
- MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override));
+ MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
MOCK_METHOD(status_t, allocate,
- (uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index da9bb49..41fcf1b 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -91,8 +91,8 @@
rm src/*.class
# Add UnsupportedAppUsage.java to known sources.
-mkdir -p out/android/annotation
-cp ../../../../base/core/java/android/annotation/UnsupportedAppUsage.java out/android/annotation
+mkdir -p out/android/compat/annotation
+cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
pushd out > /dev/null
mkdir classes
@@ -114,7 +114,7 @@
android/opengl/GLES31.java \
android/opengl/GLES31Ext.java \
android/opengl/GLES32.java \
- android/annotation/UnsupportedAppUsage.java
+ android/compat/annotation/UnsupportedAppUsage.java
popd > /dev/null
JAVA_RESULT=$?
if [ $JAVA_RESULT -ne 0 ]; then
diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
index 12728f5..9932556 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -18,7 +18,7 @@
package android.opengl;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import android.view.SurfaceView;
diff --git a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
index c2711aa..7db1101 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
+++ b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
@@ -19,7 +19,7 @@
package android.opengl;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/** OpenGL ES 2.0
*/
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 7c061c5..ae9a348 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -82,7 +82,7 @@
// Check if the "deep touch" feature is on.
static bool deepPressEnabled() {
std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "false");
+ INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
if (flag_value == "1" || flag_value == "true") {
ALOGI("Deep press feature enabled.");
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index f51d4a0..348a7ad 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -183,6 +183,9 @@
mParameters.handlesKeyRepeat = false;
config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+
+ mParameters.doNotWakeByDefault = false;
+ config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault);
}
void KeyboardInputMapper::dumpParameters(std::string& dump) {
@@ -331,10 +334,12 @@
// Key down on external an keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
- // For internal keyboards, the key layout file should specify the policy flags for
- // each wake key individually.
+ // For internal keyboards and devices for which the default wake behavior is explicitly
+ // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
+ // wake key individually.
// TODO: Use the input device configuration to control this behavior more finely.
- if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
+ if (down && getDevice()->isExternal() && !mParameters.doNotWakeByDefault &&
+ !isMediaKey(keyCode)) {
policyFlags |= POLICY_FLAG_WAKE;
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index de2a377..badbcb2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -73,6 +73,7 @@
struct Parameters {
bool orientationAware;
bool handlesKeyRepeat;
+ bool doNotWakeByDefault;
} mParameters;
void configureParameters();
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d6624c9..1fc8217 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2580,6 +2580,84 @@
AKEYCODE_DPAD_LEFT, newDisplayId));
}
+TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) {
+ // For external devices, non-media keys will trigger wake on key down. Media keys need to be
+ // marked as WAKE in the keylayout file to trigger wake.
+ mDevice->setExternal(true);
+
+ mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) {
+ // Tv Remote key's wake behavior is prescribed by the keylayout file.
+ mDevice->setExternal(true);
+
+ mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
+ addMapperAndConfigure(mapper);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
// --- CursorInputMapperTest ---
class CursorInputMapperTest : public InputMapperTest {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 0a70165..e432b1c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -17,6 +17,7 @@
#pragma once
#include <optional>
+#include <ostream>
#include <unordered_set>
#include <renderengine/LayerSettings.h>
@@ -115,5 +116,29 @@
using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>;
+static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
+ const LayerFE::ClientCompositionTargetSettings& rhs) {
+ return lhs.clip.hasSameRects(rhs.clip) &&
+ lhs.useIdentityTransform == rhs.useIdentityTransform &&
+ lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
+ lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
+ lhs.clearRegion.hasSameRects(rhs.clearRegion);
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& settings,
+ ::std::ostream* os) {
+ *os << "ClientCompositionTargetSettings{";
+ *os << "\n .clip = \n";
+ PrintTo(settings.clip, os);
+ *os << "\n .useIdentityTransform = " << settings.useIdentityTransform;
+ *os << "\n .needsFiltering = " << settings.needsFiltering;
+ *os << "\n .isSecure = " << settings.isSecure;
+ *os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent;
+ *os << "\n .clearRegion = ";
+ PrintTo(settings.clearRegion, os);
+ *os << "\n}";
+}
+
} // namespace compositionengine
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h
deleted file mode 100644
index 6741cc9..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 364661b..9971791 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -86,6 +86,9 @@
MOCK_METHOD4(setActiveConfigWithConstraints,
status_t(DisplayId, size_t, const HWC2::VsyncPeriodChangeConstraints&,
HWC2::VsyncPeriodChangeTimeline*));
+ MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
+ MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<HWC2::ContentType>*));
+ MOCK_METHOD2(setContentType, status_t(DisplayId, HWC2::ContentType));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 88acd04..2e030a1 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -23,10 +23,8 @@
#include <compositionengine/mock/Output.h>
#include <gtest/gtest.h>
-#include "FloatRectMatcher.h"
#include "MockHWC2.h"
#include "MockHWComposer.h"
-#include "RectMatcher.h"
#include "RegionMatcher.h"
namespace android::compositionengine {
@@ -159,19 +157,19 @@
mLayerFEState.geomUsesSourceCrop = false;
const FloatRect expected{};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) {
const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) {
mLayerFEState.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));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) {
@@ -179,7 +177,7 @@
mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
const FloatRect expected{0.f, 0.f, 1080.f, 1080.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) {
@@ -218,7 +216,7 @@
mLayerFEState.geomBufferTransform = entry.buffer;
mOutputState.orientation = entry.display;
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(entry.expected)) << "entry " << i;
+ EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
}
}
@@ -226,14 +224,14 @@
mLayerFEState.geomContentCrop = Rect{0, 0, 960, 540};
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), 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));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
/*
@@ -265,50 +263,50 @@
TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) {
const Rect expected{0, 0, 1920, 1080};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
mLayerFEState.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}};
const Rect expected{0, 0, 0, 0};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
const Rect expected{100, 200, 300, 500};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
const Rect expected{1420, 100, 1720, 300};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
mLayerFEState.geomCrop = Rect{};
const Rect expected{0, 0, 1920, 1080};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) {
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
const Rect expected{0, 0, 960, 540};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
mOutputState.viewport = Rect{0, 0, 960, 540};
const Rect expected{0, 0, 960, 540};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), 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));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 37b62d8..87beb0d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -35,16 +35,19 @@
#include "CallOrderStateMachineHelper.h"
#include "MockHWC2.h"
#include "RegionMatcher.h"
-#include "TransformMatcher.h"
namespace android::compositionengine {
namespace {
using testing::_;
using testing::ByMove;
+using testing::ByRef;
using testing::DoAll;
+using testing::ElementsAreArray;
using testing::Eq;
using testing::InSequence;
+using testing::Invoke;
+using testing::IsEmpty;
using testing::Mock;
using testing::Property;
using testing::Ref;
@@ -244,7 +247,7 @@
mOutput->setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
- EXPECT_THAT(mOutput->getState().transform, TransformEq(transform));
+ EXPECT_THAT(mOutput->getState().transform, transform);
EXPECT_EQ(orientation, mOutput->getState().orientation);
EXPECT_EQ(frame, mOutput->getState().frame);
EXPECT_EQ(viewport, mOutput->getState().viewport);
@@ -2439,8 +2442,6 @@
mOutput.devOptRepaintFlash(mRefreshArgs);
}
-// TODO(b/144060211) - Add coverage
-
/*
* Output::finishFrame()
*/
@@ -2668,13 +2669,7 @@
*/
struct OutputComposeSurfacesTest : public testing::Test {
- static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
- static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::DISPLAY_P3;
-
- static const Rect kDefaultOutputFrame;
- static const Rect kDefaultOutputViewport;
- static const Rect kDefaultOutputScissor;
- static const mat4 kDefaultColorTransformMat;
+ using TestType = OutputComposeSurfacesTest;
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
@@ -2692,84 +2687,404 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- mOutput.editState().frame = kDefaultOutputFrame;
- mOutput.editState().viewport = kDefaultOutputViewport;
- mOutput.editState().scissor = kDefaultOutputScissor;
- mOutput.editState().transform = ui::Transform{kDefaultOutputOrientation};
- mOutput.editState().orientation = kDefaultOutputOrientation;
- mOutput.editState().dataspace = kDefaultOutputDataspace;
- mOutput.editState().colorTransformMatrix = kDefaultColorTransformMat;
- mOutput.editState().isSecure = true;
- mOutput.editState().needsFiltering = false;
- mOutput.editState().usesClientComposition = true;
- mOutput.editState().usesDeviceComposition = false;
+ mOutput.mState.frame = kDefaultOutputFrame;
+ mOutput.mState.viewport = kDefaultOutputViewport;
+ mOutput.mState.scissor = kDefaultOutputScissor;
+ mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
+ mOutput.mState.orientation = kDefaultOutputOrientation;
+ mOutput.mState.dataspace = kDefaultOutputDataspace;
+ mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
+ mOutput.mState.isSecure = false;
+ mOutput.mState.needsFiltering = false;
+ mOutput.mState.usesClientComposition = true;
+ mOutput.mState.usesDeviceComposition = false;
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&mOutputLayer1));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
- .WillRepeatedly(Return(&mOutputLayer2));
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(mCompositionEngine, getTimeStats())
.WillRepeatedly(ReturnRef(*mTimeStats.get()));
+ EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities())
+ .WillRepeatedly(ReturnRef(kHdrCapabilities));
}
+ struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+ auto execute() {
+ getInstance()->mReadyFence = getInstance()->mOutput.composeSurfaces(kDebugRegion);
+ return nextState<FenceCheckState>();
+ }
+ };
+
+ struct FenceCheckState : public CallOrderStateMachineHelper<TestType, FenceCheckState> {
+ void expectNoFenceWasReturned() { EXPECT_FALSE(getInstance()->mReadyFence); }
+
+ void expectAFenceWasReturned() { EXPECT_TRUE(getInstance()->mReadyFence); }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return ExecuteState::make(this); }
+
+ static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+ static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
+ static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
+ static constexpr float kDefaultMaxLuminance = 0.9f;
+ static constexpr float kDefaultAvgLuminance = 0.7f;
+ static constexpr float kDefaultMinLuminance = 0.1f;
+
+ static const Rect kDefaultOutputFrame;
+ static const Rect kDefaultOutputViewport;
+ static const Rect kDefaultOutputScissor;
+ static const mat4 kDefaultColorTransformMat;
+
+ static const Region kDebugRegion;
+ static const HdrCapabilities kHdrCapabilities;
+
StrictMock<mock::CompositionEngine> mCompositionEngine;
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
// TODO: make this is a proper mock.
std::shared_ptr<TimeStats> mTimeStats = std::make_shared<android::impl::TimeStats>();
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- StrictMock<mock::OutputLayer> mOutputLayer1;
- StrictMock<mock::OutputLayer> mOutputLayer2;
StrictMock<OutputPartialMock> mOutput;
sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+
+ std::optional<base::unique_fd> mReadyFence;
};
const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5};
-
-// TODO(b/121291683): Expand unit test coverage for composeSurfaces beyond these
-// basic tests.
+const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
+const HdrCapabilities OutputComposeSurfacesTest::
+ kHdrCapabilities{{},
+ OutputComposeSurfacesTest::kDefaultMaxLuminance,
+ OutputComposeSurfacesTest::kDefaultAvgLuminance,
+ OutputComposeSurfacesTest::kDefaultMinLuminance};
TEST_F(OutputComposeSurfacesTest, doesNothingIfNoClientComposition) {
- mOutput.editState().usesClientComposition = false;
+ mOutput.mState.usesClientComposition = false;
- Region debugRegion;
- std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(debugRegion);
- EXPECT_TRUE(readyFence);
+ verify().execute().expectAFenceWasReturned();
}
-TEST_F(OutputComposeSurfacesTest, worksIfNoClientLayersQueued) {
- const Region kDebugRegion{Rect{100, 101, 102, 103}};
+TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFails) {
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
- constexpr float kDefaultMaxLuminance = 1.0f;
- constexpr float kDefaultAvgLuminance = 0.7f;
- constexpr float kDefaultMinLuminance = 0.1f;
- HdrCapabilities HdrCapabilities{{},
- kDefaultMaxLuminance,
- kDefaultAvgLuminance,
- kDefaultMinLuminance};
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
- EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).Times(1);
+ verify().execute().expectNoFenceWasReturned();
+}
- EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillOnce(Return(true));
- EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()).WillOnce(ReturnRef(HdrCapabilities));
+TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) {
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
- EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+ .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _, _)).Times(1);
- EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)).Times(1);
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(1);
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)).Times(1);
+ verify().execute().expectAFenceWasReturned();
+}
- std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(kDebugRegion);
- EXPECT_TRUE(readyFence);
+TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) {
+ renderengine::LayerSettings r1;
+ renderengine::LayerSettings r2;
+
+ r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+ r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{r1}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(
+ Invoke([&](const Region&,
+ std::vector<renderengine::LayerSettings>& clientCompositionLayers) {
+ clientCompositionLayers.emplace_back(r2);
+ }));
+
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAreArray({r1, r2}), _, true, _, _))
+ .WillRepeatedly(Return(NO_ERROR));
+
+ verify().execute().expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
+ OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ }
+
+ struct MixedCompositionState
+ : public CallOrderStateMachineHelper<TestType, MixedCompositionState> {
+ auto ifMixedCompositionIs(bool used) {
+ getInstance()->mOutput.mState.usesDeviceComposition = used;
+ return nextState<OutputUsesHdrState>();
+ }
+ };
+
+ struct OutputUsesHdrState : public CallOrderStateMachineHelper<TestType, OutputUsesHdrState> {
+ auto andIfUsesHdr(bool used) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasWideColorGamut())
+ .WillOnce(Return(used));
+ return nextState<SkipColorTransformState>();
+ }
+ };
+
+ struct SkipColorTransformState
+ : public CallOrderStateMachineHelper<TestType, SkipColorTransformState> {
+ auto andIfSkipColorTransform(bool skip) {
+ // May be called zero or one times.
+ EXPECT_CALL(getInstance()->mOutput, getSkipColorTransform())
+ .WillRepeatedly(Return(skip));
+ return nextState<ExpectDisplaySettingsState>();
+ }
+ };
+
+ struct ExpectDisplaySettingsState
+ : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
+ auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
+ EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, true, _, _))
+ .WillOnce(Return(NO_ERROR));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return MixedCompositionState::make(this); }
+};
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) {
+ verify().ifMixedCompositionIs(true)
+ .andIfUsesHdr(true)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+ Region::INVALID_REGION, kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) {
+ verify().ifMixedCompositionIs(true)
+ .andIfUsesHdr(false)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+ Region::INVALID_REGION, kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) {
+ verify().ifMixedCompositionIs(false)
+ .andIfUsesHdr(true)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace,
+ kDefaultColorTransformMat, Region::INVALID_REGION,
+ kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) {
+ verify().ifMixedCompositionIs(false)
+ .andIfUsesHdr(false)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace,
+ kDefaultColorTransformMat, Region::INVALID_REGION,
+ kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+ usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) {
+ verify().ifMixedCompositionIs(false)
+ .andIfUsesHdr(true)
+ .andIfSkipColorTransform(true)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+ Region::INVALID_REGION, kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
+ EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ }
+
+ StrictMock<mock::OutputLayer> mOutputLayer;
+ StrictMock<mock::Layer> mLayer;
+ LayerFECompositionState mLayerFEState;
+ };
+
+ OutputComposeSurfacesTest_HandlesProtectedContent() {
+ mLayer1.mLayerFEState.hasProtectedContent = false;
+ mLayer2.mLayerFEState.hasProtectedContent = false;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _))
+ .WillRepeatedly(Return(NO_ERROR));
+ }
+
+ Layer mLayer1;
+ Layer mLayer2;
+};
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) {
+ mOutput.mState.isSecure = false;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = false;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(false));
+ EXPECT_CALL(*mRenderSurface, setProtected(false));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+ // For this test, we also check the call order of key functions.
+ InSequence seq;
+
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, setProtected(true));
+ // Must happen after setting the protected content state.
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, setProtected(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
+ OutputComposeSurfacesTest_SetsExpensiveRendering() {
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ }
+};
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
+ mOutput.mState.dataspace = kExpensiveOutputDataspace;
+
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace))
+ .WillOnce(Return(std::vector<renderengine::LayerSettings>{}));
+
+ // For this test, we also check the call order of key functions.
+ InSequence seq;
+
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+ mOutput.composeSurfaces(kDebugRegion);
}
/*
@@ -2787,7 +3102,26 @@
}
};
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
+ EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+ EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ }
+
+ StrictMock<mock::OutputLayer> mOutputLayer;
+ StrictMock<mock::Layer> mLayer;
+ StrictMock<mock::LayerFE> mLayerFE;
+ LayerFECompositionState mLayerFEState;
+ impl::OutputLayerCompositionState mOutputLayerState;
+ renderengine::LayerSettings mRELayerSettings;
+ };
+
GenerateClientCompositionRequestsTest() {
+ mOutput.mState.needsFiltering = false;
+
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
@@ -2798,222 +3132,507 @@
StrictMock<OutputPartialMock> mOutput;
};
-// TODO(b/121291683): Add more unit test coverage for generateClientCompositionRequests
+struct GenerateClientCompositionRequestsTest_ThreeLayers
+ : public GenerateClientCompositionRequestsTest {
+ GenerateClientCompositionRequestsTest_ThreeLayers() {
+ mOutput.mState.frame = kDisplayFrame;
+ mOutput.mState.viewport = kDisplayViewport;
+ mOutput.mState.scissor = kDisplayScissor;
+ mOutput.mState.transform = ui::Transform{kDisplayOrientation};
+ mOutput.mState.orientation = kDisplayOrientation;
+ mOutput.mState.needsFiltering = false;
+ mOutput.mState.isSecure = false;
-TEST_F(GenerateClientCompositionRequestsTest, worksForLandscapeModeSplitScreen) {
- // In split-screen landscape mode, the screen is rotated 90 degrees, with
- // one layer on the left covering the left side of the output, and one layer
- // on the right covering that side of the output.
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ mLayers[i].mOutputLayerState.clearClientTarget = false;
+ mLayers[i].mOutputLayerState.visibleRegion = Region(kDisplayFrame);
+ mLayers[i].mLayerFEState.isOpaque = true;
+ mLayers[i].mRELayerSettings.geometry.boundaries =
+ FloatRect{static_cast<float>(i + 1), 0.f, 0.f, 0.f};
+ mLayers[i].mRELayerSettings.source.solidColor = {1.0f, 1.0f, 1.0f};
+ mLayers[i].mRELayerSettings.alpha = 1.0f;
+ mLayers[i].mRELayerSettings.disableBlending = false;
- StrictMock<mock::OutputLayer> leftOutputLayer;
- StrictMock<mock::OutputLayer> rightOutputLayer;
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(i))
+ .WillRepeatedly(Return(&mLayers[i].mOutputLayer));
+ EXPECT_CALL(mLayers[i].mOutputLayer, requiresClientComposition())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mLayers[i].mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ }
- StrictMock<mock::Layer> leftLayer;
- StrictMock<mock::LayerFE> leftLayerFE;
- StrictMock<mock::Layer> rightLayer;
- StrictMock<mock::LayerFE> rightLayerFE;
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
+ }
- impl::OutputLayerCompositionState leftOutputLayerState;
- leftOutputLayerState.clearClientTarget = false;
- leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
+ static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+ static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
- LayerFECompositionState leftLayerFEState;
- leftLayerFEState.isOpaque = true;
+ static const Rect kDisplayFrame;
+ static const Rect kDisplayViewport;
+ static const Rect kDisplayScissor;
- const half3 leftLayerColor{1.f, 0.f, 0.f};
- renderengine::LayerSettings leftLayerRESettings;
- leftLayerRESettings.source.solidColor = leftLayerColor;
+ std::array<Layer, 3> mLayers;
+};
- impl::OutputLayerCompositionState rightOutputLayerState;
- rightOutputLayerState.clearClientTarget = false;
- rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayScissor(0, 0, 102, 202);
- LayerFECompositionState rightLayerFEState;
- rightLayerFEState.isOpaque = true;
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) {
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
- const half3 rightLayerColor{0.f, 1.f, 0.f};
- renderengine::LayerSettings rightLayerRESettings;
- rightLayerRESettings.source.solidColor = rightLayerColor;
-
- EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
- EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
- EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
- EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
- EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
- EXPECT_CALL(leftLayerFE, prepareShadowClientComposition(_, _, _))
- .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
- EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
-
- EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
- EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
- EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
- EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
- EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
- EXPECT_CALL(rightLayerFE, prepareShadowClientComposition(_, _, _))
- .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
- EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
-
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&leftOutputLayer));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
- .WillRepeatedly(Return(&rightOutputLayer));
-
- const Rect kPortraitFrame(0, 0, 1000, 2000);
- const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitScissor(0, 0, 1000, 2000);
- const uint32_t kPortraitOrientation = TR_ROT_90;
-
- mOutput.editState().frame = kPortraitFrame;
- mOutput.editState().viewport = kPortraitViewport;
- mOutput.editState().scissor = kPortraitScissor;
- mOutput.editState().transform = ui::Transform{kPortraitOrientation};
- mOutput.editState().orientation = kPortraitOrientation;
- mOutput.editState().needsFiltering = true;
- mOutput.editState().isSecure = false;
-
- constexpr bool supportsProtectedContent = false;
- Region clearRegion;
- auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
- mOutput.getState().targetDataspace);
-
- ASSERT_EQ(2u, requests.size());
- EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
- EXPECT_EQ(rightLayerColor, requests[1].source.solidColor);
-}
-
-TEST_F(GenerateClientCompositionRequestsTest, ignoresLayersThatDoNotIntersectWithViewport) {
- // Layers whose visible region does not intersect with the viewport will be
- // skipped when generating client composition request state.
-
- StrictMock<mock::OutputLayer> outputLayer;
- StrictMock<mock::Layer> layer;
- StrictMock<mock::LayerFE> layerFE;
-
- impl::OutputLayerCompositionState outputLayerState;
- outputLayerState.clearClientTarget = false;
- outputLayerState.visibleRegion = Region{Rect{3000, 0, 4000, 1000}};
-
- LayerFECompositionState layerFEState;
- layerFEState.isOpaque = true;
-
- EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
- EXPECT_CALL(outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer));
- EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
- EXPECT_CALL(outputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(outputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
- EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
- EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
-
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)).WillRepeatedly(Return(&outputLayer));
-
- const Rect kPortraitFrame(0, 0, 1000, 2000);
- const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitScissor(0, 0, 1000, 2000);
- const uint32_t kPortraitOrientation = TR_ROT_90;
-
- mOutput.editState().frame = kPortraitFrame;
- mOutput.editState().viewport = kPortraitViewport;
- mOutput.editState().scissor = kPortraitScissor;
- mOutput.editState().transform = ui::Transform{kPortraitOrientation};
- mOutput.editState().orientation = kPortraitOrientation;
- mOutput.editState().needsFiltering = true;
- mOutput.editState().isSecure = false;
-
- constexpr bool supportsProtectedContent = false;
- Region clearRegion;
- auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
- mOutput.getState().targetDataspace);
-
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
}
-TEST_F(GenerateClientCompositionRequestsTest, clearsDeviceLayesAfterFirst) {
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) {
+ mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 10, 10));
+ mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
+ mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ EXPECT_EQ(0u, requests.size());
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
+ renderengine::LayerSettings mREShadowSettings;
+ mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(_)).WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[1].mRELayerSettings));
+ EXPECT_CALL(mLayers[1].mLayerFE,
+ prepareShadowClientComposition(mLayers[1].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE,
+ prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(mREShadowSettings));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(3u, requests.size());
+ EXPECT_EQ(mLayers[1].mRELayerSettings, requests[0]);
+ EXPECT_EQ(mREShadowSettings, requests[1]);
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[2]);
+
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+
+ // Check that a timestamp was set for the layers that generated requests
+ EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
+ EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
+ EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ onlyClientComposesClientComposedLayersIfNoClearingNeeded) {
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+ mLayers[0].mOutputLayerState.clearClientTarget = false;
+ mLayers[1].mOutputLayerState.clearClientTarget = false;
+ mLayers[2].mOutputLayerState.clearClientTarget = false;
+
+ mLayers[0].mLayerFEState.isOpaque = true;
+ mLayers[1].mLayerFEState.isOpaque = true;
+ mLayers[2].mLayerFEState.isOpaque = true;
+
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::nullopt));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(1u, requests.size());
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[0]);
+
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ onlyClientComposesClientComposedLayersIfOthersAreNotOpaque) {
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+ mLayers[0].mOutputLayerState.clearClientTarget = true;
+ mLayers[1].mOutputLayerState.clearClientTarget = true;
+ mLayers[2].mOutputLayerState.clearClientTarget = true;
+
+ mLayers[0].mLayerFEState.isOpaque = false;
+ mLayers[1].mLayerFEState.isOpaque = false;
+ mLayers[2].mLayerFEState.isOpaque = false;
+
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::nullopt));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(1u, requests.size());
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[0]);
+
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) {
// If client composition is performed with some layers set to use device
// composition, device layers after the first layer (device or client) will
// clear the frame buffer if they are opaque and if that layer has a flag
// set to do so. The first layer is skipped as the frame buffer is already
// expected to be clear.
- StrictMock<mock::OutputLayer> leftOutputLayer;
- StrictMock<mock::OutputLayer> rightOutputLayer;
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
- StrictMock<mock::Layer> leftLayer;
- StrictMock<mock::LayerFE> leftLayerFE;
- StrictMock<mock::Layer> rightLayer;
- StrictMock<mock::LayerFE> rightLayerFE;
+ mLayers[0].mOutputLayerState.clearClientTarget = true;
+ mLayers[1].mOutputLayerState.clearClientTarget = true;
+ mLayers[2].mOutputLayerState.clearClientTarget = true;
- impl::OutputLayerCompositionState leftOutputLayerState;
- leftOutputLayerState.clearClientTarget = true;
- leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
+ mLayers[0].mLayerFEState.isOpaque = true;
+ mLayers[1].mLayerFEState.isOpaque = true;
+ mLayers[2].mLayerFEState.isOpaque = true;
- LayerFECompositionState leftLayerFEState;
- leftLayerFEState.isOpaque = true;
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[1].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::nullopt));
- impl::OutputLayerCompositionState rightOutputLayerState;
- rightOutputLayerState.clearClientTarget = true;
- rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(2u, requests.size());
- LayerFECompositionState rightLayerFEState;
- rightLayerFEState.isOpaque = true;
+ // The second layer is expected to be rendered as alpha=0 black with no blending
+ EXPECT_EQ(mLayers[1].mRELayerSettings.geometry.boundaries, requests[0].geometry.boundaries);
+ EXPECT_FALSE(requests[0].source.buffer.buffer);
+ EXPECT_EQ((half3{0.f, 0.f, 0.f}), requests[0].source.solidColor);
+ EXPECT_EQ(half{0.f}, requests[0].alpha);
+ EXPECT_EQ(true, requests[0].disableBlending);
- const half3 rightLayerColor{0.f, 1.f, 0.f};
- renderengine::LayerSettings rightLayerRESettings;
- rightLayerRESettings.geometry.boundaries = FloatRect{456, 0, 0, 0};
- rightLayerRESettings.source.solidColor = rightLayerColor;
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[1]);
- EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
- EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
- EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
- EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
- EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
- EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
- EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
- EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
- EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
- EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
- EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
- EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
- EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ clippedVisibleRegionUsedToGenerateRequest) {
+ mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 20, 20));
+ mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30));
+ mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000));
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&leftOutputLayer));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
- .WillRepeatedly(Return(&rightOutputLayer));
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(Rect(10, 10, 20, 20)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(Rect(0, 0, 30, 30)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(Rect(0, 0, 40, 201)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ perLayerNeedsFilteringUsedToGenerateRequests) {
+ mOutput.mState.needsFiltering = false;
+ EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ wholeOutputNeedsFilteringUsedToGenerateRequests) {
+ mOutput.mState.needsFiltering = true;
+ EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ wholeOutputSecurityUsedToGenerateRequests) {
+ mOutput.mState.isSecure = true;
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ protectedContentSupportUsedToGenerateRequests) {
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
+ accumClearRegion,
+ kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
+ // In split-screen landscape mode, the screen is rotated 90 degrees, with
+ // one layer on the left covering the left side of the output, and one layer
+ // on the right covering that side of the output.
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
const Rect kPortraitScissor(0, 0, 1000, 2000);
const uint32_t kPortraitOrientation = TR_ROT_90;
+ constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.editState().frame = kPortraitFrame;
- mOutput.editState().viewport = kPortraitViewport;
- mOutput.editState().scissor = kPortraitScissor;
- mOutput.editState().transform = ui::Transform{kPortraitOrientation};
- mOutput.editState().orientation = kPortraitOrientation;
- mOutput.editState().needsFiltering = true;
- mOutput.editState().isSecure = false;
+ mOutput.mState.frame = kPortraitFrame;
+ mOutput.mState.viewport = kPortraitViewport;
+ mOutput.mState.scissor = kPortraitScissor;
+ mOutput.mState.transform = ui::Transform{kPortraitOrientation};
+ mOutput.mState.orientation = kPortraitOrientation;
+ mOutput.mState.needsFiltering = false;
+ mOutput.mState.isSecure = true;
- constexpr bool supportsProtectedContent = false;
- Region clearRegion;
- auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
- mOutput.getState().targetDataspace);
+ Layer leftLayer;
+ Layer rightLayer;
- const half3 clearColor{0.f, 0.f, 0.f};
+ leftLayer.mOutputLayerState.clearClientTarget = false;
+ leftLayer.mOutputLayerState.visibleRegion = Region(Rect(0, 0, 1000, 1000));
+ leftLayer.mLayerFEState.isOpaque = true;
+ leftLayer.mRELayerSettings.source.solidColor = {1.f, 0.f, 0.f};
- ASSERT_EQ(1u, requests.size());
- EXPECT_EQ(456.f, requests[0].geometry.boundaries.left);
- EXPECT_EQ(clearColor, requests[0].source.solidColor);
+ rightLayer.mOutputLayerState.clearClientTarget = false;
+ rightLayer.mOutputLayerState.visibleRegion = Region(Rect(1000, 0, 2000, 1000));
+ rightLayer.mLayerFEState.isOpaque = true;
+ rightLayer.mRELayerSettings.source.solidColor = {0.f, 1.f, 0.f};
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&leftLayer.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&rightLayer.mOutputLayer));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
+ Region(Rect(0, 0, 1000, 1000)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(leftLayer.mLayerFE, prepareClientComposition(Eq(ByRef(leftLayerSettings))))
+ .WillOnce(Return(leftLayer.mRELayerSettings));
+ EXPECT_CALL(leftLayer.mLayerFE,
+ prepareShadowClientComposition(leftLayer.mRELayerSettings, kPortraitViewport,
+ kOutputDataspace))
+ .WillOnce(Return(std::nullopt));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
+ Region(Rect(1000, 0, 2000, 1000)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(rightLayer.mLayerFE, prepareClientComposition(Eq(ByRef(rightLayerSettings))))
+ .WillOnce(Return(rightLayer.mRELayerSettings));
+ EXPECT_CALL(rightLayer.mLayerFE,
+ prepareShadowClientComposition(rightLayer.mRELayerSettings, kPortraitViewport,
+ kOutputDataspace))
+ .WillOnce(Return(std::nullopt));
+
+ constexpr bool supportsProtectedContent = true;
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
+ accumClearRegion, kOutputDataspace);
+ ASSERT_EQ(2u, requests.size());
+ EXPECT_EQ(leftLayer.mRELayerSettings, requests[0]);
+ EXPECT_EQ(rightLayer.mRELayerSettings, requests[1]);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
deleted file mode 100644
index d4c76bc..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
index 5a4efa9..2adde23 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -20,24 +20,22 @@
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;
+using Region = android::Region;
- size_t expectedRectCount = 0;
- android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
- size_t actualRectCount = 0;
- android::Rect const* actualRects = arg.getArray(&actualRectCount);
+struct RegionMatcher : public testing::MatcherInterface<const Region&> {
+ const Region expected;
- if (expectedRectCount != actualRectCount) return false;
- for (size_t i = 0; i < expectedRectCount; i++) {
- if (expectedRects[i] != actualRects[i]) return false;
+ explicit RegionMatcher(const Region& expectedValue) : expected(expectedValue) {}
+
+ bool MatchAndExplain(const Region& actual, testing::MatchResultListener*) const override {
+ return expected.hasSameRects(actual);
}
- return true;
+
+ void DescribeTo(::std::ostream* os) const override { PrintTo(expected, os); }
+};
+
+testing::Matcher<const Region&> RegionEq(const Region& expected) {
+ return MakeMatcher(new RegionMatcher(expected));
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
deleted file mode 100644
index ea07bed..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index dc71128..eb032f3 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1281,6 +1281,45 @@
return error;
}
+V2_4::Error Composer::setAutoLowLatencyMode(Display display, bool on) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setAutoLowLatencyMode(display, on);
+}
+
+V2_4::Error Composer::getSupportedContentTypes(
+ Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getSupportedContentTypes(displayId,
+ [&](const auto& tmpError,
+ const auto& tmpSupportedContentTypes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outSupportedContentTypes = tmpSupportedContentTypes;
+ });
+ return error;
+}
+
+V2_4::Error Composer::setContentType(Display display, IComposerClient::ContentType contentType) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setContentType(display, contentType);
+}
+
CommandReader::~CommandReader()
{
resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 336fdd8..301f54f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -221,6 +221,13 @@
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
+
+ virtual V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) = 0;
+ virtual V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) = 0;
+ virtual V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) = 0;
};
namespace impl {
@@ -442,6 +449,12 @@
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) override;
private:
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 34254e0..66d3ce9 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -799,6 +799,26 @@
return static_cast<Error>(intError);
}
+Error Display::setAutoLowLatencyMode(bool on) const {
+ auto intError = mComposer.setAutoLowLatencyMode(mId, on);
+ return static_cast<Error>(intError);
+}
+
+Error Display::getSupportedContentTypes(std::vector<ContentType>* outSupportedContentTypes) const {
+ std::vector<Hwc2::IComposerClient::ContentType> tmpSupportedContentTypes;
+ auto intError = mComposer.getSupportedContentTypes(mId, &tmpSupportedContentTypes);
+ for (Hwc2::IComposerClient::ContentType contentType : tmpSupportedContentTypes) {
+ outSupportedContentTypes->push_back(static_cast<ContentType>(contentType));
+ }
+ return static_cast<Error>(intError);
+}
+
+Error Display::setContentType(ContentType contentType) const {
+ using Hwc2_ContentType = Hwc2::IComposerClient::ContentType;
+ auto intError = mComposer.setContentType(mId, static_cast<Hwc2_ContentType>(contentType));
+ return static_cast<Error>(intError);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 81ae3b6..59f36d0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,8 +38,6 @@
#include <unordered_set>
#include <vector>
-#include "../Scheduler/StrongTyping.h"
-
namespace android {
struct DisplayedFrameStats;
class Fence;
@@ -285,6 +283,10 @@
const std::shared_ptr<const HWC2::Display::Config>& config,
const VsyncPeriodChangeConstraints& constraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
+ [[clang::warn_unused_result]] virtual Error setAutoLowLatencyMode(bool on) const = 0;
+ [[clang::warn_unused_result]] virtual Error getSupportedContentTypes(
+ std::vector<HWC2::ContentType>*) const = 0;
+ [[clang::warn_unused_result]] virtual Error setContentType(HWC2::ContentType) const = 0;
};
namespace impl {
@@ -348,7 +350,10 @@
Error setActiveConfigWithConstraints(const std::shared_ptr<const HWC2::Display::Config>& config,
const VsyncPeriodChangeConstraints& constraints,
VsyncPeriodChangeTimeline* outTimeline) override;
-
+ Error setAutoLowLatencyMode(bool on) const override;
+ Error getSupportedContentTypes(
+ std::vector<HWC2::ContentType>* outSupportedContentTypes) const override;
+ Error setContentType(HWC2::ContentType contentType) const override;
// Other Display methods
hwc2_display_t getId() const override { return mId; }
bool isConnected() const override { return mIsConnected; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index d480f7c..9accefb 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -247,8 +247,8 @@
// the refresh period and whatever closest timestamp we have.
std::lock_guard lock(displayData.lastHwVsyncLock);
nsecs_t now = systemTime(CLOCK_MONOTONIC);
- auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
- return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
+ auto vsyncPeriodNanos = getDisplayVsyncPeriod(displayId);
+ return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
}
bool HWComposer::isConnected(DisplayId displayId) const {
@@ -291,6 +291,23 @@
return config;
}
+// Composer 2.4
+
+bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+ return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
+}
+
+nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+ nsecs_t vsyncPeriodNanos;
+ auto error = mDisplayData.at(displayId).hwcDisplay->getDisplayVsyncPeriod(&vsyncPeriodNanos);
+ if (error != HWC2::Error::None) {
+ LOG_DISPLAY_ERROR(displayId, "Failed to get Vsync Period");
+ return 0;
+ }
+
+ return vsyncPeriodNanos;
+}
+
int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, -1);
@@ -541,7 +558,9 @@
return NO_ERROR;
}
-status_t HWComposer::setActiveConfig(DisplayId displayId, size_t configId) {
+status_t HWComposer::setActiveConfigWithConstraints(
+ DisplayId displayId, size_t configId, const HWC2::VsyncPeriodChangeConstraints& constraints,
+ HWC2::VsyncPeriodChangeTimeline* outTimeline) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -550,7 +569,9 @@
return BAD_INDEX;
}
- auto error = displayData.hwcDisplay->setActiveConfig(displayData.configMap[configId]);
+ auto error =
+ displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configMap[configId],
+ constraints, outTimeline);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
@@ -701,6 +722,44 @@
return getComposer()->isUsingVrComposer();
}
+status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
+ if (error == HWC2::Error::Unsupported) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == HWC2::Error::BadParameter) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
+status_t HWComposer::getSupportedContentTypes(
+ DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
+
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+ return NO_ERROR;
+}
+
+status_t HWComposer::setContentType(DisplayId displayId, HWC2::ContentType contentType) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
+ if (error == HWC2::Error::Unsupported) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == HWC2::Error::BadParameter) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+ return NO_ERROR;
+}
+
void HWComposer::dump(std::string& result) const {
// TODO: In order to provide a dump equivalent to HWC1, we need to shadow
// all the state going into the layers. This is probably better done in
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e87c5c3..c51002c 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -102,9 +102,6 @@
// set power mode
virtual status_t setPowerMode(DisplayId displayId, int mode) = 0;
- // set active config
- virtual status_t setActiveConfig(DisplayId displayId, size_t configId) = 0;
-
// Sets a color transform to be applied to the result of composition
virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
@@ -180,6 +177,18 @@
virtual bool isUsingVrComposer() const = 0;
+ // Composer 2.4
+ virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
+ virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
+ virtual status_t setActiveConfigWithConstraints(
+ DisplayId displayId, size_t configId,
+ const HWC2::VsyncPeriodChangeConstraints& constraints,
+ HWC2::VsyncPeriodChangeTimeline* outTimeline) = 0;
+ virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+ virtual status_t getSupportedContentTypes(
+ DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) = 0;
+ virtual status_t setContentType(DisplayId displayId, HWC2::ContentType contentType) = 0;
+
// for debugging ----------------------------------------------------------
virtual void dump(std::string& out) const = 0;
@@ -232,9 +241,6 @@
// set power mode
status_t setPowerMode(DisplayId displayId, int mode) override;
- // set active config
- status_t setActiveConfig(DisplayId displayId, size_t configId) override;
-
// Sets a color transform to be applied to the result of composition
status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
@@ -305,6 +311,17 @@
bool isUsingVrComposer() const override;
+ // Composer 2.4
+ bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
+ nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
+ status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
+ const HWC2::VsyncPeriodChangeConstraints& constraints,
+ HWC2::VsyncPeriodChangeTimeline* outTimeline) override;
+ status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
+ status_t getSupportedContentTypes(DisplayId displayId,
+ std::vector<HWC2::ContentType>*) override;
+ status_t setContentType(DisplayId displayId, HWC2::ContentType) override;
+
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1d50fe1..71a6a2f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -59,11 +59,13 @@
namespace android {
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
- const scheduler::RefreshRateConfigs& refreshRateConfig)
+ const scheduler::RefreshRateConfigs& refreshRateConfig,
+ ISchedulerCallback& schedulerCallback)
: mPrimaryDispSync(new impl::DispSync("SchedulerDispSync",
sysprop::running_without_sync_framework(true))),
mEventControlThread(new impl::EventControlThread(std::move(function))),
mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+ mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(refreshRateConfig) {
using namespace sysprop;
@@ -104,10 +106,12 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs)
+ const scheduler::RefreshRateConfigs& configs,
+ ISchedulerCallback& schedulerCallback)
: mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
mSupportKernelTimer(false),
+ mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs) {}
Scheduler::~Scheduler() {
@@ -368,12 +372,7 @@
mFeatures.configId = newConfigId;
};
auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
-}
-
-void Scheduler::setSchedulerCallback(android::Scheduler::ISchedulerCallback* callback) {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- mSchedulerCallback = callback;
+ mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
void Scheduler::resetIdleTimer() {
@@ -486,7 +485,7 @@
}
}
const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- changeRefreshRate(newRefreshRate, event);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
}
HwcConfigIndexType Scheduler::calculateRefreshRateType() {
@@ -526,10 +525,36 @@
return mFeatures.configId;
}
-void Scheduler::changeRefreshRate(const RefreshRate& refreshRate, ConfigEvent configEvent) {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mSchedulerCallback) {
- mSchedulerCallback->changeRefreshRate(refreshRate, configEvent);
+void Scheduler::onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline) {
+ if (timeline.refreshRequired) {
+ mSchedulerCallback.repaintEverythingForHWC();
+ }
+
+ std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+ mLastVsyncPeriodChangeTimeline = std::make_optional(timeline);
+
+ const auto maxAppliedTime = systemTime() + MAX_VSYNC_APPLIED_TIME.count();
+ if (timeline.newVsyncAppliedTimeNanos > maxAppliedTime) {
+ mLastVsyncPeriodChangeTimeline->newVsyncAppliedTimeNanos = maxAppliedTime;
+ }
+}
+
+void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
+ bool callRepaint = false;
+ {
+ std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+ if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
+ if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
+ mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+ } else {
+ // We need to send another refresh as refreshTimeNanos is still in the future
+ callRepaint = true;
+ }
+ }
+ }
+
+ if (callRepaint) {
+ mSchedulerCallback.repaintEverythingForHWC();
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 04a8390..15277ce 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -41,22 +41,24 @@
class InjectVSyncSource;
struct DisplayStateInfo;
+class ISchedulerCallback {
+public:
+ virtual ~ISchedulerCallback() = default;
+ virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+ scheduler::RefreshRateConfigEvent) = 0;
+ virtual void repaintEverythingForHWC() = 0;
+};
+
class Scheduler {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- class ISchedulerCallback {
- public:
- virtual ~ISchedulerCallback() = default;
- virtual void changeRefreshRate(const RefreshRate&, ConfigEvent) = 0;
- };
-
// Indicates whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
- const scheduler::RefreshRateConfigs&);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
virtual ~Scheduler();
@@ -114,9 +116,6 @@
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
- // Called by Scheduler to control SurfaceFlinger operations.
- void setSchedulerCallback(ISchedulerCallback*);
-
bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
void resetIdleTimer();
@@ -131,6 +130,12 @@
// Get the appropriate refresh for current conditions.
std::optional<HwcConfigIndexType> getPreferredConfigId();
+ // Notifies the scheduler about a refresh rate timeline change.
+ void onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline);
+
+ // Notifies the scheduler when the display was refreshed
+ void onDisplayRefreshed(nsecs_t timestamp);
+
private:
friend class TestableScheduler;
@@ -142,7 +147,7 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
nsecs_t offsetThresholdForNextVsync);
@@ -165,8 +170,6 @@
void setVsyncPeriod(nsecs_t period);
HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
- // Acquires a lock and calls the ChangeRefreshRateCallback with given parameters.
- void changeRefreshRate(const RefreshRate&, ConfigEvent);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -203,8 +206,7 @@
// Timer used to monitor display power mode.
std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
- std::mutex mCallbackLock;
- ISchedulerCallback* mSchedulerCallback GUARDED_BY(mCallbackLock) = nullptr;
+ ISchedulerCallback& mSchedulerCallback;
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
@@ -223,6 +225,11 @@
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
+
+ std::mutex mVsyncTimelineLock;
+ std::optional<HWC2::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
+ GUARDED_BY(mVsyncTimelineLock);
+ static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 2394ed2..fb4f315 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -85,7 +85,7 @@
};
if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
- ALOGW("Failed to set timerfd");
+ ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 3894992..3b99a58 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -23,6 +23,7 @@
#include <utils/Trace.h>
#include <algorithm>
#include <chrono>
+#include <sstream>
#include "SchedulerUtils.h"
namespace android::scheduler {
@@ -153,12 +154,24 @@
}
auto const oldest = *std::min_element(timestamps.begin(), timestamps.end());
- auto const ordinalRequest = (timePoint - oldest + slope) / slope;
+
+ // See b/145667109, the ordinal calculation must take into account the intercept.
+ auto const zeroPoint = oldest + intercept;
+ auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
auto const prediction = (ordinalRequest * slope) + intercept + oldest;
- ALOGV("prediction made from: %" PRId64 " prediction: %" PRId64 " (+%" PRId64 ") slope: %" PRId64
- " intercept: %" PRId64,
- timePoint, prediction, prediction - timePoint, slope, intercept);
+ auto const printer = [&, slope = slope, intercept = intercept] {
+ std::stringstream str;
+ str << "prediction made from: " << timePoint << "prediction: " << prediction << " (+"
+ << prediction - timePoint << ") slope: " << slope << " intercept: " << intercept
+ << "oldestTS: " << oldest << " ordinal: " << ordinalRequest;
+ return str.str();
+ };
+
+ ALOGV("%s", printer().c_str());
+ LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
+ printer().c_str());
+
return prediction;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9f4dd2b..2dd4495 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1004,11 +1004,25 @@
LOG_ALWAYS_FATAL_IF(!displayId);
ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.fps);
- getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId.value());
- // we need to submit an empty frame to HWC to start the process
+ // TODO(b/142753666) use constrains
+ HWC2::VsyncPeriodChangeConstraints constraints;
+ constraints.desiredTimeNanos = systemTime();
+ constraints.seamlessRequired = false;
+
+ HWC2::VsyncPeriodChangeTimeline outTimeline;
+ auto status =
+ getHwComposer().setActiveConfigWithConstraints(*displayId,
+ mUpcomingActiveConfig.configId.value(),
+ constraints, &outTimeline);
+ if (status != NO_ERROR) {
+ LOG_ALWAYS_FATAL("setActiveConfigWithConstraints failed: %d", status);
+ return false;
+ }
+
+ mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+ // Scheduler will submit an empty frame to HWC if needed.
mCheckPendingFence = true;
- mEventQueue->invalidate();
return false;
}
@@ -1097,6 +1111,86 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ Mutex::Autolock _l(mStateLock);
+
+ if (!displayToken) {
+ ALOGE("getAutoLowLatencyModeSupport() failed. Missing display token.");
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("getAutoLowLatencyModeSupport() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ *outSupport = getHwComposer().hasDisplayCapability(displayId,
+ HWC2::DisplayCapability::AutoLowLatencyMode);
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
+ postMessageAsync(new LambdaMessage([=] { setAutoLowLatencyModeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on) {
+ if (!displayToken) {
+ ALOGE("setAutoLowLatencyMode() failed. Missing display token.");
+ return;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setAutoLowLatencyMode() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return;
+ }
+
+ getHwComposer().setAutoLowLatencyMode(*displayId, on);
+}
+
+status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ Mutex::Autolock _l(mStateLock);
+
+ if (!displayToken) {
+ ALOGE("getGameContentTypeSupport() failed. Missing display token.");
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("getGameContentTypeSupport() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+
+ std::vector<HWC2::ContentType> outSupportedContentTypes;
+ getHwComposer().getSupportedContentTypes(*displayId, &outSupportedContentTypes);
+ *outSupport = std::find(outSupportedContentTypes.begin(), outSupportedContentTypes.end(),
+ HWC2::ContentType::Game) != outSupportedContentTypes.end();
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
+ postMessageAsync(new LambdaMessage([=] { setGameContentTypeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on) {
+ if (!displayToken) {
+ ALOGE("setGameContentType() failed. Missing display token.");
+ return;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setGameContentType() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return;
+ }
+
+ const HWC2::ContentType type = on ? HWC2::ContentType::Game : HWC2::ContentType::None;
+ getHwComposer().setContentType(*displayId, type);
+}
+
status_t SurfaceFlinger::clearAnimationFrameStats() {
Mutex::Autolock _l(mStateLock);
mAnimFrameTracker.clearStats();
@@ -1361,8 +1455,7 @@
return 0;
}
- const auto config = getHwComposer().getActiveConfig(*displayId);
- return config ? config->getVsyncPeriod() : 0;
+ return getHwComposer().getDisplayVsyncPeriod(*displayId);
}
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1448,9 +1541,13 @@
}
void SurfaceFlinger::onVsyncPeriodTimingChangedReceived(
- int32_t /*sequenceId*/, hwc2_display_t /*display*/,
- const hwc_vsync_period_change_timeline_t& /*updatedTimeline*/) {
- // TODO(b/142753004): use timeline when changing refresh rate
+ int32_t sequenceId, hwc2_display_t /*display*/,
+ const hwc_vsync_period_change_timeline_t& updatedTimeline) {
+ Mutex::Autolock lock(mStateLock);
+ if (sequenceId != getBE().mComposerSequenceId) {
+ return;
+ }
+ mScheduler->onNewVsyncPeriodChangeTimeline(updatedTimeline);
}
void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
@@ -1761,11 +1858,17 @@
mGeometryInvalid = false;
+ // Store the present time just before calling to the composition engine so we could notify
+ // the scheduler.
+ const auto presentTime = systemTime();
+
mCompositionEngine->present(refreshArgs);
mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
// Reset the frame start time now that we've recorded this frame.
mFrameStartTime = 0;
+ mScheduler->onDisplayRefreshed(presentTime);
+
postFrame();
postComposition();
@@ -2006,6 +2109,7 @@
// Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
if (ATRACE_ENABLED()) {
+ // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
}
}
@@ -2551,7 +2655,7 @@
// start the EventThread
mScheduler =
getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
- *mRefreshRateConfigs);
+ *mRefreshRateConfigs, *this);
mAppConnectionHandle =
mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
mPhaseOffsets->getOffsetThresholdForNextVsync(),
@@ -2570,8 +2674,6 @@
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
-
- mScheduler->setSchedulerCallback(this);
}
void SurfaceFlinger::commitTransaction()
@@ -4316,8 +4418,8 @@
" refresh-rate : %f fps\n"
" x-dpi : %f\n"
" y-dpi : %f\n",
- 1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(),
- activeConfig->getDpiY());
+ 1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId),
+ activeConfig->getDpiX(), activeConfig->getDpiY());
}
StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -4417,6 +4519,10 @@
case SET_DESIRED_DISPLAY_CONFIG_SPECS:
case GET_DESIRED_DISPLAY_CONFIG_SPECS:
case SET_ACTIVE_COLOR_MODE:
+ case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
+ case SET_AUTO_LOW_LATENCY_MODE:
+ case GET_GAME_CONTENT_TYPE_SUPPORT:
+ case SET_GAME_CONTENT_TYPE:
case INJECT_VSYNC:
case SET_POWER_MODE:
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
@@ -5438,6 +5544,27 @@
void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
const std::vector<int32_t>& allowedConfigs) {
if (!display->isPrimary()) {
+ // TODO(b/144711714): For non-primary displays we should be able to set an active config
+ // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
+ // it should go thru setDesiredActiveConfig, similar to primary display.
+ ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
+ const auto displayId = display->getId();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+
+ HWC2::VsyncPeriodChangeConstraints constraints;
+ constraints.desiredTimeNanos = systemTime();
+ constraints.seamlessRequired = false;
+
+ HWC2::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
+ getHwComposer().setActiveConfigWithConstraints(*displayId, allowedConfigs[0], constraints,
+ &timeline);
+ if (timeline.refreshRequired) {
+ repaintEverythingForHWC();
+ }
+
+ auto configId = HwcConfigIndexType(allowedConfigs[0]);
+ display->setActiveConfig(configId);
+ mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId);
return;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ae3f2d7..be47e94 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -176,7 +176,7 @@
public ClientCache::ErasedRecipient,
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
- private Scheduler::ISchedulerCallback {
+ private ISchedulerCallback {
public:
SurfaceFlingerBE& getBE() { return mBE; }
const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -273,9 +273,6 @@
// force full composition on all displays
void repaintEverything();
- // force full composition on all displays without resetting the scheduler idle timer.
- void repaintEverythingForHWC();
-
surfaceflinger::Factory& getFactory() { return mFactory; }
// The CompositionEngine encapsulates all composition related interfaces and actions.
@@ -452,6 +449,12 @@
ui::DisplayPrimaries &primaries);
ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+ status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+ bool* outSupported) const override;
+ void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
+ status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
+ bool* outSupported) const override;
+ void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
status_t setActiveConfig(const sp<IBinder>& displayToken, int id) override;
status_t clearAnimationFrameStats() override;
@@ -519,9 +522,11 @@
const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
/* ------------------------------------------------------------------------
- * Scheduler::ISchedulerCallback
+ * ISchedulerCallback
*/
void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+ // force full composition on all displays without resetting the scheduler idle timer.
+ void repaintEverythingForHWC() override;
/* ------------------------------------------------------------------------
* Message handling
*/
@@ -564,6 +569,10 @@
void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
const std::vector<int32_t>& allowedConfigs)
REQUIRES(mStateLock);
+ // called on the main thread in response to setAutoLowLatencyMode()
+ void setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on);
+ // called on the main thread in response to setGameContentType()
+ void setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on);
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 4f439da..bd4cdba 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -64,8 +64,9 @@
}
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
- SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs) {
- return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs);
+ SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
+ ISchedulerCallback& schedulerCallback) {
+ return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback);
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 42bb177..1a24448 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -32,7 +32,8 @@
std::unique_ptr<MessageQueue> createMessageQueue() override;
std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override;
std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&) override;
+ const scheduler::RefreshRateConfigs&,
+ ISchedulerCallback&) override;
std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 20784d2..0db941d 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -40,6 +40,7 @@
class HWComposer;
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
+class ISchedulerCallback;
class Layer;
class MessageQueue;
class Scheduler;
@@ -75,7 +76,8 @@
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&) = 0;
+ const scheduler::RefreshRateConfigs&,
+ ISchedulerCallback&) = 0;
virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 6d79615..0c370a6 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -760,6 +760,22 @@
return V2_4::Error::UNSUPPORTED;
}
+V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
+ ALOGV("setAutoLowLatencyMode");
+ return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getSupportedContentTypes(
+ Display, std::vector<IComposerClient::ContentType>*) {
+ ALOGV("getSupportedContentTypes");
+ return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
+ ALOGV("setContentType");
+ return V2_4::Error::UNSUPPORTED;
+}
+
//////////////////////////////////////////////////////////////////
void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index 2a08b9b..f9ff2bf 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -246,6 +246,11 @@
Display display, Config config,
const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display display,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
void setClient(ComposerClient* client);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 40c00c4..fa095aa 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -26,17 +26,17 @@
namespace android {
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
- : Scheduler([](bool) {}, configs) {
+ : Scheduler([](bool) {}, configs, *this) {
mLayerHistory.emplace();
}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs)
- : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {
+ : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this) {
mLayerHistory.emplace();
}
@@ -68,6 +68,10 @@
mutablePrimaryDispSync().reset();
mConnections.clear();
}
+
+private:
+ void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+ void repaintEverythingForHWC() override {}
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b2210ec..facf142 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -84,7 +84,8 @@
}
std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
- const scheduler::RefreshRateConfigs&) override {
+ const scheduler::RefreshRateConfigs&,
+ ISchedulerCallback&) override {
return nullptr;
}
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index d0c8090..4cb6a38 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -319,4 +319,36 @@
}
}
+// See b/145667109, and comment in prod code under test.
+TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
+ std::vector<nsecs_t> const simulatedVsyncs{
+ 158929578733000,
+ 158929306806205, // oldest TS in ringbuffer
+ 158929650879052,
+ 158929661969209,
+ 158929684198847,
+ 158929695268171,
+ 158929706370359,
+ };
+ auto const idealPeriod = 11111111;
+ auto const expectedPeriod = 11113500;
+ auto const expectedIntercept = -395335;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+
+ // (timePoint - oldestTS) % expectedPeriod works out to be: 395334
+ // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
+ // so failure to account for the offset will floor the ordinal to 38, which was in the past.
+ auto const timePoint = 158929728723871;
+ auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
+ EXPECT_THAT(prediction, Ge(timePoint));
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2453ccb..0f9dd5b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -130,6 +130,10 @@
MOCK_METHOD4(setActiveConfigWithConstraints,
V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
VsyncPeriodChangeTimeline*));
+ MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
+ MOCK_METHOD2(getSupportedContentTypes,
+ V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
+ MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
};
} // namespace mock