Start VTS for color management.

Right now this only makes sure that extended sRGB buffers aren't
corrupted by the time it hits the display. This is especially important
for Ultra HDR, because display compensation is defined by the spec.

Bug: 276331687
Bug: 259144781
Test: VtsHalGraphicsComposer3_ReadbackTest
Change-Id: Ib6c39858289d5559ae76280ca1a0ef756f2daef0
diff --git a/graphics/composer/aidl/vts/ReadbackVts.cpp b/graphics/composer/aidl/vts/ReadbackVts.cpp
index abb58e2..8605628 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.cpp
+++ b/graphics/composer/aidl/vts/ReadbackVts.cpp
@@ -35,6 +35,7 @@
     writer.setLayerPlaneAlpha(mDisplay, mLayer, mAlpha);
     writer.setLayerBlendMode(mDisplay, mLayer, mBlendMode);
     writer.setLayerBrightness(mDisplay, mLayer, mBrightness);
+    writer.setLayerDataspace(mDisplay, mLayer, mDataspace);
 }
 
 std::string ReadbackHelper::getColorModeString(ColorMode mode) {
@@ -99,6 +100,7 @@
 
     layerSettings.geometry.positionTransform = scale * translation;
     layerSettings.whitePointNits = mWhitePointNits;
+    layerSettings.sourceDataspace = static_cast<::android::ui::Dataspace>(mDataspace);
 
     return layerSettings;
 }
@@ -189,6 +191,23 @@
     }
 }
 
+void ReadbackHelper::compareColorBuffers(void* expectedBuffer, void* actualBuffer,
+                                         const uint32_t stride, const uint32_t width,
+                                         const uint32_t height, common::PixelFormat pixelFormat) {
+    const int32_t bytesPerPixel = ReadbackHelper::GetBytesPerPixel(pixelFormat);
+    ASSERT_NE(-1, bytesPerPixel);
+    for (int row = 0; row < height; row++) {
+        for (int col = 0; col < width; col++) {
+            int offset = (row * static_cast<int32_t>(stride) + col) * bytesPerPixel;
+            uint8_t* expectedColor = (uint8_t*)expectedBuffer + offset;
+            uint8_t* actualColor = (uint8_t*)actualBuffer + offset;
+            ASSERT_EQ(expectedColor[0], actualColor[0]);
+            ASSERT_EQ(expectedColor[1], actualColor[1]);
+            ASSERT_EQ(expectedColor[2], actualColor[2]);
+        }
+    }
+}
+
 ReadbackBuffer::ReadbackBuffer(int64_t display, const std::shared_ptr<VtsComposerClient>& client,
                                int32_t width, int32_t height, common::PixelFormat pixelFormat,
                                common::Dataspace dataspace)
@@ -248,6 +267,15 @@
     EXPECT_EQ(::android::OK, status);
 }
 
+::android::sp<::android::GraphicBuffer> ReadbackBuffer::getBuffer() {
+    const auto& [fenceStatus, bufferFence] = mComposerClient->getReadbackBufferFence(mDisplay);
+    EXPECT_TRUE(fenceStatus.isOk());
+    if (bufferFence.get() != -1) {
+        sync_wait(bufferFence.get(), -1);
+    }
+    return mGraphicBuffer;
+}
+
 void TestColorLayer::write(ComposerClientWriter& writer) {
     TestLayer::write(writer);
     writer.setLayerCompositionType(mDisplay, mLayer, Composition::SOLID_COLOR);
@@ -344,10 +372,6 @@
             "TestBufferLayer");
 }
 
-void TestBufferLayer::setDataspace(common::Dataspace dataspace, ComposerClientWriter& writer) {
-    writer.setLayerDataspace(mDisplay, mLayer, dataspace);
-}
-
 void TestBufferLayer::setToClientComposition(ComposerClientWriter& writer) {
     writer.setLayerCompositionType(mDisplay, mLayer, Composition::CLIENT);
 }
