Pass display's colorTransform to RE even if HW handles it
Even if the DPU handles the colorTransform, RE needs to know about it
for A8 screen decorations layers. So pass the colorTransform to RE
always. Add a new boolean, specifying whether the DPU handles it, so
existing use cases can continue to ignore it.
RE uses a color matrix to turn transparent pixels in the A8 screen
decorations layer to black, providing antialiasing. If the DPU handles
the colorTransform, that black will then be converted to another color
(e.g. white, if the colorTransform comes from the "Color inversion"
accesibility feature). So RE needs to convert it to a color that the DPU
will then convert back to black, matching plastic next to the screen. Do
this by inverting the upper left 3x3 of the matrix and applying it to
black. If the color matrix is not invertible (note that the one used for
"Color inversion" *is*), leave black unchanged.
If the DPU does not handle the colorTransform, concat the A8 layer's
color matrix with the colorTransform.
Update libcompositionengine_test to use the proper colorTransform.
Add GTEST_SKIP if R8 buffers are not supported (follow-up from
I340de485fd62bea12a5a097f09073c4f7d58fa90).
Bug: 215706351
Bug: 216178319
Test: librenderengine_test
Test: libcompositionengine_test
Test: manual
Change-Id: Ib127620be4e5fc400ac2525c90dea1b9a29bd1f7
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index e197150..add7a94 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -2627,6 +2627,7 @@
const auto r8Buffer = allocateR8Buffer(2, 1);
if (!r8Buffer) {
+ GTEST_SKIP() << "Test is only necessary on devices that support r8";
return;
}
{
@@ -2677,6 +2678,144 @@
expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 255);
expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255);
}
+
+TEST_P(RenderEngineTest, r8_respects_color_transform) {
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+
+ const auto r8Buffer = allocateR8Buffer(2, 1);
+ if (!r8Buffer) {
+ GTEST_SKIP() << "Test is only necessary on devices that support r8";
+ return;
+ }
+ {
+ uint8_t* pixels;
+ r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 0;
+ pixels[1] = 255;
+ r8Buffer->getBuffer()->unlock();
+ }
+
+ const auto rect = Rect(0, 0, 2, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .outputDataspace = ui::Dataspace::SRGB,
+ // Verify that the R8 layer respects the color transform when
+ // deviceHandlesColorTransform is false. This transform converts
+ // pure red to pure green. That will occur when the R8 buffer is
+ // 255. When the R8 buffer is 0, it will still change to black, as
+ // with r8_behaves_as_mask.
+ .colorTransform = mat4(0, 1, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1),
+ .deviceHandlesColorTransform = false,
+ };
+
+ const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255));
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = redBuffer,
+ },
+ },
+ .alpha = 1.0f,
+ };
+ const renderengine::LayerSettings r8Layer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = r8Buffer,
+ },
+ },
+ .alpha = 1.0f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(0, 0, 1, 1), 0, 0, 0, 255);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 255, 0, 255);
+}
+
+TEST_P(RenderEngineTest, r8_respects_color_transform_when_device_handles) {
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+
+ const auto r8Buffer = allocateR8Buffer(2, 1);
+ if (!r8Buffer) {
+ GTEST_SKIP() << "Test is only necessary on devices that support r8";
+ return;
+ }
+ {
+ uint8_t* pixels;
+ r8Buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = 0;
+ pixels[1] = 255;
+ r8Buffer->getBuffer()->unlock();
+ }
+
+ const auto rect = Rect(0, 0, 2, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .outputDataspace = ui::Dataspace::SRGB,
+ // If deviceHandlesColorTransform is true, pixels where the A8
+ // buffer is opaque are unaffected. If the colorTransform is
+ // invertible, pixels where the A8 buffer are transparent have the
+ // inverse applied to them so that the DPU will convert them back to
+ // black. Test with an arbitrary, invertible matrix.
+ .colorTransform = mat4(1, 0, 0, 2,
+ 3, 1, 2, 5,
+ 0, 5, 3, 0,
+ 0, 1, 0, 2),
+ .deviceHandlesColorTransform = true,
+ };
+
+ const auto redBuffer = allocateAndFillSourceBuffer(2, 1, ubyte4(255, 0, 0, 255));
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = redBuffer,
+ },
+ },
+ .alpha = 1.0f,
+ };
+ const renderengine::LayerSettings r8Layer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = r8Buffer,
+ },
+ },
+ .alpha = 1.0f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{redLayer, r8Layer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 0, 2, 1), 255, 0, 0, 255); // Still red.
+ expectBufferColor(Rect(0, 0, 1, 1), 0, 70, 0, 255);
+}
} // namespace renderengine
} // namespace android