Merge "Export libui_headers as vndk header only library" into oc-mr1-dev am: 0d70e447b5
am: 5e11d48a1f
Change-Id: Ia0c6cc4eddd55f7524cba5c5d46bbde9d635f1bb
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 630c1f3..443dce6 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -5,13 +5,13 @@
srcs: ["installd_utils_test.cpp"],
shared_libs: [
"libbase",
- "liblog",
"libutils",
"libcutils",
],
static_libs: [
- "libinstalld",
"libdiskusage",
+ "libinstalld",
+ "liblog",
],
}
@@ -23,14 +23,14 @@
"libbase",
"libbinder",
"libcutils",
- "liblog",
- "liblogwrap",
"libselinux",
"libutils",
],
static_libs: [
- "libinstalld",
"libdiskusage",
+ "libinstalld",
+ "liblog",
+ "liblogwrap",
],
}
@@ -42,13 +42,13 @@
"libbase",
"libbinder",
"libcutils",
- "liblog",
- "liblogwrap",
"libselinux",
"libutils",
],
static_libs: [
- "libinstalld",
"libdiskusage",
+ "libinstalld",
+ "liblog",
+ "liblogwrap",
],
}
diff --git a/include/android/sensor.h b/include/android/sensor.h
index a88733c..2db0ee7 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -197,7 +197,7 @@
* A sensor event.
*/
-/* NOTE: Must match hardware/sensors.h */
+/* NOTE: changes to these structs have to be backward compatible */
typedef struct ASensorVector {
union {
float v[3];
@@ -259,7 +259,7 @@
};
} AAdditionalInfoEvent;
-/* NOTE: Must match hardware/sensors.h */
+/* NOTE: changes to this struct has to be backward compatible */
typedef struct ASensorEvent {
int32_t version; /* sizeof(struct ASensorEvent) */
int32_t sensor;
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 795f575..ffa1614 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -264,6 +264,40 @@
Movement mMovements[HISTORY_SIZE];
};
+class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy {
+public:
+ ImpulseVelocityTrackerStrategy();
+ virtual ~ImpulseVelocityTrackerStrategy();
+
+ virtual void clear();
+ virtual void clearPointers(BitSet32 idBits);
+ virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const VelocityTracker::Position* positions);
+ virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
+
+private:
+ // Sample horizon.
+ // We don't use too much history by default since we want to react to quick
+ // changes in direction.
+ static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms
+
+ // Number of samples to keep.
+ static constexpr size_t HISTORY_SIZE = 20;
+
+ struct Movement {
+ nsecs_t eventTime;
+ BitSet32 idBits;
+ VelocityTracker::Position positions[MAX_POINTERS];
+
+ inline const VelocityTracker::Position& getPosition(uint32_t id) const {
+ return positions[idBits.getIndexOfBit(id)];
+ }
+ };
+
+ size_t mIndex;
+ Movement mMovements[HISTORY_SIZE];
+};
+
} // namespace android
#endif // _LIBINPUT_VELOCITY_TRACKER_H
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 9b06e63..573f685 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -45,6 +45,8 @@
output.writeInt32(overrideScalingMode);
output.writeStrongBinder(IInterface::asBinder(barrierGbp));
output.writeStrongBinder(relativeLayerHandle);
+ output.writeStrongBinder(parentHandleForChild);
+ output.writeStrongBinder(childHandle);
output.write(transparentRegion);
return NO_ERROR;
}
@@ -77,6 +79,8 @@
barrierGbp =
interface_cast<IGraphicBufferProducer>(input.readStrongBinder());
relativeLayerHandle = input.readStrongBinder();
+ parentHandleForChild = input.readStrongBinder();
+ childHandle = input.readStrongBinder();
input.read(transparentRegion);
return NO_ERROR;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 5b1c599..6c9d88b 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1509,6 +1509,12 @@
return NO_ERROR;
}
+android_dataspace_t Surface::getBuffersDataSpace() {
+ ALOGV("Surface::getBuffersDataSpace");
+ Mutex::Autolock lock(mMutex);
+ return mDataSpace;
+}
+
void Surface::freeAllBuffers() {
for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
mSlots[i].buffer = 0;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 7ae2672..b0ae7e0 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -176,6 +176,9 @@
status_t reparentChildren(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id,
const sp<IBinder>& newParentHandle);
+ status_t reparentChild(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id, const sp<IBinder>& newParentHandle,
+ const sp<IBinder>& childHandle);
status_t detachChildren(const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id);
status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client,
@@ -493,6 +496,21 @@
return NO_ERROR;
}
+status_t Composer::reparentChild(const sp<SurfaceComposerClient>& client,
+ const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle,
+ const sp<IBinder>& childHandle) {
+ Mutex::Autolock lock(mLock);
+ layer_state_t* s = getLayerStateLocked(client, id);
+ if (!s) {
+ return BAD_INDEX;
+ }
+ s->what |= layer_state_t::eReparentChild;
+ s->parentHandleForChild = newParentHandle;
+ s->childHandle = childHandle;
+ return NO_ERROR;
+}
+
status_t Composer::detachChildren(
const sp<SurfaceComposerClient>& client,
const sp<IBinder>& id) {
@@ -831,6 +849,11 @@
return getComposer().reparentChildren(this, id, newParentHandle);
}
+status_t SurfaceComposerClient::reparentChild(const sp<IBinder>& id,
+ const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle) {
+ return getComposer().reparentChild(this, id, newParentHandle, childHandle);
+}
+
status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) {
return getComposer().detachChildren(this, id);
}
diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index 58bd273..b9c5ef9 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -191,6 +191,13 @@
return mClient->reparentChildren(mHandle, newParentHandle);
}
+status_t SurfaceControl::reparentChild(const sp<IBinder>& newParentHandle,
+ const sp<IBinder>& childHandle) {
+ status_t err = validate();
+ if (err < 0) return err;
+ return mClient->reparentChild(mHandle, newParentHandle, childHandle);
+}
+
status_t SurfaceControl::detachChildren() {
status_t err = validate();
if (err < 0) return err;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 55dd6bf..3fe29d9 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -281,6 +281,8 @@
// detachNextBuffer, or attachBuffer call.
status_t getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out);
+ android_dataspace_t getBuffersDataSpace();
+
protected:
enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS };
enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 };
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 145c059..6e2cb83 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -161,6 +161,8 @@
const sp<Surface>& handle, uint64_t frameNumber);
status_t reparentChildren(const sp<IBinder>& id,
const sp<IBinder>& newParentHandle);
+ status_t reparentChild(const sp<IBinder>& id, const sp<IBinder>& newParentHandle,
+ const sp<IBinder>& childHandle);
status_t detachChildren(const sp<IBinder>& id);
status_t setOverrideScalingMode(const sp<IBinder>& id,
int32_t overrideScalingMode);
diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h
index c15209d..d8b67ef 100644
--- a/libs/gui/include/gui/SurfaceControl.h
+++ b/libs/gui/include/gui/SurfaceControl.h
@@ -124,6 +124,12 @@
// Reparents all children of this layer to the new parent handle.
status_t reparentChildren(const sp<IBinder>& newParentHandle);
+ // Reparents a specified child from this layer to the new parent handle.
+ // The child, parent, and new parent must all have the same client.
+ // This can be used instead of reparentChildren if the caller wants to
+ // only re-parent specific children.
+ status_t reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle);
+
// Detaches all child surfaces (and their children recursively)
// from their SurfaceControl.
// The child SurfaceControl's will not throw exceptions or return errors,
diff --git a/libs/gui/include/private/gui/LayerState.h b/libs/gui/include/private/gui/LayerState.h
index 307c764..4f73e04 100644
--- a/libs/gui/include/private/gui/LayerState.h
+++ b/libs/gui/include/private/gui/LayerState.h
@@ -59,7 +59,8 @@
eGeometryAppliesWithResize = 0x00001000,
eReparentChildren = 0x00002000,
eDetachChildren = 0x00004000,
- eRelativeLayerChanged = 0x00008000
+ eRelativeLayerChanged = 0x00008000,
+ eReparentChild = 0x00010000
};
layer_state_t()
@@ -107,6 +108,9 @@
sp<IBinder> relativeLayerHandle;
+ sp<IBinder> parentHandleForChild;
+ sp<IBinder> childHandle;
+
// non POD must be last. see write/read
Region transparentRegion;
};
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 62acea3..a914408 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -141,6 +141,11 @@
}
VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
+ if (!strcmp("impulse", strategy)) {
+ // Physical model of pushing an object. Quality: VERY GOOD.
+ // Works with duplicate coordinates, unclean finger liftoff.
+ return new ImpulseVelocityTrackerStrategy();
+ }
if (!strcmp("lsq1", strategy)) {
// 1st order least squares. Quality: POOR.
// Frequently underfits the touch data especially when the finger accelerates
@@ -352,9 +357,6 @@
// --- LeastSquaresVelocityTrackerStrategy ---
-const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON;
-const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE;
-
LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(
uint32_t degree, Weighting weighting) :
mDegree(degree), mWeighting(weighting) {
@@ -863,10 +865,6 @@
// --- LegacyVelocityTrackerStrategy ---
-const nsecs_t LegacyVelocityTrackerStrategy::HORIZON;
-const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE;
-const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION;
-
LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() {
clear();
}
@@ -979,4 +977,192 @@
return true;
}
+// --- ImpulseVelocityTrackerStrategy ---
+
+ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy() {
+ clear();
+}
+
+ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() {
+}
+
+void ImpulseVelocityTrackerStrategy::clear() {
+ mIndex = 0;
+ mMovements[0].idBits.clear();
+}
+
+void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) {
+ BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value);
+ mMovements[mIndex].idBits = remainingIdBits;
+}
+
+void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
+ const VelocityTracker::Position* positions) {
+ if (++mIndex == HISTORY_SIZE) {
+ mIndex = 0;
+ }
+
+ Movement& movement = mMovements[mIndex];
+ movement.eventTime = eventTime;
+ movement.idBits = idBits;
+ uint32_t count = idBits.count();
+ for (uint32_t i = 0; i < count; i++) {
+ movement.positions[i] = positions[i];
+ }
+}
+
+/**
+ * Calculate the total impulse provided to the screen and the resulting velocity.
+ *
+ * The touchscreen is modeled as a physical object.
+ * Initial condition is discussed below, but for now suppose that v(t=0) = 0
+ *
+ * The kinetic energy of the object at the release is E=0.5*m*v^2
+ * Then vfinal = sqrt(2E/m). The goal is to calculate E.
+ *
+ * The kinetic energy at the release is equal to the total work done on the object by the finger.
+ * The total work W is the sum of all dW along the path.
+ *
+ * dW = F*dx, where dx is the piece of path traveled.
+ * Force is change of momentum over time, F = dp/dt = m dv/dt.
+ * Then substituting:
+ * dW = m (dv/dt) * dx = m * v * dv
+ *
+ * Summing along the path, we get:
+ * W = sum(dW) = sum(m * v * dv) = m * sum(v * dv)
+ * Since the mass stays constant, the equation for final velocity is:
+ * vfinal = sqrt(2*sum(v * dv))
+ *
+ * Here,
+ * dv : change of velocity = (v[i+1]-v[i])
+ * dx : change of distance = (x[i+1]-x[i])
+ * dt : change of time = (t[i+1]-t[i])
+ * v : instantaneous velocity = dx/dt
+ *
+ * The final formula is:
+ * vfinal = sqrt(2) * sqrt(sum((v[i]-v[i-1])*|v[i]|)) for all i
+ * The absolute value is needed to properly account for the sign. If the velocity over a
+ * particular segment descreases, then this indicates braking, which means that negative
+ * work was done. So for two positive, but decreasing, velocities, this contribution would be
+ * negative and will cause a smaller final velocity.
+ *
+ * Initial condition
+ * There are two ways to deal with initial condition:
+ * 1) Assume that v(0) = 0, which would mean that the screen is initially at rest.
+ * This is not entirely accurate. We are only taking the past X ms of touch data, where X is
+ * currently equal to 100. However, a touch event that created a fling probably lasted for longer
+ * than that, which would mean that the user has already been interacting with the touchscreen
+ * and it has probably already been moving.
+ * 2) Assume that the touchscreen has already been moving at a certain velocity, calculate this
+ * initial velocity and the equivalent energy, and start with this initial energy.
+ * Consider an example where we have the following data, consisting of 3 points:
+ * time: t0, t1, t2
+ * x : x0, x1, x2
+ * v : 0 , v1, v2
+ * Here is what will happen in each of these scenarios:
+ * 1) By directly applying the formula above with the v(0) = 0 boundary condition, we will get
+ * vfinal = sqrt(2*(|v1|*(v1-v0) + |v2|*(v2-v1))). This can be simplified since v0=0
+ * vfinal = sqrt(2*(|v1|*v1 + |v2|*(v2-v1))) = sqrt(2*(v1^2 + |v2|*(v2 - v1)))
+ * since velocity is a real number
+ * 2) If we treat the screen as already moving, then it must already have an energy (per mass)
+ * equal to 1/2*v1^2. Then the initial energy should be 1/2*v1*2, and only the second segment
+ * will contribute to the total kinetic energy (since we can effectively consider that v0=v1).
+ * This will give the following expression for the final velocity:
+ * vfinal = sqrt(2*(1/2*v1^2 + |v2|*(v2-v1)))
+ * This analysis can be generalized to an arbitrary number of samples.
+ *
+ *
+ * Comparing the two equations above, we see that the only mathematical difference
+ * is the factor of 1/2 in front of the first velocity term.
+ * This boundary condition would allow for the "proper" calculation of the case when all of the
+ * samples are equally spaced in time and distance, which should suggest a constant velocity.
+ *
+ * Note that approach 2) is sensitive to the proper ordering of the data in time, since
+ * the boundary condition must be applied to the oldest sample to be accurate.
+ */
+static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count) {
+ // The input should be in reversed time order (most recent sample at index i=0)
+ // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function
+ static constexpr float NANOS_PER_SECOND = 1E-9;
+ static constexpr float sqrt2 = 1.41421356237;
+
+ if (count < 2) {
+ return 0; // if 0 or 1 points, velocity is zero
+ }
+ if (t[1] > t[0]) { // Algorithm will still work, but not perfectly
+ ALOGE("Samples provided to calculateImpulseVelocity in the wrong order");
+ }
+ if (count == 2) { // if 2 points, basic linear calculation
+ if (t[1] == t[0]) {
+ ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]);
+ return 0;
+ }
+ return (x[1] - x[0]) / (NANOS_PER_SECOND * (t[1] - t[0]));
+ }
+ // Guaranteed to have at least 3 points here
+ float work = 0;
+ float vprev, vcurr; // v[i-1] and v[i], respectively
+ vprev = 0;
+ for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time
+ if (t[i] == t[i-1]) {
+ ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]);
+ continue;
+ }
+ vcurr = (x[i] - x[i-1]) / (NANOS_PER_SECOND * (t[i] - t[i-1]));
+ work += (vcurr - vprev) * fabsf(vcurr);
+ if (i == count - 1) {
+ work *= 0.5; // initial condition, case 2) above
+ }
+ vprev = vcurr;
+ }
+ return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2;
+}
+
+bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id,
+ VelocityTracker::Estimator* outEstimator) const {
+ outEstimator->clear();
+
+ // Iterate over movement samples in reverse time order and collect samples.
+ float x[HISTORY_SIZE];
+ float y[HISTORY_SIZE];
+ nsecs_t time[HISTORY_SIZE];
+ size_t m = 0; // number of points that will be used for fitting
+ size_t index = mIndex;
+ const Movement& newestMovement = mMovements[mIndex];
+ do {
+ const Movement& movement = mMovements[index];
+ if (!movement.idBits.hasBit(id)) {
+ break;
+ }
+
+ nsecs_t age = newestMovement.eventTime - movement.eventTime;
+ if (age > HORIZON) {
+ break;
+ }
+
+ const VelocityTracker::Position& position = movement.getPosition(id);
+ x[m] = position.x;
+ y[m] = position.y;
+ time[m] = movement.eventTime;
+ index = (index == 0 ? HISTORY_SIZE : index) - 1;
+ } while (++m < HISTORY_SIZE);
+
+ if (m == 0) {
+ return false; // no data
+ }
+ outEstimator->xCoeff[0] = 0;
+ outEstimator->yCoeff[0] = 0;
+ outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m);
+ outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m);
+ outEstimator->xCoeff[2] = 0;
+ outEstimator->yCoeff[2] = 0;
+ outEstimator->time = newestMovement.eventTime;
+ outEstimator->degree = 2; // similar results to 2nd degree fit
+ outEstimator->confidence = 1;
+#if DEBUG_STRATEGY
+ ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+#endif
+ return true;
+}
+
} // namespace android
diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS
new file mode 100644
index 0000000..6a38a1f
--- /dev/null
+++ b/libs/sensor/OWNERS
@@ -0,0 +1,2 @@
+ashutoshj@google.com
+pengxu@google.com
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index f9f87ff..6b86433 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -429,7 +429,7 @@
if (!status) {
ALOGE(
"ProducerQueue::FreeAllBuffers: Failed to remove buffer at "
- "slot=%d.",
+ "slot=%zu.",
slot);
last_error = status.error_status();
}
diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS
new file mode 100644
index 0000000..6a38a1f
--- /dev/null
+++ b/services/sensorservice/OWNERS
@@ -0,0 +1,2 @@
+ashutoshj@google.com
+pengxu@google.com
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 7d9b0b7..da3b275 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -223,8 +223,13 @@
}
void SensorDevice::autoDisable(void *ident, int handle) {
- Info& info( mActivationCount.editValueFor(handle) );
Mutex::Autolock _l(mLock);
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ ALOGW("Handle %d cannot be found in activation record", handle);
+ return;
+ }
+ Info& info(mActivationCount.editValueAt(activationIndex));
info.removeBatchParamsForIdent(ident);
}
@@ -235,7 +240,12 @@
bool actuateHardware = false;
Mutex::Autolock _l(mLock);
- Info& info( mActivationCount.editValueFor(handle) );
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ ALOGW("Handle %d cannot be found in activation record", handle);
+ return BAD_VALUE;
+ }
+ Info& info(mActivationCount.editValueAt(activationIndex));
ALOGD_IF(DEBUG_CONNECTIONS,
"SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",
@@ -329,7 +339,12 @@
ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
Mutex::Autolock _l(mLock);
- Info& info(mActivationCount.editValueFor(handle));
+ ssize_t activationIndex = mActivationCount.indexOfKey(handle);
+ if (activationIndex < 0) {
+ ALOGW("Handle %d cannot be found in activation record", handle);
+ return BAD_VALUE;
+ }
+ Info& info(mActivationCount.editValueAt(activationIndex));
if (info.batchParams.indexOfKey(ident) < 0) {
BatchParams params(samplingPeriodNs, maxBatchReportLatencyNs);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 248ef53..0244c1b 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -28,6 +28,7 @@
#include <utils/RefBase.h>
#include <utils/Log.h>
+#include <ui/DebugUtils.h>
#include <ui/DisplayInfo.h>
#include <ui/PixelFormat.h>
@@ -617,6 +618,7 @@
void DisplayDevice::dump(String8& result) const {
const Transform& tr(mGlobalTransform);
+ ANativeWindow* const window = mNativeWindow.get();
EGLint redSize, greenSize, blueSize, alphaSize;
eglGetConfigAttrib(mDisplay, mConfig, EGL_RED_SIZE, &redSize);
eglGetConfigAttrib(mDisplay, mConfig, EGL_GREEN_SIZE, &greenSize);
@@ -626,9 +628,9 @@
result.appendFormat(" type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p "
"(%d:%d:%d:%d), orient=%2d (type=%08x), "
"flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n",
- mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight,
- mNativeWindow.get(), redSize, greenSize, blueSize, alphaSize, mOrientation,
- tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
+ mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight, window,
+ redSize, greenSize, blueSize, alphaSize, mOrientation, tr.getType(),
+ getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig,
mVisibleLayersSortedByZ.size());
result.appendFormat(" v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d],"
"transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n",
@@ -636,6 +638,9 @@
mFrame.left, mFrame.top, mFrame.right, mFrame.bottom, mScissor.left,
mScissor.top, mScissor.right, mScissor.bottom, tr[0][0], tr[1][0], tr[2][0],
tr[0][1], tr[1][1], tr[2][1], tr[0][2], tr[1][2], tr[2][2]);
+ auto const surface = static_cast<Surface*>(window);
+ android_dataspace dataspace = surface->getBuffersDataSpace();
+ result.appendFormat(" dataspace: %s (%d)\n", dataspaceDetails(dataspace).c_str(), dataspace);
String8 surfaceDump;
mDisplaySurface->dumpAsString(surfaceDump);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e92565f..826b4a8 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -2501,7 +2501,7 @@
const Layer::State& layerState(getDrawingState());
const HWCInfo& hwcInfo = mHwcLayers.at(hwcId);
- result.appendFormat(" %10u | ", layerState.z);
+ result.appendFormat(" %10d | ", layerState.z);
result.appendFormat("%10s | ",
to_string(getCompositionType(hwcId)).c_str());
const Rect& frame = hwcInfo.displayFrame;
@@ -2625,6 +2625,44 @@
return true;
}
+bool Layer::reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle) {
+ if (newParentHandle == nullptr || childHandle == nullptr) {
+ return false;
+ }
+
+ auto handle = static_cast<Handle*>(newParentHandle.get());
+ sp<Layer> newParent = handle->owner.promote();
+ if (newParent == nullptr) {
+ ALOGE("Unable to promote Layer handle");
+ return false;
+ }
+
+ handle = static_cast<Handle*>(childHandle.get());
+ sp<Layer> child = handle->owner.promote();
+ if (child == nullptr) {
+ ALOGE("Unable to promote child Layer handle");
+ return false;
+ }
+
+ if (mCurrentChildren.indexOf(child) < 0) {
+ ALOGE("Child layer is not child of current layer");
+ return false;
+ }
+
+ sp<Client> parentClient(mClientRef.promote());
+ sp<Client> childClient(child->mClientRef.promote());
+ sp<Client> newParentClient(newParent->mClientRef.promote());
+
+ if (parentClient != childClient || childClient != newParentClient) {
+ ALOGE("Current layer, child layer, and new parent layer must have the same client");
+ return false;
+ }
+
+ newParent->addChild(child);
+ mCurrentChildren.remove(child);
+ return true;
+}
+
bool Layer::detachChildren() {
traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* child) {
if (child == this) {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index f7b82e4..f94833b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -241,6 +241,7 @@
bool setOverrideScalingMode(int32_t overrideScalingMode);
void setInfo(uint32_t type, uint32_t appId);
bool reparentChildren(const sp<IBinder>& layer);
+ bool reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle);
bool detachChildren();
// If we have received a new buffer this frame, we will pass its surface
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 959d22f..e9d8c96 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2777,7 +2777,14 @@
if (parent == nullptr) {
mCurrentState.layersSortedByZ.add(lbc);
} else {
- if (mCurrentState.layersSortedByZ.indexOf(parent) < 0) {
+ bool found = false;
+ mCurrentState.traverseInZOrder([&](Layer* layer) {
+ if (layer == parent.get()) {
+ found = true;
+ }
+ });
+
+ if (!found) {
ALOGE("addClientLayer called with a removed parent");
return NAME_NOT_FOUND;
}
@@ -3103,6 +3110,13 @@
// We don't trigger a traversal here because if no other state is
// changed, we don't want this to cause any more work
}
+ // Always re-parent the children that explicitly requested to get
+ // re-parented before the general re-parent of all children.
+ if (what & layer_state_t::eReparentChild) {
+ if (layer->reparentChild(s.parentHandleForChild, s.childHandle)) {
+ flags |= eTransactionNeeded|eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eReparentChildren) {
if (layer->reparentChildren(s.reparentHandle)) {
flags |= eTransactionNeeded|eTraversalNeeded;
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index a92e1f9..71aa52d 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -2679,6 +2679,13 @@
// We don't trigger a traversal here because if no other state is
// changed, we don't want this to cause any more work
}
+ // Always re-parent the children that explicitly requested to get
+ // re-parented before the general re-parent of all children.
+ if (what & layer_state_t::eReparentChild) {
+ if (layer->reparentChild(s.parentHandleForChild, s.childHandle)) {
+ flags |= eTransactionNeeded|eTraversalNeeded;
+ }
+ }
if (what & layer_state_t::eReparentChildren) {
if (layer->reparentChildren(s.reparentHandle)) {
flags |= eTransactionNeeded|eTraversalNeeded;
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 4ce14f8..d80ed92 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -1123,4 +1123,48 @@
fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0);
}
+TEST_F(ChildLayerTest, ReparentChild) {
+ SurfaceComposerClient::openGlobalTransaction();
+ mChild->show();
+ mChild->setPosition(10, 10);
+ mFGSurfaceControl->setPosition(64, 64);
+ SurfaceComposerClient::closeGlobalTransaction(true);
+
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ // Top left of foreground must now be visible
+ mCapture->expectFGColor(64, 64);
+ // But 10 pixels in we should see the child surface
+ mCapture->expectChildColor(74, 74);
+ // And 10 more pixels we should be back to the foreground surface
+ mCapture->expectFGColor(84, 84);
+ }
+ mFGSurfaceControl->reparentChild(mBGSurfaceControl->getHandle(), mChild->getHandle());
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ mCapture->expectFGColor(64, 64);
+ // In reparenting we should have exposed the entire foreground surface.
+ mCapture->expectFGColor(74, 74);
+ // And the child layer should now begin at 10, 10 (since the BG
+ // layer is at (0, 0)).
+ mCapture->expectBGColor(9, 9);
+ mCapture->expectChildColor(10, 10);
+ }
+}
+
+TEST_F(ChildLayerTest, NestedChildren) {
+ sp<SurfaceControl> grandchild = mComposerClient->createSurface(
+ String8("Grandchild surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mChild.get());
+ fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+ {
+ ScreenCapture::captureScreen(&mCapture);
+ // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer
+ // which begins at 64, 64
+ mCapture->checkPixel(64, 64, 50, 50, 50);
+ }
+}
+
}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index 60916f3..d97ffa3 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -36,7 +36,6 @@
#include <thread>
constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0);
-constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1);
using namespace sftest;
@@ -168,7 +167,7 @@
ALOGV("enableCallback");
mCallbacksOn = enable;
if (mCallbacksOn) {
- mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED);
+ mClient->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
}
}
@@ -507,7 +506,7 @@
if (mSurfaceComposer != nullptr) {
mSurfaceComposer->injectVSync(timestamp);
} else {
- mClient->onVsync(DEFAULT_DISPLAY, timestamp);
+ mClient->onVsync(PRIMARY_DISPLAY, timestamp);
}
}
}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index 294abb2..2a5a8ad 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -19,6 +19,9 @@
#include "ComposerClient.h"
#include "RenderState.h"
+// Needed for display type/ID enums
+#include <hardware/hwcomposer_defs.h>
+
#include <utils/Condition.h>
#include <chrono>
@@ -40,6 +43,13 @@
namespace sftest {
+// NOTE: The ID's need to be exactly these. VR composer and parts of
+// the SurfaceFlinger assume the display IDs to have these values
+// despite the enum being documented as a display type.
+// TODO: Reference to actual documentation
+constexpr Display PRIMARY_DISPLAY = static_cast<Display>(HWC_DISPLAY_PRIMARY);
+constexpr Display EXTERNAL_DISPLAY = static_cast<Display>(HWC_DISPLAY_EXTERNAL);
+
class FakeComposerClient : public ComposerBase {
public:
FakeComposerClient();
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 8902ede..9ac3331 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -168,17 +168,15 @@
android::hardware::ProcessState::self()->startThreadPool();
android::ProcessState::self()->startThreadPool();
- EXPECT_CALL(*mMockComposer, getDisplayType(1, _))
+ EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _))
.WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
Return(Error::NONE)));
- // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy
- // but encoding that here exactly.
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _))
- .Times(5)
- .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
- // TODO: Find out what code is generating the ID 0.
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _))
- .Times(5)
+ // Primary display will be queried twice for all 5 attributes. One
+ // set of queries comes from the SurfaceFlinger proper an the
+ // other set from the VR composer.
+ // TODO: Is VR composer always present? Change to atLeast(5)?
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
+ .Times(2 * 5)
.WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
startSurfaceFlinger();
@@ -207,31 +205,32 @@
TEST_F(DisplayTest, Hotplug) {
ALOGD("DisplayTest::Hotplug");
- EXPECT_CALL(*mMockComposer, getDisplayType(2, _))
+ EXPECT_CALL(*mMockComposer, getDisplayType(EXTERNAL_DISPLAY, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL),
Return(Error::NONE)));
// The attribute queries will get done twice. This is for defaults
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _))
+ EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
.Times(2 * 3)
.WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
// ... and then special handling for dimensions. Specifying this
// rules later means that gmock will try them first, i.e.,
// ordering of width/height vs. the default implementation for
// other queries is significant.
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _))
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _))
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
.Times(2)
.WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
// TODO: Width and height queries are not actually called. Display
// info returns dimensions 0x0 in display info. Why?
- mMockComposer->hotplugDisplay(static_cast<Display>(2),
- IComposerCallback::Connection::CONNECTED);
+ mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
{
sp<android::IBinder> display(
@@ -257,13 +256,11 @@
}
}
- mMockComposer->hotplugDisplay(static_cast<Display>(2),
- IComposerCallback::Connection::DISCONNECTED);
+ mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
mMockComposer->clearFrames();
- mMockComposer->hotplugDisplay(static_cast<Display>(2),
- IComposerCallback::Connection::CONNECTED);
+ mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
{
sp<android::IBinder> display(
@@ -288,8 +285,7 @@
ASSERT_EQ(NO_ERROR, surfaceControl->show());
}
}
- mMockComposer->hotplugDisplay(static_cast<Display>(2),
- IComposerCallback::Connection::DISCONNECTED);
+ mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
}
////////////////////////////////////////////////
@@ -664,7 +660,7 @@
{{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}},
{{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}};
// clang-format on
- constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData);
+ constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
for (int i = 0; i < TEST_COUNT; i++) {
// TODO: How to leverage the HWC2 stringifiers?
diff --git a/services/surfaceflinger/tests/hwc2/Android.mk b/services/surfaceflinger/tests/hwc2/Android.mk
index 203ced5..6d20349 100644
--- a/services/surfaceflinger/tests/hwc2/Android.mk
+++ b/services/surfaceflinger/tests/hwc2/Android.mk
@@ -36,7 +36,9 @@
libui \
libgui \
liblog \
- libsync
+ libsync \
+ libskia \
+ android.hardware.graphics.common@1.0
LOCAL_STATIC_LIBRARIES := \
libbase \
libadf \
@@ -49,6 +51,7 @@
Hwc2TestLayers.cpp \
Hwc2TestBuffer.cpp \
Hwc2TestClientTarget.cpp \
- Hwc2TestVirtualDisplay.cpp
+ Hwc2TestVirtualDisplay.cpp \
+ Hwc2TestPixelComparator.cpp
include $(BUILD_NATIVE_TEST)
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
index 4055527..4878c14 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp
@@ -1775,6 +1775,145 @@
}
}
+ void createAndPresentVirtualDisplay(size_t layerCnt,
+ Hwc2TestCoverage coverage,
+ const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>&
+ coverageExceptions)
+ {
+ Hwc2TestVirtualDisplay testVirtualDisplay(coverage);
+ hwc2_display_t display;
+ android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+
+ do {
+ // Items dependent on the display dimensions
+ hwc2_error_t err = HWC2_ERROR_NONE;
+ const UnsignedArea& dimension =
+ testVirtualDisplay.getDisplayDimension();
+ ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width,
+ dimension.height, &desiredFormat, &display, &err));
+ ASSERT_TRUE(err == HWC2_ERROR_NONE)
+ << "Cannot allocate virtual display";
+
+ ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON));
+ ASSERT_NO_FATAL_FAILURE(enableVsync(display));
+
+ std::vector<hwc2_config_t> configs;
+ ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs));
+
+ for (auto config : configs) {
+ ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config));
+
+ Area displayArea;
+ ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display,
+ &displayArea));
+
+ std::vector<hwc2_layer_t> layers;
+ ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers,
+ layerCnt));
+ Hwc2TestLayers testLayers(layers, coverage, displayArea,
+ coverageExceptions);
+
+ /*
+ * Layouts that do not cover an entire virtual display will
+ * cause undefined behavior.
+ * Enable optimizeLayouts to avoid this.
+ */
+ testLayers.optimizeLayouts();
+ do {
+ // Items dependent on the testLayers properties
+ std::set<hwc2_layer_t> clientLayers;
+ std::set<hwc2_layer_t> clearLayers;
+ uint32_t numTypes, numRequests;
+ bool hasChanges, skip;
+ bool flipClientTarget;
+ int32_t presentFence;
+ Hwc2TestClientTarget testClientTarget;
+ buffer_handle_t outputBufferHandle;
+ android::base::unique_fd outputBufferReleaseFence;
+
+ ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers,
+ &testLayers, &skip));
+
+ if (skip)
+ continue;
+
+ ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes,
+ &numRequests, &hasChanges));
+
+ if (hasChanges)
+ EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size()))
+ << "wrong number of requests";
+
+ ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display,
+ testLayers, layers, numTypes, &clientLayers));
+
+ ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers,
+ numRequests, &clearLayers, &flipClientTarget));
+ ASSERT_NO_FATAL_FAILURE(setClientTarget(display,
+ &testClientTarget, testLayers, clientLayers,
+ clearLayers, flipClientTarget, displayArea));
+ ASSERT_NO_FATAL_FAILURE(acceptDisplayChanges(display));
+
+ ASSERT_EQ(testVirtualDisplay.getOutputBuffer(
+ &outputBufferHandle, &outputBufferReleaseFence), 0);
+ ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display,
+ outputBufferHandle, outputBufferReleaseFence));
+
+ EXPECT_NO_FATAL_FAILURE(presentDisplay(display,
+ &presentFence));
+ ASSERT_NO_FATAL_FAILURE(closeFences(display, presentFence));
+
+ ASSERT_EQ(testVirtualDisplay.verifyOutputBuffer(&testLayers,
+ &layers, &clearLayers), 0);
+
+ /*
+ * Upscaling the image causes minor pixel differences.
+ * Work around this by using some threshold.
+ *
+ * Fail test if we are off by more than 1% of our
+ * pixels.
+ */
+ ComparatorResult& comparatorResult = ComparatorResult::get();
+ int threshold = (dimension.width * dimension.height) / 100;
+ double diffPercent = (comparatorResult.getDifferentPixelCount() * 100.0) /
+ (dimension.width * dimension.height);
+
+ if (comparatorResult.getDifferentPixelCount() != 0)
+ EXPECT_TRUE(false)
+ << comparatorResult.getDifferentPixelCount() << " pixels ("
+ << diffPercent << "%) are different.";
+
+ if (comparatorResult.getDifferentPixelCount() > threshold) {
+ EXPECT_TRUE(false)
+ << "Mismatched pixel count exceeds threshold. "
+ << "Writing buffers to file.";
+
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()
+ ->current_test_info();
+
+ EXPECT_EQ(testVirtualDisplay.writeBuffersToFile(
+ test_info->name()), 0)
+ << "Failed to write buffers.";
+ }
+
+ ASSERT_LE(comparatorResult.getDifferentPixelCount(), threshold)
+ << comparatorResult.getDifferentPixelCount() << " pixels ("
+ << diffPercent << "%) are different. "
+ << "Exceeds 1% threshold, terminating test. "
+ << "Test case: " << testLayers.dump();
+
+ } while (testLayers.advance());
+
+ ASSERT_NO_FATAL_FAILURE(destroyLayers(display,
+ std::move(layers)));
+ }
+ ASSERT_NO_FATAL_FAILURE(disableVsync(display));
+ ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF));
+ ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display));
+ } while (testVirtualDisplay.advance());
+ }
+
hwc2_device_t* mHwc2Device = nullptr;
enum class Hwc2TestHotplugStatus {
@@ -4479,7 +4618,7 @@
buffer_handle_t handle;
android::base::unique_fd acquireFence;
- if (testVirtualDisplay->getBuffer(&handle, &acquireFence) >= 0)
+ if (testVirtualDisplay->getOutputBuffer(&handle, &acquireFence) >= 0)
EXPECT_NO_FATAL_FAILURE(test->setOutputBuffer(display,
handle, acquireFence));
}));
@@ -4499,7 +4638,7 @@
ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay));
- if (testVirtualDisplay->getBuffer(&handle, &acquireFence) < 0)
+ if (testVirtualDisplay->getOutputBuffer(&handle, &acquireFence) < 0)
return;
ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(badDisplay,
@@ -4539,7 +4678,7 @@
android::base::unique_fd acquireFence;
hwc2_error_t err = HWC2_ERROR_NONE;
- if (testVirtualDisplay.getBuffer(&handle, &acquireFence) < 0)
+ if (testVirtualDisplay.getOutputBuffer(&handle, &acquireFence) < 0)
continue;
ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display, handle,
@@ -4557,3 +4696,74 @@
ASSERT_NO_FATAL_FAILURE(dump(&buffer));
}
+
+/*
+ * TODO(b/64724708): Hwc2TestPropertyName::BufferArea MUST be default for all
+ * virtual display tests as we don't handle this case correctly.
+ *
+ * Only default dataspace is supported in our drawing code.
+ */
+const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>
+ virtualDisplayExceptions =
+ {{Hwc2TestPropertyName::BufferArea, Hwc2TestCoverage::Default},
+ {Hwc2TestPropertyName::Dataspace, Hwc2TestCoverage::Default}};
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with default coverage on a
+ * virtual display. */
+TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_1)
+{
+ Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+ const size_t layerCnt = 1;
+ ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
+ virtualDisplayExceptions));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 1 layer with basic coverage on a
+ * virtual display. */
+TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_basic_1)
+{
+ Hwc2TestCoverage coverage = Hwc2TestCoverage::Basic;
+ const size_t layerCnt = 1;
+ ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
+ virtualDisplayExceptions));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 2 layers with default coverage on a
+ * virtual display. */
+TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_2)
+{
+ Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+ const size_t layerCnt = 2;
+ ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
+ virtualDisplayExceptions));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 3 layers with default coverage on a
+ * virtual display. */
+TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_3)
+{
+ Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+ const size_t layerCnt = 3;
+ ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
+ virtualDisplayExceptions));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 4 layers with default coverage on a
+ * virtual display. */
+TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_4)
+{
+ Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+ const size_t layerCnt = 4;
+ ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
+ virtualDisplayExceptions));
+}
+
+/* TESTCASE: Tests that the HWC2 can present 5 layers with default coverage on a
+ * virtual display. */
+TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_5)
+{
+ Hwc2TestCoverage coverage = Hwc2TestCoverage::Default;
+ const size_t layerCnt = 5;
+ ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage,
+ virtualDisplayExceptions));
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
index 1d3a1d3..6484562 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
@@ -23,14 +23,17 @@
#include <gui/BufferItemConsumer.h>
#include <ui/GraphicBuffer.h>
+#include <android/hardware/graphics/common/1.0/types.h>
#include <math/vec4.h>
#include <GLES3/gl3.h>
-
+#include <SkImageEncoder.h>
+#include <SkStream.h>
#include "Hwc2TestBuffer.h"
#include "Hwc2TestLayers.h"
using namespace android;
+using android::hardware::graphics::common::V1_0::BufferUsage;
/* Returns a fence from egl */
typedef void (*FenceCallback)(int32_t fence, void* callbackArgs);
@@ -396,8 +399,9 @@
{
/* Create new graphic buffer with correct dimensions */
mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
- mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
- "hwc2_test_buffer");
+ mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY, "hwc2_test_buffer");
+
int ret = mGraphicBuffer->initCheck();
if (ret) {
return ret;
@@ -408,7 +412,8 @@
/* Locks the buffer for writing */
uint8_t* img;
- mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ mGraphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN),
+ (void**)(&img));
uint32_t stride = mGraphicBuffer->getStride();
@@ -458,31 +463,22 @@
Hwc2TestClientTargetBuffer::~Hwc2TestClientTargetBuffer() { }
-/* Generates a client target buffer using the layers assigned for client
- * composition. Takes into account the individual layer properties such as
+/* Generates a buffer from layersToDraw.
+ * Takes into account the individual layer properties such as
* transform, blend mode, source crop, etc. */
-int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle,
- int32_t* outFence, const Area& bufferArea,
+static void compositeBufferFromLayers(
+ const android::sp<android::GraphicBuffer>& graphicBuffer,
+ android_pixel_format_t format, const Area& bufferArea,
const Hwc2TestLayers* testLayers,
- const std::set<hwc2_layer_t>* clientLayers,
+ const std::set<hwc2_layer_t>* layersToDraw,
const std::set<hwc2_layer_t>* clearLayers)
{
- /* Create new graphic buffer with correct dimensions */
- mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height,
- mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER,
- "hwc2_test_buffer");
- int ret = mGraphicBuffer->initCheck();
- if (ret) {
- return ret;
- }
- if (!mGraphicBuffer->handle) {
- return -EINVAL;
- }
-
+ /* Locks the buffer for writing */
uint8_t* img;
- mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+ graphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN),
+ (void**)(&img));
- uint32_t stride = mGraphicBuffer->getStride();
+ uint32_t stride = graphicBuffer->getStride();
float bWDiv3 = bufferArea.width / 3;
float bW2Div3 = bufferArea.width * 2 / 3;
@@ -497,10 +493,10 @@
uint8_t r = 0, g = 0, b = 0;
float a = 0.0f;
- /* Cycle through each client layer from back to front and
+ /* Cycle through each layer from back to front and
* update the pixel color. */
- for (auto layer = clientLayers->rbegin();
- layer != clientLayers->rend(); ++layer) {
+ for (auto layer = layersToDraw->rbegin();
+ layer != layersToDraw->rend(); ++layer) {
const hwc_rect_t df = testLayers->getDisplayFrame(*layer);
@@ -570,8 +566,8 @@
* (100x50) at the end of the transformation. */
if (transform & HWC_TRANSFORM_ROT_90) {
float tmp = xPos;
- xPos = -yPos * dfW / dfH;
- yPos = tmp * dfH / dfW;
+ xPos = yPos * dfW / dfH;
+ yPos = -tmp * dfH / dfW;
}
/* Change origin back to the top left corner of the
@@ -682,14 +678,114 @@
}
/* Set the pixel color */
- setColor(x, y, mFormat, stride, img, r, g, b, a * 255);
+ setColor(x, y, format, stride, img, r, g, b, a * 255);
}
}
- mGraphicBuffer->unlock();
+ graphicBuffer->unlock();
+}
+
+/* Generates a client target buffer using the layers assigned for client
+ * composition. Takes into account the individual layer properties such as
+ * transform, blend mode, source crop, etc. */
+int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle,
+ int32_t* outFence, const Area& bufferArea,
+ const Hwc2TestLayers* testLayers,
+ const std::set<hwc2_layer_t>* clientLayers,
+ const std::set<hwc2_layer_t>* clearLayers)
+{
+ /* Create new graphic buffer with correct dimensions */
+ mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height,
+ mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY, "hwc2_test_buffer");
+
+ int ret = mGraphicBuffer->initCheck();
+ if (ret)
+ return ret;
+
+ if (!mGraphicBuffer->handle)
+ return -EINVAL;
+
+ compositeBufferFromLayers(mGraphicBuffer, mFormat, bufferArea, testLayers,
+ clientLayers, clearLayers);
*outFence = mFenceGenerator->get();
*outHandle = mGraphicBuffer->handle;
return 0;
}
+
+void Hwc2TestVirtualBuffer::updateBufferArea(const Area& bufferArea)
+{
+ mBufferArea.width = bufferArea.width;
+ mBufferArea.height = bufferArea.height;
+}
+
+bool Hwc2TestVirtualBuffer::writeBufferToFile(std::string path)
+{
+ SkFILEWStream file(path.c_str());
+ const SkImageInfo info = SkImageInfo::Make(mBufferArea.width,
+ mBufferArea.height, SkColorType::kRGBA_8888_SkColorType,
+ SkAlphaType::kPremul_SkAlphaType);
+
+ uint8_t* img;
+ mGraphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN),
+ (void**)(&img));
+
+ SkPixmap pixmap(info, img, mGraphicBuffer->getStride());
+ bool result = file.isValid() && SkEncodeImage(&file, pixmap,
+ SkEncodedImageFormat::kPNG, 100);
+
+ mGraphicBuffer->unlock();
+ return result;
+}
+
+/* Generates a buffer that holds the expected result of compositing all of our
+ * layers */
+int Hwc2TestExpectedBuffer::generateExpectedBuffer(
+ const Hwc2TestLayers* testLayers,
+ const std::vector<hwc2_layer_t>* allLayers,
+ const std::set<hwc2_layer_t>* clearLayers)
+{
+ mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
+ mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN,
+ "hwc2_test_buffer");
+
+ int ret = mGraphicBuffer->initCheck();
+ if (ret)
+ return ret;
+
+ if (!mGraphicBuffer->handle)
+ return -EINVAL;
+
+ const std::set<hwc2_layer_t> allLayerSet(allLayers->begin(),
+ allLayers->end());
+
+ compositeBufferFromLayers(mGraphicBuffer, mFormat, mBufferArea, testLayers,
+ &allLayerSet, clearLayers);
+
+ return 0;
+}
+
+int Hwc2TestOutputBuffer::getOutputBuffer(buffer_handle_t* outHandle,
+ int32_t* outFence)
+{
+ if (mBufferArea.width == -1 || mBufferArea.height == -1)
+ return -EINVAL;
+
+ mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height,
+ mFormat, BufferUsage::CPU_READ_OFTEN |
+ BufferUsage::GPU_RENDER_TARGET, "hwc2_test_buffer");
+
+ int ret = mGraphicBuffer->initCheck();
+ if (ret)
+ return ret;
+
+ if (!mGraphicBuffer->handle)
+ return -EINVAL;
+
+ *outFence = -1;
+ *outHandle = mGraphicBuffer->handle;
+
+ return 0;
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
index b2b3a66..fd54fef 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h
@@ -71,4 +71,38 @@
const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
};
+
+class Hwc2TestVirtualBuffer {
+public:
+ void updateBufferArea(const Area& bufferArea);
+
+ bool writeBufferToFile(std::string path);
+
+ android::sp<android::GraphicBuffer>& graphicBuffer()
+ {
+ return mGraphicBuffer;
+ }
+
+protected:
+ android::sp<android::GraphicBuffer> mGraphicBuffer;
+
+ Area mBufferArea = {-1, -1};
+
+ const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+};
+
+
+class Hwc2TestExpectedBuffer : public Hwc2TestVirtualBuffer {
+public:
+ int generateExpectedBuffer(const Hwc2TestLayers* testLayers,
+ const std::vector<hwc2_layer_t>* allLayers,
+ const std::set<hwc2_layer_t>* clearLayers);
+};
+
+
+class Hwc2TestOutputBuffer : public Hwc2TestVirtualBuffer {
+public:
+ int getOutputBuffer(buffer_handle_t* outHandle, int32_t* outFence);
+};
+
#endif /* ifndef _HWC2_TEST_BUFFER_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp
new file mode 100644
index 0000000..904b927
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <android/hardware/graphics/common/1.0/types.h>
+
+#include "Hwc2TestPixelComparator.h"
+
+using android::hardware::graphics::common::V1_0::BufferUsage;
+
+uint32_t ComparatorResult::getPixel(int32_t x, int32_t y, uint32_t stride,
+ uint8_t* img) const
+{
+ uint32_t r = img[(y * stride + x) * 4 + 0];
+ uint32_t g = img[(y * stride + x) * 4 + 1];
+ uint32_t b = img[(y * stride + x) * 4 + 2];
+ uint32_t a = img[(y * stride + x) * 4 + 3];
+
+ uint32_t pixel = 0;
+ pixel |= r;
+ pixel |= g << 8;
+ pixel |= b << 16;
+ pixel |= a << 24;
+ return pixel;
+}
+
+void ComparatorResult::CompareBuffers(
+ android::sp<android::GraphicBuffer>& resultBuffer,
+ android::sp<android::GraphicBuffer>& expectedBuffer)
+{
+ uint8_t* resultBufferImg;
+ uint8_t* expectedBufferImg;
+ resultBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_READ_OFTEN),
+ (void**)(&resultBufferImg));
+
+ expectedBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_READ_OFTEN),
+ (void**)(&expectedBufferImg));
+ mComparisons.clear();
+ int32_t mDifferentPixelCount = 0;
+ int32_t mBlankPixelCount = 0;
+
+ for (uint32_t y = 0; y < resultBuffer->getHeight(); y++) {
+ for (uint32_t x = 0; x < resultBuffer->getWidth(); x++) {
+ uint32_t result = getPixel(x, y, resultBuffer->getStride(),
+ resultBufferImg);
+ uint32_t expected = getPixel(x, y, expectedBuffer->getStride(),
+ expectedBufferImg);
+
+ if (result == 0)
+ mBlankPixelCount++;
+
+ if (result != expected)
+ mDifferentPixelCount++;
+
+ mComparisons.emplace_back(std::make_tuple(x, y, result, expected));
+ }
+ }
+ resultBuffer->unlock();
+ expectedBuffer->unlock();
+}
+
+std::string ComparatorResult::pixelDiff(uint32_t x, uint32_t y,
+ uint32_t resultPixel, uint32_t expectedPixel) const
+{
+ uint32_t resultAlpha = (resultPixel >> 24) & 0xFF;
+ uint32_t resultBlue = (resultPixel >> 16) & 0xFF;
+ uint32_t resultGreen = (resultPixel >> 8) & 0xFF;
+ uint32_t resultRed = resultPixel & 0xFF;
+
+ uint32_t expectedAlpha = (expectedPixel >> 24) & 0xFF;
+ uint32_t expectedBlue = (expectedPixel >> 16) & 0xFF;
+ uint32_t expectedGreen = (expectedPixel >> 8) & 0xFF;
+ uint32_t expectedRed = expectedPixel & 0xFF;
+
+ std::ostringstream stream;
+
+ stream << "x: " << x << " y: " << y << std::endl;
+ stream << std::hex;
+ stream << "Result pixel: " << resultRed << "|" << resultGreen << "|"
+ << resultBlue << "|" << resultAlpha << std::endl;
+
+ stream << "Expected pixel: " << expectedRed << "|" << expectedGreen << "|"
+ << expectedBlue << "|" << expectedAlpha << std::endl;
+
+ return stream.str();
+}
+
+std::string ComparatorResult::dumpComparison() const
+{
+ std::ostringstream stream;
+ stream << "Number of different pixels: " << mDifferentPixelCount;
+
+ for (const auto& comparison : mComparisons) {
+ if (std::get<2>(comparison) != std::get<3>(comparison))
+ stream << pixelDiff(std::get<0>(comparison),
+ std::get<1>(comparison), std::get<2>(comparison),
+ std::get<3>(comparison));
+ }
+ return stream.str();
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h
new file mode 100644
index 0000000..55fa936
--- /dev/null
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _HWC2_TEST_PIXEL_COMPARATOR_H
+#define _HWC2_TEST_PIXEL_COMPARATOR_H
+
+#include <ui/GraphicBuffer.h>
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+
+class ComparatorResult {
+public:
+ static ComparatorResult& get()
+ {
+ static ComparatorResult instance;
+ return instance;
+ }
+
+ void CompareBuffers(android::sp<android::GraphicBuffer>& resultBuffer,
+ android::sp<android::GraphicBuffer>& expectedBuffer);
+
+ std::string dumpComparison() const;
+
+ ComparatorResult(const ComparatorResult&) = delete;
+ ComparatorResult(ComparatorResult&&) = delete;
+ ComparatorResult& operator=(ComparatorResult const&) = delete;
+ ComparatorResult& operator=(ComparatorResult&&) = delete;
+
+ int32_t getDifferentPixelCount() const { return mDifferentPixelCount; }
+ int32_t getBlankPixelCount() const { return mBlankPixelCount; }
+
+private:
+ ComparatorResult() = default;
+ uint32_t getPixel(int32_t x, int32_t y, uint32_t stride, uint8_t* img) const;
+ std::string pixelDiff(uint32_t x, uint32_t y, uint32_t resultPixel,
+ uint32_t expectedPixel) const;
+
+ int32_t mDifferentPixelCount;
+ int32_t mBlankPixelCount;
+ /* std::tuple<X coordinate, Y coordinate, resultPixel, expectedPixel> */
+ std::vector<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>>
+ mComparisons;
+};
+
+#endif /* ifndef _HWC2_TEST_PIXEL_COMPARATOR_H */
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
index b5522de..5b3bbeb 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp
@@ -335,9 +335,9 @@
return dmp.str();
}
-void Hwc2TestDisplayDimension::setDependent(Hwc2TestBuffer* buffer)
+void Hwc2TestDisplayDimension::setDependent(Hwc2TestVirtualBuffer* buffer)
{
- mBuffer = buffer;
+ mBuffers.insert(buffer);
updateDependents();
}
@@ -345,8 +345,8 @@
{
const UnsignedArea& curr = get();
- if (mBuffer)
- mBuffer->updateBufferArea({static_cast<int32_t>(curr.width),
+ for (Hwc2TestVirtualBuffer* buffer : mBuffers)
+ buffer->updateBufferArea({static_cast<int32_t>(curr.width),
static_cast<int32_t>(curr.height)});
}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
index c2029ab..cb811e0 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h
@@ -243,6 +243,7 @@
static const std::array<bool, 6> mCompositionSupport;
};
+class Hwc2TestVirtualBuffer;
class Hwc2TestDisplayDimension : public Hwc2TestProperty<UnsignedArea> {
public:
@@ -250,12 +251,12 @@
std::string dump() const;
- void setDependent(Hwc2TestBuffer* buffer);
+ void setDependent(Hwc2TestVirtualBuffer* buffer);
private:
void updateDependents();
- Hwc2TestBuffer* mBuffer;
+ std::set<Hwc2TestVirtualBuffer*> mBuffers;
static const std::vector<UnsignedArea> mDefaultDisplayDimensions;
static const std::vector<UnsignedArea> mBasicDisplayDimensions;
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
index d0fbc0b..e6cceb8 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp
@@ -15,14 +15,18 @@
*/
#include <sstream>
+#include <sys/stat.h>
#include "Hwc2TestVirtualDisplay.h"
+#define DIR_NAME "images"
+
Hwc2TestVirtualDisplay::Hwc2TestVirtualDisplay(
Hwc2TestCoverage coverage)
: mDisplayDimension(coverage)
{
- mDisplayDimension.setDependent(&mBuffer);
+ mDisplayDimension.setDependent(&mOutputBuffer);
+ mDisplayDimension.setDependent(&mExpectedBuffer);
}
std::string Hwc2TestVirtualDisplay::dump() const
@@ -36,11 +40,11 @@
return dmp.str();
}
-int Hwc2TestVirtualDisplay::getBuffer(buffer_handle_t* outHandle,
+int Hwc2TestVirtualDisplay::getOutputBuffer(buffer_handle_t* outHandle,
android::base::unique_fd* outAcquireFence)
{
int32_t acquireFence;
- int ret = mBuffer.get(outHandle, &acquireFence);
+ int ret = mOutputBuffer.getOutputBuffer(outHandle, &acquireFence);
outAcquireFence->reset(acquireFence);
return ret;
}
@@ -59,3 +63,36 @@
{
return mDisplayDimension.get();
}
+
+int Hwc2TestVirtualDisplay::verifyOutputBuffer(const Hwc2TestLayers* testLayers,
+ const std::vector<hwc2_layer_t>* allLayers,
+ const std::set<hwc2_layer_t>* clearLayers)
+{
+ int ret = mExpectedBuffer.generateExpectedBuffer(testLayers, allLayers,
+ clearLayers);
+ if (ret)
+ return ret;
+
+ ComparatorResult::get().CompareBuffers(mOutputBuffer.graphicBuffer(),
+ mExpectedBuffer.graphicBuffer());
+
+ return 0;
+}
+
+int Hwc2TestVirtualDisplay::writeBuffersToFile(std::string name)
+{
+ std::ostringstream expectedPath;
+ std::ostringstream resultPath;
+ int ret = mkdir(DIR_NAME, DEFFILEMODE);
+ if (ret && errno != EEXIST)
+ return ret;
+
+ expectedPath << DIR_NAME << "/expected-" << name << ".png";
+ resultPath << DIR_NAME << "/result-" << name << ".png";
+
+ if (!mExpectedBuffer.writeBufferToFile(expectedPath.str()) ||
+ !mOutputBuffer.writeBufferToFile(resultPath.str()))
+ return -1;
+
+ return 0;
+}
diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
index 09420ef..10c8ef0 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h
@@ -18,6 +18,7 @@
#define _HWC2_TEST_VIRTUAL_DISPLAY_H
#include "Hwc2TestBuffer.h"
+#include "Hwc2TestPixelComparator.h"
#include "Hwc2TestProperties.h"
#define HWC2_INCLUDE_STRINGIFICATION
@@ -32,17 +33,22 @@
std::string dump() const;
- int getBuffer(buffer_handle_t* outHandle,
+ int getOutputBuffer(buffer_handle_t* outHandle,
android::base::unique_fd* outAcquireFence);
+ int verifyOutputBuffer(const Hwc2TestLayers* testLayers,
+ const std::vector<hwc2_layer_t>* allLayers,
+ const std::set<hwc2_layer_t>* clearLayers);
+
+ int writeBuffersToFile(std::string name);
void reset();
bool advance();
UnsignedArea getDisplayDimension() const;
private:
- Hwc2TestBuffer mBuffer;
-
+ Hwc2TestOutputBuffer mOutputBuffer;
+ Hwc2TestExpectedBuffer mExpectedBuffer;
Hwc2TestDisplayDimension mDisplayDimension;
};