diff --git a/graphics/composer/aidl/vts/ReadbackVts.h b/graphics/composer/aidl/vts/ReadbackVts.h
index d5602c1..ee20573 100644
--- a/graphics/composer/aidl/vts/ReadbackVts.h
+++ b/graphics/composer/aidl/vts/ReadbackVts.h
@@ -42,6 +42,9 @@
 static const Color GREEN = {0.0f, 1.0f, 0.0f, 1.0f};
 static const Color BLUE = {0.0f, 0.0f, 1.0f, 1.0f};
 static const Color WHITE = {1.0f, 1.0f, 1.0f, 1.0f};
+static const Color LIGHT_RED = {0.5f, 0.0f, 0.0f, 1.0f};
+static const Color LIGHT_GREEN = {0.0f, 0.5f, 0.0f, 1.0f};
+static const Color LIGHT_BLUE = {0.0f, 0.0f, 0.5f, 1.0f};
 
 class TestRenderEngine;
 
@@ -71,6 +74,8 @@
         mSurfaceDamage = std::move(surfaceDamage);
     }
 
+    void setDataspace(Dataspace dataspace) { mDataspace = dataspace; }
+
     void setTransform(Transform transform) { mTransform = transform; }
     void setAlpha(float alpha) { mAlpha = alpha; }
     void setBlendMode(BlendMode blendMode) { mBlendMode = blendMode; }
@@ -98,6 +103,7 @@
     float mAlpha = 1.0;
     BlendMode mBlendMode = BlendMode::NONE;
     uint32_t mZOrder = 0;
+    Dataspace mDataspace = Dataspace::UNKNOWN;
 };
 
 class TestColorLayer : public TestLayer {
@@ -130,8 +136,6 @@
 
     void setBuffer(std::vector<Color> colors);
 
-    void setDataspace(Dataspace dataspace, ComposerClientWriter& writer);
-
     void setToClientComposition(ComposerClientWriter& writer);
 
     uint32_t getWidth() const { return mWidth; }
@@ -185,6 +189,9 @@
     static void compareColorBuffers(const std::vector<Color>& expectedColors, void* bufferData,
                                     const uint32_t stride, const uint32_t width,
                                     const uint32_t height, PixelFormat pixelFormat);
+    static void compareColorBuffers(void* expectedBuffer, void* actualBuffer, const uint32_t stride,
+                                    const uint32_t width, const uint32_t height,
+                                    PixelFormat pixelFormat);
 };
 
 class ReadbackBuffer {
@@ -196,6 +203,8 @@
 
     void checkReadbackBuffer(const std::vector<Color>& expectedColors);
 
+    ::android::sp<::android::GraphicBuffer> getBuffer();
+
   protected:
     uint32_t mWidth;
     uint32_t mHeight;
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.cpp b/graphics/composer/aidl/vts/RenderEngineVts.cpp
index d1f89d0..4e7f773 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.cpp
+++ b/graphics/composer/aidl/vts/RenderEngineVts.cpp
@@ -89,4 +89,32 @@
     ASSERT_EQ(::android::OK, mGraphicBuffer->unlock());
 }
 
