test-hwc2: Added virtual Display tests

VirtualDisplay tests added.
Test: Ran on (heavily modified) ryu
Change-Id: I5cc92d11d4cde6c3407d71652f87ea3c3fb63228
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 9ff9a4f..6484562 100644
--- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
+++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp
@@ -27,7 +27,8 @@
 #include <math/vec4.h>
 
 #include <GLES3/gl3.h>
-
+#include <SkImageEncoder.h>
+#include <SkStream.h>
 #include "Hwc2TestBuffer.h"
 #include "Hwc2TestLayers.h"
 
@@ -462,33 +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, 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;
-    }
-
+    /* Locks the buffer for writing */
     uint8_t* img;
-    mGraphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN),
+    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;
@@ -503,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);
 
@@ -688,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;
 };