End-to-end plumbing for dimming SDR layers
Model here is:
* HDR luminance is set to the current display brightness
* SDR luminance is set to the current SDR white point reported by
DisplayManager
Ideally we use scene-referred white points instead, so:
* PQ is always 10k nits
* HLG is always 1k nits
* Everything else is 150-200 nits
So relative dimming thresholds are fixed. But right now this is visually
less jarring (otherwise youtube UI will suddenly dim when autoplaying
HDR video).
Bug: 200310158
Test: Verified that plumbing sdr white point is sent to renderengine
Test: librenderengine_test
Test: libcompositionengine_test
Test: DataspaceUtils_test
Change-Id: I5bcea7941935c43e57cd5434e1ec69b41d31f2b4
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index d395d06..b4cab39 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -57,8 +57,10 @@
// orientation.
uint32_t orientation = ui::Transform::ROT_0;
- // SDR white point, -1f if unknown
- float sdrWhitePointNits = -1.f;
+ // Target luminance of the display. -1f if unknown.
+ // All layers will be dimmed by (max(layer white points) / targetLuminanceNits).
+ // If the target luminance is unknown, then no display-level dimming occurs.
+ float targetLuminanceNits = -1.f;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 715f3f8..702e8b0 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -171,6 +171,12 @@
// Name associated with the layer for debugging purposes.
std::string name;
+
+ // Luminance of the white point for this layer. Used for linear dimming.
+ // Individual layers will be dimmed by (whitePointNits / maxWhitePoint).
+ // If white point nits are unknown, then this layer is assumed to have the
+ // same luminance as the brightest layer in the scene.
+ float whitePointNits = -1.f;
};
// Keep in sync with custom comparison function in
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 376e279..cc90946 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -46,6 +46,7 @@
#include <cmath>
#include <cstdint>
#include <memory>
+#include <numeric>
#include "../gl/GLExtensions.h"
#include "Cache.h"
@@ -612,33 +613,33 @@
AutoBackendTexture::CleanupManager& mMgr;
};
-sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
- const LayerSettings& layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect) {
- const auto stretchEffect = layer.stretchEffect;
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
+ const RuntimeEffectShaderParameters& parameters) {
// The given surface will be stretched by HWUI via matrix transformation
// which gets similar results for most surfaces
// Determine later on if we need to leverage the stertch shader within
// surface flinger
+ const auto& stretchEffect = parameters.layer.stretchEffect;
+ auto shader = parameters.shader;
if (stretchEffect.hasEffect()) {
- const auto targetBuffer = layer.source.buffer.buffer;
+ const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicBuffer && shader) {
+ if (graphicBuffer && parameters.shader) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
}
}
- if (requiresLinearEffect) {
- const ui::Dataspace inputDataspace =
- mUseColorManagement ? layer.sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
- const ui::Dataspace outputDataspace =
- mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
+ if (parameters.requiresLinearEffect) {
+ const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
+ const ui::Dataspace outputDataspace = mUseColorManagement
+ ? parameters.display.outputDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
- auto effect = shaders::LinearEffect{.inputDataspace = inputDataspace,
- .outputDataspace = outputDataspace,
- .undoPremultipliedAlpha = undoPremultipliedAlpha};
+ auto effect =
+ shaders::LinearEffect{.inputDataspace = inputDataspace,
+ .outputDataspace = outputDataspace,
+ .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha};
auto effectIter = mRuntimeEffects.find(effect);
sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -648,16 +649,16 @@
} else {
runtimeEffect = effectIter->second;
}
- float maxLuminance = layer.source.buffer.maxLuminanceNits;
- // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
- // white point
- if (maxLuminance <= 0.f) {
- maxLuminance = display.sdrWhitePointNits;
- }
- return createLinearEffectShader(shader, effect, runtimeEffect, layer.colorTransform,
- display.maxLuminance, maxLuminance);
+ mat4 colorTransform = parameters.layer.colorTransform;
+
+ colorTransform *=
+ mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
+ parameters.layerDimmingRatio, 1.f));
+ return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
+ parameters.display.maxLuminance,
+ parameters.layer.source.buffer.maxLuminanceNits);
}
- return shader;
+ return parameters.shader;
}
void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
@@ -730,6 +731,11 @@
return roundedRect;
}
+static bool equalsWithinMargin(float expected, float value, float margin) {
+ LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
+ return std::abs(expected - value) < margin;
+}
+
void SkiaGLRenderEngine::drawLayersInternal(
const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
@@ -791,6 +797,18 @@
const bool ctModifiesAlpha =
displayColorTransform && !displayColorTransform->isAlphaUnchanged();
+ // Find the max layer white point to determine the max luminance of the scene...
+ const float maxLayerWhitePoint = std::transform_reduce(
+ layers.cbegin(), layers.cend(), 0.f,
+ [](float left, float right) { return std::max(left, right); },
+ [&](const auto& l) { return l.whitePointNits; });
+
+ // ...and compute the dimming ratio if dimming is requested
+ const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
+ maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+ ? maxLayerWhitePoint / display.targetLuminanceNits
+ : 1.f;
+
// Find if any layers have requested blur, we'll use that info to decide when to render to an
// offscreen buffer and when to render to the native buffer.
sk_sp<SkSurface> activeSurface(dstSurface);
@@ -964,11 +982,14 @@
drawShadow(canvas, rrect, layer.shadow);
}
+ const float layerDimmingRatio = layer.whitePointNits <= 0.f
+ ? displayDimmingRatio
+ : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
+
const bool requiresLinearEffect = layer.colorTransform != mat4() ||
(mUseColorManagement &&
needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
- (display.sdrWhitePointNits > 0.f &&
- display.sdrWhitePointNits != display.maxLuminance);
+ !equalsWithinMargin(1.f, layerDimmingRatio, 0.001f);
// quick abort from drawing the remaining portion of the layer
if (layer.skipContentDraw ||
@@ -1067,9 +1088,20 @@
toSkColorSpace(layerDataspace)));
}
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- !item.isOpaque && item.usePremultipliedAlpha,
- requiresLinearEffect));
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = !item.isOpaque &&
+ item.usePremultipliedAlpha,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
+
+ // Turn on dithering when dimming beyond this threshold.
+ static constexpr float kDimmingThreshold = 0.2f;
+ if (layerDimmingRatio <= kDimmingThreshold) {
+ paint.setDither(true);
+ }
paint.setAlphaf(layer.alpha);
} else {
ATRACE_NAME("DrawColor");
@@ -1079,9 +1111,13 @@
.fB = color.b,
.fA = layer.alpha},
toSkColorSpace(layerDataspace));
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- /* undoPremultipliedAlpha */ false,
- requiresLinearEffect));
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = false,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
}
if (layer.disableBlending) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 53792f9..a650313 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -106,12 +106,18 @@
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& shadowSettings);
+
// If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
// Otherwise it returns the input shader.
- sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings& layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect);
+ struct RuntimeEffectShaderParameters {
+ sk_sp<SkShader> shader;
+ const LayerSettings& layer;
+ const DisplaySettings& display;
+ bool undoPremultipliedAlpha;
+ bool requiresLinearEffect;
+ float layerDimmingRatio;
+ };
+ sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
EGLDisplay mEGLDisplay;
EGLContext mEGLContext;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 8259063..eb2b2dc 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -208,6 +208,21 @@
renderengine::ExternalTexture::Usage::WRITEABLE);
}
+ std::shared_ptr<renderengine::ExternalTexture> allocateAndFillSourceBuffer(uint32_t width,
+ uint32_t height,
+ ubyte4 color) {
+ const auto buffer = allocateSourceBuffer(width, height);
+ uint8_t* pixels;
+ buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = color.r;
+ pixels[1] = color.g;
+ pixels[2] = color.b;
+ pixels[3] = color.a;
+ buffer->getBuffer()->unlock();
+ return buffer;
+ }
+
RenderEngineTest() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
@@ -2195,6 +2210,133 @@
expectBufferColor(rect, 0, 128, 0, 128);
}
+TEST_P(RenderEngineTest, testDimming) {
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+ initializeRenderEngine();
+
+ const auto displayRect = Rect(3, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = 1000.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+ const auto redBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(255, 0, 0, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f / 51.f,
+ };
+
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = FloatRect(2.f, 0.f, 3.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = redBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ // When the white point is not set for a layer, just ignore it and treat it as the same
+ // as the max layer
+ .whitePointNits = -1.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer, redLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 5, 255, 1);
+ expectBufferColor(Rect(2, 0, 3, 1), 51, 0, 0, 255, 1);
+}
+
+TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
+ initializeRenderEngine();
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ const auto displayRect = Rect(2, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = -1.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 255, 255);
+}
+
TEST_P(RenderEngineTest, test_isOpaque) {
initializeRenderEngine();
diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/ui/include_types/ui/DataspaceUtils.h
new file mode 100644
index 0000000..a461cb4
--- /dev/null
+++ b/libs/ui/include_types/ui/DataspaceUtils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ui/GraphicTypes.h>
+
+namespace android {
+
+inline bool isHdrDataspace(ui::Dataspace dataspace) {
+ const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 0ee15f2..22fbf45 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -163,3 +163,13 @@
"-Werror",
],
}
+
+cc_test {
+ name: "DataspaceUtils_test",
+ shared_libs: ["libui"],
+ srcs: ["DataspaceUtils_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
new file mode 100644
index 0000000..3e09671
--- /dev/null
+++ b/libs/ui/tests/DataspaceUtils_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "DataspaceUtilsTest"
+
+#include <gtest/gtest.h>
+#include <ui/DataspaceUtils.h>
+
+namespace android {
+
+class DataspaceUtilsTest : public testing::Test {};
+
+TEST_F(DataspaceUtilsTest, isHdrDataspace) {
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_HLG));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG));
+
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR));
+ // scRGB defines a very wide gamut but not an expanded luminance range
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT709));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::ADOBE_RGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_ITU));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_BT2020));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index ac243c0..e77e155 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -134,6 +134,9 @@
// Configure layer settings for using blurs
BlurSetting blurSetting;
+
+ // Requested white point of the layer in nits
+ const float whitePointNits;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index b407267..3571e11 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -76,7 +76,7 @@
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
- virtual void applyClientTargetRequests(const ClientTargetProperty&);
+ virtual void applyClientTargetRequests(const ClientTargetProperty&, float whitePointNits);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 44f754f..c5b6443 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -127,6 +127,9 @@
// SDR white point
float sdrWhitePointNits{-1.f};
+ // White point of the client target
+ float clientTargetWhitePointNits{-1.f};
+
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 7564c54..627b80b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -146,6 +146,10 @@
// Timestamp for when the layer is queued for client composition
nsecs_t clientCompositionTimestamp{0};
+
+ // White point of the layer, in nits.
+ static constexpr float kDefaultWhitePointNits = 200.f;
+ float whitePointNits = kDefaultWhitePointNits;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 02fa49f..4603e6b 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -237,7 +237,8 @@
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
- applyClientTargetRequests(changes->clientTargetProperty);
+ applyClientTargetRequests(changes->clientTargetProperty,
+ changes->clientTargetWhitePointNits);
}
// Determine what type of composition we are doing from the final state
@@ -309,12 +310,14 @@
}
}
-void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty,
+ float whitePointNits) {
if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
return;
}
editState().dataspace = clientTargetProperty.dataspace;
+ editState().clientTargetWhitePointNits = whitePointNits;
getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 6800004..d503153 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -1057,7 +1057,7 @@
clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
? outputState.displayBrightnessNits
: mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
- clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits;
+ clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits;
// Compute the global color transform matrix.
if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
@@ -1217,7 +1217,8 @@
.dataspace = outputDataspace,
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
- .blurSetting = blurSetting};
+ .blurSetting = blurSetting,
+ .whitePointNits = layerState.whitePointNits};
results = layerFE.prepareClientCompositionList(targetSettings);
if (realContentIsVisible && !results.empty()) {
layer->editState().clientCompositionTimestamp = systemTime();
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index cf76183..b010d9f 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -24,6 +24,9 @@
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <cstdint>
+#include "system/graphics-base-v1.0.h"
+
+#include <ui/DataspaceUtils.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -317,6 +320,14 @@
? outputState.targetDataspace
: layerFEState->dataspace;
+ // For hdr content, treat the white point as the display brightness - HDR content should not be
+ // boosted or dimmed.
+ if (isHdrDataspace(state.dataspace)) {
+ state.whitePointNits = getOutput().getState().displayBrightnessNits;
+ } else {
+ state.whitePointNits = getOutput().getState().sdrWhitePointNits;
+ }
+
// These are evaluated every frame as they can potentially change at any
// time.
if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
@@ -479,6 +490,16 @@
ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
to_string(error).c_str(), static_cast<int32_t>(error));
}
+
+ // Don't dim cached layers
+ const auto whitePointNits = outputDependentState.overrideInfo.buffer
+ ? getOutput().getState().displayBrightnessNits
+ : outputDependentState.whitePointNits;
+
+ if (auto error = hwcLayer->setWhitePointNits(whitePointNits); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set white point %f: %s (%d)", getLayerFE().getDebugName(),
+ whitePointNits, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
}
void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ff96d94..2203f22 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -170,6 +170,7 @@
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
+ .targetLuminanceNits = outputState.displayBrightnessNits,
};
LayerFE::ClientCompositionTargetSettings targetSettings{
@@ -182,6 +183,7 @@
.realContentIsVisible = true,
.clearContent = false,
.blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ .whitePointNits = outputState.displayBrightnessNits,
};
std::vector<renderengine::LayerSettings> layerSettings;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index ed235b8..568efce 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -596,6 +596,7 @@
hal::DisplayRequest::FLIP_CLIENT_TARGET,
{{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
{hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+ -1.f,
};
// Since two calls are made to anyLayersRequireClientComposition with different return
@@ -788,15 +789,18 @@
.dataspace = hal::Dataspace::STANDARD_BT470M,
};
+ static constexpr float kWhitePointNits = 800.f;
+
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
EXPECT_CALL(*renderSurface, setBufferPixelFormat(clientTargetProperty.pixelFormat));
EXPECT_CALL(*renderSurface, setBufferDataspace(clientTargetProperty.dataspace));
- mDisplay->applyClientTargetRequests(clientTargetProperty);
+ mDisplay->applyClientTargetRequests(clientTargetProperty, kWhitePointNits);
auto& state = mDisplay->getState();
EXPECT_EQ(clientTargetProperty.dataspace, state.dataspace);
+ EXPECT_EQ(kWhitePointNits, state.clientTargetWhitePointNits);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 9518659..9e08f9e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -71,6 +71,7 @@
MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
MOCK_METHOD3(setLayerGenericMetadata,
Error(const std::string&, bool, const std::vector<uint8_t>&));
+ MOCK_METHOD1(setWhitePointNits, Error(float));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index fd584fd..207c31e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -652,6 +652,22 @@
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
}
+TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) {
+ mOutputState.sdrWhitePointNits = 200.f;
+ mOutputState.displayBrightnessNits = 800.f;
+
+ mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
+ mLayerFEState.isColorspaceAgnostic = false;
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+ EXPECT_EQ(mOutputState.sdrWhitePointNits, mOutputLayer.getState().whitePointNits);
+
+ mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
+ mLayerFEState.isColorspaceAgnostic = false;
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+ EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+}
+
TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
mOutputLayer.editState().forceClientComposition = false;
@@ -728,6 +744,8 @@
static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
+ static constexpr float kWhitePointNits = 200.f;
+ static constexpr float kDisplayBrightnessNits = 400.f;
static const half4 kColor;
static const Rect kDisplayFrame;
@@ -758,6 +776,7 @@
outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
outputLayerState.dataspace = kDataspace;
+ outputLayerState.whitePointNits = kWhitePointNits;
mLayerFEState.blendMode = kBlendMode;
mLayerFEState.alpha = kAlpha;
@@ -770,6 +789,8 @@
mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
mLayerFEState.acquireFence = kFence;
+ mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
+
EXPECT_CALL(mOutput, getDisplayColorProfile())
.WillRepeatedly(Return(&mDisplayColorProfile));
EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
@@ -818,9 +839,11 @@
void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
ui::Dataspace dataspace = kDataspace,
const Region& visibleRegion = kOutputSpaceVisibleRegion,
- const Region& surfaceDamage = kSurfaceDamage) {
+ const Region& surfaceDamage = kSurfaceDamage,
+ float whitePointNits = kWhitePointNits) {
EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setWhitePointNits(whitePointNits)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
.WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
? hal::Error::UNSUPPORTED
@@ -1084,7 +1107,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kSkipAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1100,7 +1123,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kSkipAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls();
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1116,7 +1139,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
@@ -1132,7 +1155,7 @@
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cf63ef5..6d96260 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3023,6 +3023,7 @@
mOutput.mState.usesDeviceComposition = false;
mOutput.mState.reusedClientComposition = false;
mOutput.mState.flipClientTarget = false;
+ mOutput.mState.clientTargetWhitePointNits = kClientTargetLuminanceNits;
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
@@ -3057,6 +3058,7 @@
static constexpr float kDefaultMaxLuminance = 0.9f;
static constexpr float kDefaultAvgLuminance = 0.7f;
static constexpr float kDefaultMinLuminance = 0.1f;
+ static constexpr float kClientTargetLuminanceNits = 200.f;
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
@@ -3424,7 +3426,8 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3435,7 +3438,8 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3444,10 +3448,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
- kDefaultMaxLuminance, kDefaultOutputDataspace,
- kDefaultColorTransformMat,
- kDefaultOutputOrientationFlags})
+ .thenExpectDisplaySettingsUsed(
+ {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+ kDefaultOutputDataspace, kDefaultColorTransformMat,
+ kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3456,10 +3460,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
- kDefaultMaxLuminance, kDefaultOutputDataspace,
- kDefaultColorTransformMat,
- kDefaultOutputOrientationFlags})
+ .thenExpectDisplaySettingsUsed(
+ {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+ kDefaultOutputDataspace, kDefaultColorTransformMat,
+ kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3471,7 +3475,8 @@
.andIfSkipColorTransform(true)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3729,6 +3734,8 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
}
+ static constexpr float kLayerWhitePointNits = 200.f;
+
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
StrictMock<OutputPartialMock> mOutput;
@@ -3768,6 +3775,7 @@
static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
+ static constexpr float kLayerWhitePointNits = 200.f;
static const Rect kDisplayFrame;
static const Rect kDisplayViewport;
@@ -3930,14 +3938,15 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* needs filtering */
- false, /* secure */
- false, /* supports protected content */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
kDisplayViewport,
kDisplayDataspace,
false /* realContentIsVisible */,
true /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -3949,6 +3958,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -3988,6 +3998,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
@@ -3999,6 +4010,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
@@ -4010,6 +4022,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4039,6 +4052,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4050,6 +4064,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4061,6 +4076,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4090,7 +4106,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
-
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4102,6 +4118,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4113,6 +4130,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4141,6 +4159,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4152,6 +4171,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4163,6 +4183,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4179,7 +4200,6 @@
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
protectedContentSupportUsedToGenerateRequests) {
-
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
@@ -4190,6 +4210,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4201,6 +4222,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4212,6 +4234,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4379,6 +4402,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4396,6 +4420,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4428,6 +4453,7 @@
false /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
LayerFE::LayerSettings mShadowSettings;
@@ -4472,6 +4498,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 42b3d97..d5a117a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -55,13 +55,21 @@
}
MATCHER_P(ClientCompositionTargetSettingsSecureEq, expectedSecureSetting, "") {
- *result_listener << "ClientCompositionTargetSettings' SecureSettings aren't equal \n";
+ *result_listener << "ClientCompositionTargetSettings' isSecure bits aren't equal \n";
*result_listener << "expected " << expectedSecureSetting << "\n";
*result_listener << "actual " << arg.isSecure << "\n";
return expectedSecureSetting == arg.isSecure;
}
+MATCHER_P(ClientCompositionTargetSettingsWhitePointEq, expectedWhitePoint, "") {
+ *result_listener << "ClientCompositionTargetSettings' white points aren't equal \n";
+ *result_listener << "expected " << expectedWhitePoint << "\n";
+ *result_listener << "actual " << arg.whitePointNits << "\n";
+
+ return expectedWhitePoint == arg.whitePointNits;
+}
+
static const ui::Size kOutputSize = ui::Size(1, 1);
class CachedSetTest : public testing::Test {
@@ -431,6 +439,54 @@
cachedSet.append(CachedSet(layer3));
}
+TEST_F(CachedSetTest, renderWhitePoint) {
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+
+ mOutputState.displayBrightnessNits = 400.f;
+
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ };
+
+ EXPECT_CALL(*layerFE1,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ mOutputState.isSecure = true;
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+ EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds());
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) {
// Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 200bb65..29e5a74 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1005,12 +1005,19 @@
}
Error AidlComposer::getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* whitePointNits) {
ClientTargetProperty property;
- mReader.takeClientTargetProperty(translate<int64_t>(display), &property);
+ mReader.takeClientTargetProperty(translate<int64_t>(display), &property, whitePointNits);
*outClientTargetProperty = translate<IComposerClient::ClientTargetProperty>(property);
return Error::NONE;
}
+Error AidlComposer::setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) {
+ mWriter.setLayerWhitePointNits(translate<int64_t>(display), translate<int64_t>(layer),
+ whitePointNits);
+ return Error::NONE;
+}
+
} // namespace Hwc2
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 418a8cc..8cae25f 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -194,9 +194,10 @@
bool mandatory, const std::vector<uint8_t>& value) override;
V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
- Error getClientTargetProperty(
- Display display,
- IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
+ Error getClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outClientTargetWhitePointNits) override;
+ Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
private:
// Many public functions above simply write a command into the command
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 3bbce7b..3cc5e5e 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -221,7 +221,11 @@
virtual V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
virtual Error getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) = 0;
+
+ // AIDL Composer
+ virtual Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) = 0;
};
} // namespace android::Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index e21b0da..596666c 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -555,8 +555,10 @@
return static_cast<Error>(intError);
}
-Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
- const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) {
+ const auto error =
+ mComposer.getClientTargetProperty(mId, outClientTargetProperty, outWhitePointNits);
return static_cast<Error>(error);
}
@@ -919,6 +921,16 @@
return static_cast<Error>(intError);
}
+// AIDL HAL
+Error Layer::setWhitePointNits(float whitePointNits) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerWhitePointNits(mDisplay->getId(), mId, whitePointNits);
+ return static_cast<Error>(intError);
+}
+
} // namespace impl
} // namespace HWC2
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index a65efb2..5e0ba06 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -146,7 +146,7 @@
std::vector<hal::ContentType>*) const = 0;
[[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
[[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
- hal::ClientTargetProperty* outClientTargetProperty) = 0;
+ hal::ClientTargetProperty* outClientTargetProperty, float* outWhitePointNits) = 0;
};
namespace impl {
@@ -209,7 +209,8 @@
hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>* outSupportedContentTypes) const override;
hal::Error setContentType(hal::ContentType) override;
- hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
+ hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) override;
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
@@ -288,6 +289,9 @@
// Composer HAL 2.4
[[clang::warn_unused_result]] virtual hal::Error setLayerGenericMetadata(
const std::string& name, bool mandatory, const std::vector<uint8_t>& value) = 0;
+
+ // AIDL HAL
+ [[clang::warn_unused_result]] virtual hal::Error setWhitePointNits(float whitePointNits) = 0;
};
namespace impl {
@@ -331,6 +335,9 @@
hal::Error setLayerGenericMetadata(const std::string& name, bool mandatory,
const std::vector<uint8_t>& value) override;
+ // AIDL HAL
+ hal::Error setWhitePointNits(float whitePointNits) override;
+
private:
// These are references to data owned by HWC2::Device, which will outlive
// this HWC2::Layer, so these references are guaranteed to be valid for
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 06f5df5..d851e22 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -522,11 +522,13 @@
RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
- error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
+ float clientTargetWhitePointNits;
+ error = hwcDisplay->getClientTargetProperty(&clientTargetProperty, &clientTargetWhitePointNits);
outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
std::move(layerRequests),
- std::move(clientTargetProperty)});
+ std::move(clientTargetProperty),
+ clientTargetWhitePointNits});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 0a090da..bd79977 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -79,6 +79,7 @@
DisplayRequests displayRequests;
LayerRequests layerRequests;
ClientTargetProperty clientTargetProperty;
+ float clientTargetWhitePointNits;
};
struct HWCDisplayMode {
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 6c40598..11d41c0 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1164,8 +1164,14 @@
}
Error HidlComposer::getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) {
mReader.takeClientTargetProperty(display, outClientTargetProperty);
+ *outWhitePointNits = -1.f;
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerWhitePointNits(Display, Layer, float) {
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index ad253a2..18c0635 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -304,9 +304,10 @@
bool mandatory, const std::vector<uint8_t>& value) override;
V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
- Error getClientTargetProperty(
- Display display,
- IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
+ Error getClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) override;
+ Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 4606746..b395159 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -46,6 +46,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
+#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
@@ -585,6 +586,8 @@
layerSettings.alpha = alpha;
layerSettings.sourceDataspace = getDataSpace();
+
+ layerSettings.whitePointNits = targetSettings.whitePointNits;
switch (targetSettings.blurSetting) {
case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b7327b2..885d698 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -67,6 +67,7 @@
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
+#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
@@ -2316,12 +2317,7 @@
mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
const auto layerFe = layer->getCompositionEngineLayerFE();
if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
- const Dataspace transfer =
- static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
- const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
- transfer == Dataspace::TRANSFER_HLG);
-
- if (isHdr) {
+ if (isHdrDataspace(layer->getDataSpace())) {
const auto* outputLayer =
compositionDisplay->getOutputLayerForLayer(layerFe);
if (outputLayer) {
@@ -6425,6 +6421,8 @@
BlurSetting::Disabled
: compositionengine::LayerFE::ClientCompositionTargetSettings::
BlurSetting::Enabled,
+ DisplayDevice::sDefaultMaxLumiance,
+
};
std::vector<compositionengine::LayerFE::LayerSettings> results =
layer->prepareClientCompositionList(targetSettings);
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 1ba3c0f..1debd65 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -136,7 +136,9 @@
const std::vector<uint8_t>&));
MOCK_METHOD1(getLayerGenericMetadataKeys,
V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
- MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
+ MOCK_METHOD3(getClientTargetProperty,
+ Error(Display, IComposerClient::ClientTargetProperty*, float*));
+ MOCK_METHOD3(setLayerWhitePointNits, Error(Display, Layer, float));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index fe1544e..83a0996 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -85,7 +85,8 @@
MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *),
(const, override));
MOCK_METHOD(hal::Error, setContentType, (hal::ContentType), (override));
- MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *), (override));
+ MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *, float *),
+ (override));
};
class Layer : public HWC2::Layer {
@@ -116,6 +117,7 @@
MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
MOCK_METHOD(hal::Error, setLayerGenericMetadata,
(const std::string &, bool, const std::vector<uint8_t> &), (override));
+ MOCK_METHOD(hal::Error, setWhitePointNits, (float whitePointNits), (override));
};
} // namespace android::HWC2::mock