+void TestRenderEngine::checkColorBuffer(const ::android::sp<::android::GraphicBuffer>& buffer) {
+    ASSERT_EQ(mGraphicBuffer->getWidth(), buffer->getWidth());
+    ASSERT_EQ(mGraphicBuffer->getHeight(), buffer->getHeight());
+    void* renderedBufferData;
+    int32_t bytesPerPixel = -1;
+    int32_t bytesPerStride = -1;
+    ASSERT_EQ(0, mGraphicBuffer->lock(static_cast<uint32_t>(mGraphicBuffer->getUsage()),
+                                      &renderedBufferData, &bytesPerPixel, &bytesPerStride));
+    const uint32_t renderedStride = (bytesPerPixel > 0 && bytesPerStride > 0)
+                                            ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel)
+                                            : mGraphicBuffer->getStride();
+
+    void* bufferData;
+    ASSERT_EQ(0, buffer->lock(static_cast<uint32_t>(buffer->getUsage()), &bufferData,
+                              &bytesPerPixel, &bytesPerStride));
+    const uint32_t bufferStride = (bytesPerPixel > 0 && bytesPerStride > 0)
+                                          ? static_cast<uint32_t>(bytesPerStride / bytesPerPixel)
+                                          : buffer->getStride();
+
+    ASSERT_EQ(renderedStride, bufferStride);
+
+    ReadbackHelper::compareColorBuffers(renderedBufferData, bufferData, bufferStride,
+                                        mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight(),
+                                        mFormat);
+    ASSERT_EQ(::android::OK, buffer->unlock());
+    ASSERT_EQ(::android::OK, mGraphicBuffer->unlock());
+}
+
 }  // namespace aidl::android::hardware::graphics::composer3::vts
diff --git a/graphics/composer/aidl/vts/RenderEngineVts.h b/graphics/composer/aidl/vts/RenderEngineVts.h
index 612f0ea..bbe508f 100644
--- a/graphics/composer/aidl/vts/RenderEngineVts.h
+++ b/graphics/composer/aidl/vts/RenderEngineVts.h
@@ -45,6 +45,7 @@
     };
     void drawLayers();
     void checkColorBuffer(const std::vector<Color>& expectedColors);
+    void checkColorBuffer(const ::android::sp<::android::GraphicBuffer>& buffer);
 
     ::android::renderengine::RenderEngine& getInternalRenderEngine() { return *mRenderEngine; }
 
diff --git a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
index 4b0f336..466954f 100644
--- a/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
+++ b/graphics/composer/aidl/vts/VtsHalGraphicsComposer3_ReadbackTest.cpp
@@ -70,9 +70,9 @@
                                                             ContextPriority::HIGH)
                                 .build())));
 
-        ::android::renderengine::DisplaySettings clientCompositionDisplay;
-        clientCompositionDisplay.physicalDisplay = Rect(getDisplayWidth(), getDisplayHeight());
-        clientCompositionDisplay.clip = clientCompositionDisplay.physicalDisplay;
+        mClientCompositionDisplaySettings.physicalDisplay =
+                Rect(getDisplayWidth(), getDisplayHeight());
+        mClientCompositionDisplaySettings.clip = mClientCompositionDisplaySettings.physicalDisplay;
 
         mTestRenderEngine->initGraphicBuffer(
                 static_cast<uint32_t>(getDisplayWidth()), static_cast<uint32_t>(getDisplayHeight()),
@@ -81,7 +81,7 @@
                         static_cast<uint64_t>(common::BufferUsage::CPU_READ_OFTEN) |
                         static_cast<uint64_t>(common::BufferUsage::CPU_WRITE_OFTEN) |
                         static_cast<uint64_t>(common::BufferUsage::GPU_RENDER_TARGET)));
-        mTestRenderEngine->setDisplaySettings(clientCompositionDisplay);
+        mTestRenderEngine->setDisplaySettings(mClientCompositionDisplaySettings);
     }
 
     void TearDown() override {
@@ -163,6 +163,7 @@
     std::unique_ptr<TestRenderEngine> mTestRenderEngine;
     common::PixelFormat mPixelFormat;
     common::Dataspace mDataspace;
+    ::android::renderengine::DisplaySettings mClientCompositionDisplaySettings;
 
     static constexpr uint32_t kClientTargetSlotCount = 64;
 
@@ -271,7 +272,7 @@
                 getDisplayHeight(), common::PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors));
 
         std::vector<std::shared_ptr<TestLayer>> layers = {layer};
@@ -455,7 +456,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_FP16);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<std::shared_ptr<TestLayer>> layers = {layer};
 
@@ -553,7 +554,7 @@
         deviceLayer->setDisplayFrame({0, 0, static_cast<int32_t>(deviceLayer->getWidth()),
                                       static_cast<int32_t>(deviceLayer->getHeight())});
         deviceLayer->setZOrder(10);
-        deviceLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        deviceLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         ASSERT_NO_FATAL_FAILURE(deviceLayer->setBuffer(deviceColors));
         deviceLayer->write(*mWriter);
 
@@ -640,7 +641,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         ASSERT_NO_FATAL_FAILURE(layer->setBuffer(expectedColors));
 
         std::vector<std::shared_ptr<TestLayer>> layers = {layer};
@@ -768,7 +769,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        layer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
         layer->setSourceCrop({0, static_cast<float>(getDisplayHeight() / 2),
                               static_cast<float>(getDisplayWidth()),
                               static_cast<float>(getDisplayHeight())});
@@ -987,7 +988,7 @@
                                                        getDisplayHeight(), PixelFormat::RGBA_8888);
         layer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
         layer->setZOrder(10);
-        layer->setDataspace(Dataspace::UNKNOWN, *mWriter);
+        layer->setDataspace(Dataspace::UNKNOWN);
         ASSERT_NO_FATAL_FAILURE(layer->setBuffer(topLayerPixelColors));
 
         layer->setBlendMode(blendMode);
@@ -1222,7 +1223,7 @@
                                       getDisplayHeight(), mPixelFormat, mDataspace);
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
         mLayer->setTransform(Transform::FLIP_H);
-        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<Color> expectedColors(
                 static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1268,7 +1269,7 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
 
         mLayer->setTransform(Transform::FLIP_V);
-        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<Color> expectedColors(
                 static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1313,7 +1314,7 @@
         ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
 
         mLayer->setTransform(Transform::ROT_180);
-        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode), *mWriter);
+        mLayer->setDataspace(ReadbackHelper::getDataspaceForColorMode(mode));
 
         std::vector<Color> expectedColors(
                 static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
@@ -1342,6 +1343,100 @@
     }
 }
 
+class GraphicsColorManagementCompositionTest
+    : public GraphicsCompositionTestBase,
+      public testing::WithParamInterface<std::tuple<std::string, Dataspace, Dataspace, Dataspace>> {
+  public:
+    void SetUp() override {
+        SetUpBase(std::get<0>(GetParam()));
+        // for some reason only sRGB reliably works
+        mTestColorModes.erase(
+                std::remove_if(mTestColorModes.begin(), mTestColorModes.end(),
+                               [](ColorMode mode) { return mode != ColorMode::SRGB; }),
+                mTestColorModes.end());
+        auto standard = std::get<1>(GetParam());
+        auto transfer = std::get<2>(GetParam());
+        auto range = std::get<3>(GetParam());
+
+        mLayerDataspace = static_cast<Dataspace>(static_cast<int32_t>(standard) |
+                                                 static_cast<int32_t>(transfer) |
+                                                 static_cast<int32_t>(range));
+        ALOGD("Invoking test for dataspace: {%s, %s, %s}", toString(standard).c_str(),
+              toString(transfer).c_str(), toString(range).c_str());
+    }
+
+    void makeLayer() {
+        mLayer = std::make_shared<TestBufferLayer>(
+                mComposerClient, *mTestRenderEngine, getPrimaryDisplayId(), getDisplayWidth(),
+                getDisplayHeight(), common::PixelFormat::RGBA_8888);
+        mLayer->setDisplayFrame({0, 0, getDisplayWidth(), getDisplayHeight()});
+        mLayer->setZOrder(10);
+        mLayer->setAlpha(1.f);
+        mLayer->setDataspace(mLayerDataspace);
+    }
+
+    void fillColor(Color color) {
+        std::vector<Color> baseColors(static_cast<size_t>(getDisplayWidth() * getDisplayHeight()));
+        ReadbackHelper::fillColorsArea(baseColors, getDisplayWidth(),
+                                       common::Rect{.left = 0,
+                                                    .top = 0,
+                                                    .right = getDisplayWidth(),
+                                                    .bottom = getDisplayHeight()},
+                                       color);
+        ASSERT_NO_FATAL_FAILURE(mLayer->setBuffer(baseColors));
+    }
+
+    Dataspace mLayerDataspace;
+    std::shared_ptr<TestBufferLayer> mLayer;
+};
+
+TEST_P(GraphicsColorManagementCompositionTest, ColorConversion) {
+    for (ColorMode mode : mTestColorModes) {
+        EXPECT_TRUE(mComposerClient
+                            ->setColorMode(getPrimaryDisplayId(), mode, RenderIntent::COLORIMETRIC)
+                            .isOk());
+
+        bool isSupported;
+        ASSERT_NO_FATAL_FAILURE(isSupported = getHasReadbackBuffer());
+        if (!isSupported) {
+            GTEST_SUCCEED() << "Readback not supported or unsupported pixelFormat/dataspace";
+            return;
+        }
+
+        mClientCompositionDisplaySettings.outputDataspace =
+                static_cast<::android::ui::Dataspace>(mDataspace);
+        mTestRenderEngine->setDisplaySettings(mClientCompositionDisplaySettings);
+
+        makeLayer();
+        for (auto color : {LIGHT_RED, LIGHT_GREEN, LIGHT_BLUE}) {
+            ALOGD("Testing color: %f, %f, %f, %f with color mode: %d", color.r, color.g, color.b,
+                  color.a, mode);
+            ReadbackBuffer readbackBuffer(getPrimaryDisplayId(), mComposerClient, getDisplayWidth(),
+                                          getDisplayHeight(), mPixelFormat, mDataspace);
+            ASSERT_NO_FATAL_FAILURE(readbackBuffer.setReadbackBuffer());
+            fillColor(color);
+            writeLayers({mLayer});
+            EXPECT_TRUE(mComposerClient->setPowerMode(getPrimaryDisplayId(), PowerMode::ON).isOk());
+
+            ASSERT_TRUE(mReader.takeErrors().empty());
+            mWriter->validateDisplay(getPrimaryDisplayId(), ComposerClientWriter::kNoTimestamp);
+            execute();
+            if (!mReader.takeChangedCompositionTypes(getPrimaryDisplayId()).empty()) {
+                continue;
+            }
+            ASSERT_TRUE(mReader.takeErrors().empty());
+            mWriter->presentDisplay(getPrimaryDisplayId());
+            execute();
+            ASSERT_TRUE(mReader.takeErrors().empty());
+
+            mTestRenderEngine->setRenderLayers({mLayer});
+            ASSERT_NO_FATAL_FAILURE(mTestRenderEngine->drawLayers());
+            ASSERT_NO_FATAL_FAILURE(
+                    mTestRenderEngine->checkColorBuffer(readbackBuffer.getBuffer()));
+        }
+    }
+}
+
 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsCompositionTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, GraphicsCompositionTest,
@@ -1360,5 +1455,17 @@
         testing::ValuesIn(::android::getAidlHalInstanceNames(IComposer::descriptor)),
         ::android::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsColorManagementCompositionTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, GraphicsColorManagementCompositionTest,
+                         testing::Combine(testing::ValuesIn(::android::getAidlHalInstanceNames(
+                                                  IComposer::descriptor)),
+                                          // Only check sRGB, but verify that extended range
+                                          // doesn't trigger any gamma shifts
+                                          testing::Values(Dataspace::STANDARD_BT709),
+                                          testing::Values(Dataspace::TRANSFER_SRGB),
+                                          // Don't test limited range until we send YUV overlays
+                                          testing::Values(Dataspace::RANGE_FULL,
+                                                          Dataspace::RANGE_EXTENDED)));
+
 }  // namespace
 }  // namespace aidl::android::hardware::graphics::composer3::vts