summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/renderengine/Android.bp1
-rw-r--r--libs/renderengine/include/renderengine/DisplaySettings.h16
-rw-r--r--libs/renderengine/skia/SkiaRenderEngine.cpp24
-rw-r--r--libs/renderengine/skia/filters/MouriMap.cpp183
-rw-r--r--libs/renderengine/skia/filters/MouriMap.h81
-rw-r--r--libs/ui/DisplayIdentification.cpp5
-rw-r--r--services/surfaceflinger/FrontEnd/RequestedLayerState.cpp24
-rw-r--r--services/surfaceflinger/ScreenCaptureOutput.cpp13
-rw-r--r--services/surfaceflinger/ScreenCaptureOutput.h5
-rw-r--r--services/surfaceflinger/SurfaceFlinger.cpp47
-rw-r--r--services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp37
-rw-r--r--vulkan/libvulkan/driver.cpp8
-rw-r--r--vulkan/scripts/driver_generator.py2
13 files changed, 412 insertions, 34 deletions
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index c003111ebd..757d935647 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -102,6 +102,7 @@ filegroup {
"skia/filters/GaussianBlurFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/MouriMap.cpp",
"skia/filters/StretchShaderFactory.cpp",
],
}
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index deb62534a5..b640983a55 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -86,6 +86,22 @@ struct DisplaySettings {
// Configures the rendering intent of the output display. This is used for tonemapping.
aidl::android::hardware::graphics::composer3::RenderIntent renderIntent =
aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC;
+
+ // Tonemapping strategy to use for each layer. This is only used for tonemapping HDR source
+ // content
+ enum class TonemapStrategy {
+ // Use a tonemapper defined by libtonemap. This may be OEM-defined as of Android 13, aka
+ // undefined.
+ // This is typically a global tonemapper, designed to match what is on screen.
+ Libtonemap,
+ // Use a local tonemapper. Because local tonemapping uses large intermediate allocations,
+ // this
+ // method is primarily recommended for infrequent rendering that does not need to exactly
+ // match
+ // pixels that are on-screen.
+ Local,
+ };
+ TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index d3441643f6..e62640eb85 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -79,6 +79,7 @@
#include "filters/GaussianBlurFilter.h"
#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
+#include "filters/MouriMap.h"
#include "log/log_main.h"
#include "skia/compat/SkiaBackendTexture.h"
#include "skia/debug/SkiaCapture.h"
@@ -509,9 +510,9 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader(
// Determine later on if we need to leverage the stertch shader within
// surface flinger
const auto& stretchEffect = parameters.layer.stretchEffect;
+ const auto& targetBuffer = parameters.layer.source.buffer.buffer;
auto shader = parameters.shader;
if (stretchEffect.hasEffect()) {
- const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
if (graphicBuffer && parameters.shader) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
@@ -519,6 +520,24 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader(
}
if (parameters.requiresLinearEffect) {
+ const auto format = targetBuffer != nullptr
+ ? std::optional<ui::PixelFormat>(
+ static_cast<ui::PixelFormat>(targetBuffer->getPixelFormat()))
+ : std::nullopt;
+
+ if (parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local) {
+ // TODO: Handle color matrix transforms in linear space.
+ SkImage* image = parameters.shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+ if (image) {
+ static MouriMap kMapper;
+ const float ratio = getHdrRenderType(parameters.layer.sourceDataspace, format) ==
+ HdrRenderType::GENERIC_HDR
+ ? 1.0f
+ : parameters.layerDimmingRatio;
+ return kMapper.mouriMap(getActiveContext(), parameters.shader, ratio);
+ }
+ }
+
auto effect =
shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace,
.outputDataspace = parameters.outputDataSpace,
@@ -712,8 +731,7 @@ void SkiaRenderEngine::drawLayersInternal(
[&](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
+ const float displayDimmingRatio = display.targetLuminanceNits > 0.f && maxLayerWhitePoint > 0.f
? maxLayerWhitePoint / display.targetLuminanceNits
: 1.f;
diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp
new file mode 100644
index 0000000000..7d8b8a5116
--- /dev/null
+++ b/libs/renderengine/skia/filters/MouriMap.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2024 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 "MouriMap.h"
+#include <SkCanvas.h>
+#include <SkColorType.h>
+#include <SkPaint.h>
+#include <SkTileMode.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+namespace {
+sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) {
+ auto [effect, error] = SkRuntimeEffect::MakeForShader(sksl);
+ LOG_ALWAYS_FATAL_IF(!effect, "RuntimeShader error: %s", error.c_str());
+ return effect;
+}
+const SkString kCrosstalkAndChunk16x16(R"(
+ uniform shader bitmap;
+ uniform float hdrSdrRatio;
+ vec4 main(vec2 xy) {
+ float maximum = 0.0;
+ for (int y = 0; y < 16; y++) {
+ for (int x = 0; x < 16; x++) {
+ float3 linear = toLinearSrgb(bitmap.eval(xy * 16 + vec2(x, y)).rgb) * hdrSdrRatio;
+ float maxRGB = max(linear.r, max(linear.g, linear.b));
+ maximum = max(maximum, log2(max(maxRGB, 1.0)));
+ }
+ }
+ return float4(float3(maximum), 1.0);
+ }
+)");
+const SkString kChunk8x8(R"(
+ uniform shader bitmap;
+ vec4 main(vec2 xy) {
+ float maximum = 0.0;
+ for (int y = 0; y < 8; y++) {
+ for (int x = 0; x < 8; x++) {
+ maximum = max(maximum, bitmap.eval(xy * 8 + vec2(x, y)).r);
+ }
+ }
+ return float4(float3(maximum), 1.0);
+ }
+)");
+const SkString kBlur(R"(
+ uniform shader bitmap;
+ vec4 main(vec2 xy) {
+ float C[5];
+ C[0] = 1.0 / 16.0;
+ C[1] = 4.0 / 16.0;
+ C[2] = 6.0 / 16.0;
+ C[3] = 4.0 / 16.0;
+ C[4] = 1.0 / 16.0;
+ float result = 0.0;
+ for (int y = -2; y <= 2; y++) {
+ for (int x = -2; x <= 2; x++) {
+ result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r;
+ }
+ }
+ return float4(float3(exp2(result)), 1.0);
+ }
+)");
+const SkString kTonemap(R"(
+ uniform shader image;
+ uniform shader lux;
+ uniform float scaleFactor;
+ uniform float hdrSdrRatio;
+ vec4 main(vec2 xy) {
+ float localMax = lux.eval(xy * scaleFactor).r;
+ float4 rgba = image.eval(xy);
+ float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio;
+
+ if (localMax <= 1.0) {
+ return float4(fromLinearSrgb(linear), 1.0);
+ }
+
+ float maxRGB = max(linear.r, max(linear.g, linear.b));
+ localMax = max(localMax, maxRGB);
+ float gain = (1 + maxRGB / (localMax * localMax)) / (1 + maxRGB);
+ return float4(fromLinearSrgb(linear * gain), 1.0);
+ }
+)");
+
+// Draws the given runtime shader on a GPU surface and returns the result as an SkImage.
+sk_sp<SkImage> makeImage(SkSurface* surface, const SkRuntimeShaderBuilder& builder) {
+ sk_sp<SkShader> shader = builder.makeShader(nullptr);
+ LOG_ALWAYS_FATAL_IF(!shader, "%s, Failed to make shader!", __func__);
+ SkPaint paint;
+ paint.setShader(std::move(shader));
+ paint.setBlendMode(SkBlendMode::kSrc);
+ surface->getCanvas()->drawPaint(paint);
+ return surface->makeImageSnapshot();
+}
+
+} // namespace
+
+MouriMap::MouriMap()
+ : mCrosstalkAndChunk16x16(makeEffect(kCrosstalkAndChunk16x16)),
+ mChunk8x8(makeEffect(kChunk8x8)),
+ mBlur(makeEffect(kBlur)),
+ mTonemap(makeEffect(kTonemap)) {}
+
+sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input,
+ float hdrSdrRatio) {
+ auto downchunked = downchunk(context, input, hdrSdrRatio);
+ auto localLux = blur(context, downchunked.get());
+ return tonemap(input, localLux.get(), hdrSdrRatio);
+}
+
+sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
+ float hdrSdrRatio) const {
+ SkMatrix matrix;
+ SkImage* image = input->isAImage(&matrix, (SkTileMode*)nullptr);
+ SkRuntimeShaderBuilder crosstalkAndChunk16x16Builder(mCrosstalkAndChunk16x16);
+ crosstalkAndChunk16x16Builder.child("bitmap") = input;
+ crosstalkAndChunk16x16Builder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ // TODO: fp16 might be overkill. Most practical surfaces use 8-bit RGB for HDR UI and 10-bit YUV
+ // for HDR video. These downsample operations compute log2(max(linear RGB, 1.0)). So we don't
+ // care about LDR precision since they all resolve to LDR-max. For appropriately mastered HDR
+ // content that follows BT. 2408, 25% of the bit range for HLG and 42% of the bit range for PQ
+ // are reserved for HDR. This means that we can fit the entire HDR range for 10-bit HLG inside
+ // of 8 bits. We can also fit about half of the range for PQ, but most content does not fill the
+ // entire 10k nit range for PQ. Furthermore, we blur all of this later on anyways, so we might
+ // not need to be so precise. So, it's possible that we could use A8 or R8 instead. If we want
+ // to be really conservative we can try to use R16 or even RGBA1010102 to fake an R10 surface,
+ // which would cut write bandwidth significantly.
+ static constexpr auto kFirstDownscaleAmount = 16;
+ sk_sp<SkSurface> firstDownsampledSurface = context->createRenderTarget(
+ image->imageInfo()
+ .makeWH(std::max(1, image->width() / kFirstDownscaleAmount),
+ std::max(1, image->height() / kFirstDownscaleAmount))
+ .makeColorType(kRGBA_F16_SkColorType));
+ LOG_ALWAYS_FATAL_IF(!firstDownsampledSurface, "%s: Failed to create surface!", __func__);
+ auto firstDownsampledImage =
+ makeImage(firstDownsampledSurface.get(), crosstalkAndChunk16x16Builder);
+ SkRuntimeShaderBuilder chunk8x8Builder(mChunk8x8);
+ chunk8x8Builder.child("bitmap") =
+ firstDownsampledImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions());
+ static constexpr auto kSecondDownscaleAmount = 8;
+ sk_sp<SkSurface> secondDownsampledSurface = context->createRenderTarget(
+ firstDownsampledImage->imageInfo()
+ .makeWH(std::max(1, firstDownsampledImage->width() / kSecondDownscaleAmount),
+ std::max(1, firstDownsampledImage->height() / kSecondDownscaleAmount)));
+ LOG_ALWAYS_FATAL_IF(!secondDownsampledSurface, "%s: Failed to create surface!", __func__);
+ return makeImage(secondDownsampledSurface.get(), chunk8x8Builder);
+}
+sk_sp<SkImage> MouriMap::blur(SkiaGpuContext* context, SkImage* input) const {
+ SkRuntimeShaderBuilder blurBuilder(mBlur);
+ blurBuilder.child("bitmap") =
+ input->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
+ sk_sp<SkSurface> blurSurface = context->createRenderTarget(input->imageInfo());
+ LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__);
+ return makeImage(blurSurface.get(), blurBuilder);
+}
+sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux,
+ float hdrSdrRatio) const {
+ static constexpr float kScaleFactor = 1.0f / 128.0f;
+ SkRuntimeShaderBuilder tonemapBuilder(mTonemap);
+ tonemapBuilder.child("image") = input;
+ tonemapBuilder.child("lux") =
+ localLux->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone));
+ tonemapBuilder.uniform("scaleFactor") = kScaleFactor;
+ tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ return tonemapBuilder.makeShader();
+}
+} // namespace skia
+} // namespace renderengine
+} // namespace android \ No newline at end of file
diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h
new file mode 100644
index 0000000000..3c0df8abf0
--- /dev/null
+++ b/libs/renderengine/skia/filters/MouriMap.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2024 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 <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include "../compat/SkiaGpuContext.h"
+namespace android {
+namespace renderengine {
+namespace skia {
+/**
+ * MouriMap is a fast, albeit not realtime, tonemapping algorithm optimized for near-exact
+ * preservation of SDR (or, equivalently, LDR) regions, while trying to do an acceptable job of
+ * preserving HDR detail.
+ *
+ * MouriMap is a local tonemapping algorithm, meaning that nearby pixels are taken into
+ * consideration when choosing a tonemapping curve.
+ *
+ * The algorithm conceptually is as follows:
+ * 1. Partition the image into 128x128 chunks, computing the log2(maximum luminance) in each chunk
+ *. a. Maximum luminance is computed as max(R, G, B), where the R, G, B values are in linear
+ *. luminance on a scale defined by the destination color gamut. Max(R, G, B) has been found
+ *. to minimize difference in hue while restricting to typical LDR color volumes. See: Burke,
+ *. Adam & Smith, Michael & Zink, Michael. 2020. Color Volume and Hue-preservation in HDR
+ *. Tone Mapping. SMPTE Motion Imaging Journal.
+ *. b. Each computed luminance is lower-bounded by 1.0 in Skia's color
+ *. management, or 203 nits.
+ * 2. Blur the resulting chunks using a 5x5 gaussian kernel, to smooth out the local luminance map.
+ * 3. Now, for each pixel in the original image:
+ * a. Upsample from the blurred chunks of luminance computed in (2). Call this luminance value
+ *. L: an estimate of the maximum luminance of surrounding pixels.
+ *. b. If the luminance is less than 1.0 (203 nits), then do not modify the pixel value of the
+ *. original image.
+ *. c. Otherwise,
+ *. parameterize a tone-mapping curve using a method described by Chrome:
+ *. https://docs.google.com/document/d/17T2ek1i2R7tXdfHCnM-i5n6__RoYe0JyMfKmTEjoGR8/.
+ *. i. Compute a gain G = (1 + max(linear R, linear G, linear B) / (L * L))
+ *. / (1 + max(linear R, linear G, linear B)). Note the similarity with the 1D curve
+ *. described by Erik Reinhard, Michael Stark, Peter Shirley, and James Ferwerda. 2002.
+ *. Photographic tone reproduction for digital images. ACM Trans. Graph.
+ *. ii. Multiply G by the linear source colors to compute the final colors.
+ *
+ * Because it is a multi-renderpass algorithm requiring multiple off-screen textures, MouriMap is
+ * typically not suitable to be ran "frequently", at high refresh rates (e.g., 120hz). However,
+ * MouriMap is sufficiently fast enough for infrequent composition where preserving SDR detail is
+ * most important, such as for screenshots.
+ */
+class MouriMap {
+public:
+ MouriMap();
+ // Apply the MouriMap tonemmaping operator to the input.
+ // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger
+ // then 1.0 means that there is headroom above the SDR region.
+ sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio);
+
+private:
+ sk_sp<SkImage> downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
+ float hdrSdrRatio) const;
+ sk_sp<SkImage> blur(SkiaGpuContext* context, SkImage* input) const;
+ sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio) const;
+ const sk_sp<SkRuntimeEffect> mCrosstalkAndChunk16x16;
+ const sk_sp<SkRuntimeEffect> mChunk8x8;
+ const sk_sp<SkRuntimeEffect> mBlur;
+ const sk_sp<SkRuntimeEffect> mTonemap;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android \ No newline at end of file
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index 0908ae85a8..e5af7406ed 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -320,6 +320,11 @@ std::optional<PnpId> getPnpId(PhysicalDisplayId displayId) {
std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData(
uint8_t port, const DisplayIdentificationData& data) {
+ if (data.empty()) {
+ ALOGI("Display identification data is empty.");
+ return {};
+ }
+
if (!isEdid(data)) {
ALOGE("Display identification data has unknown format.");
return {};
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 139428aca1..3e8d74094d 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -587,23 +587,22 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
- layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
- layer_state_t::eLayerStackChanged | layer_state_t::eReparent |
+ layer_state_t::eBlurRegionsChanged | layer_state_t::eLayerStackChanged |
+ layer_state_t::eReparent |
(FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
? 0
- : layer_state_t::eAutoRefreshChanged);
+ : (layer_state_t::eAutoRefreshChanged | layer_state_t::eFlagsChanged));
if (s.what & deniedFlags) {
ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
s.what & deniedFlags);
return false;
}
- bool changedFlags = diff(s);
- static constexpr auto deniedChanges = layer_state_t::ePositionChanged |
- layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged |
- layer_state_t::eBackgroundColorChanged | layer_state_t::eMatrixChanged |
- layer_state_t::eCornerRadiusChanged | layer_state_t::eBackgroundBlurRadiusChanged |
- layer_state_t::eBufferTransformChanged |
+ const uint64_t changedFlags = diff(s);
+ const uint64_t deniedChanges = layer_state_t::ePositionChanged | layer_state_t::eAlphaChanged |
+ layer_state_t::eColorTransformChanged | layer_state_t::eBackgroundColorChanged |
+ layer_state_t::eMatrixChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBufferTransformChanged |
layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged |
@@ -611,10 +610,13 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const {
layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged |
layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged |
layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged |
- layer_state_t::eDesiredHdrHeadroomChanged;
+ layer_state_t::eDesiredHdrHeadroomChanged |
+ (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
+ ? layer_state_t::eFlagsChanged
+ : 0);
if (changedFlags & deniedChanges) {
ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__,
- s.what & deniedChanges);
+ changedFlags & deniedChanges);
return false;
}
diff --git a/services/surfaceflinger/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index dd03366bcc..8bb72b8470 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -30,7 +30,7 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp
ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
const compositionengine::Output::ColorProfile&,
bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling,
- args.dimInGammaSpaceForEnhancedScreenshots);
+ args.dimInGammaSpaceForEnhancedScreenshots, args.enableLocalTonemapping);
output->editState().isSecure = args.renderArea.isSecure();
output->editState().isProtected = args.isProtected;
output->setCompositionEnabled(true);
@@ -63,11 +63,13 @@ std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutp
ScreenCaptureOutput::ScreenCaptureOutput(
const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots)
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
+ bool enableLocalTonemapping)
: mRenderArea(renderArea),
mColorProfile(colorProfile),
mRegionSampling(regionSampling),
- mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots) {}
+ mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots),
+ mEnableLocalTonemapping(enableLocalTonemapping) {}
void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) {
auto& outputState = editState();
@@ -88,6 +90,11 @@ renderengine::DisplaySettings ScreenCaptureOutput::generateClientCompositionDisp
aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF;
}
+ if (mEnableLocalTonemapping) {
+ clientCompositionDisplay.tonemapStrategy =
+ renderengine::DisplaySettings::TonemapStrategy::Local;
+ }
+
return clientCompositionDisplay;
}
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 069f458bdb..c233ead575 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -39,6 +39,7 @@ struct ScreenCaptureOutputArgs {
bool treat170mAsSrgb;
bool dimInGammaSpaceForEnhancedScreenshots;
bool isProtected = false;
+ bool enableLocalTonemapping = false;
};
// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
@@ -49,7 +50,8 @@ class ScreenCaptureOutput : public compositionengine::impl::Output {
public:
ScreenCaptureOutput(const RenderArea& renderArea,
const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots);
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
+ bool enableLocalTonemapping);
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
@@ -67,6 +69,7 @@ private:
const compositionengine::Output::ColorProfile& mColorProfile;
const bool mRegionSampling;
const bool mDimInGammaSpaceForEnhancedScreenshots;
+ const bool mEnableLocalTonemapping;
};
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index b8b7a70677..5f81cd45cc 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -6522,12 +6522,17 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const {
uint8_t port;
DisplayIdentificationData data;
if (!getHwComposer().getDisplayIdentificationData(*hwcDisplayId, &port, &data)) {
- result.append("no identification data\n");
+ result.append("no display identification data\n");
+ continue;
+ }
+
+ if (data.empty()) {
+ result.append("empty display identification data\n");
continue;
}
if (!isEdid(data)) {
- result.append("unknown identification data\n");
+ result.append("unknown format for display identification data\n");
continue;
}
@@ -8326,6 +8331,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
captureResults.capturedDataspace = requestedDataspace;
+ const bool enableLocalTonemapping = FlagManager::getInstance().local_tonemap_screenshots() &&
+ !renderArea->getHintForSeamlessTransition();
+
{
Mutex::Autolock lock(mStateLock);
const DisplayDevice* display = nullptr;
@@ -8359,16 +8367,19 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
displayBrightnessNits = sdrWhitePointNits;
} else {
displayBrightnessNits = state.displayBrightnessNits;
- // Only clamp the display brightness if this is not a seamless transition. Otherwise
- // for seamless transitions it's important to match the current display state as the
- // buffer will be shown under these same conditions, and we want to avoid any
- // flickers
- if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
- // Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming
- // the SDR portion. 2.0 chosen by experimentation
- constexpr float kMaxScreenshotHeadroom = 2.0f;
- displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
- displayBrightnessNits);
+
+ if (!enableLocalTonemapping) {
+ // Only clamp the display brightness if this is not a seamless transition.
+ // Otherwise for seamless transitions it's important to match the current
+ // display state as the buffer will be shown under these same conditions, and we
+ // want to avoid any flickers
+ if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
+ // Restrict the amount of HDR "headroom" in the screenshot to avoid
+ // over-dimming the SDR portion. 2.0 chosen by experimentation
+ constexpr float kMaxScreenshotHeadroom = 2.0f;
+ displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
+ displayBrightnessNits);
+ }
}
}
@@ -8400,7 +8411,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected,
layerFEs = copyLayerFEs(), layerStack, regionSampling,
- renderArea = std::move(renderArea), renderIntent]() -> FenceResult {
+ renderArea = std::move(renderArea), renderIntent,
+ enableLocalTonemapping]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
compositionEngine->setRenderEngine(mRenderEngine.get());
@@ -8409,7 +8421,11 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
.renderIntent = renderIntent};
float targetBrightness = 1.0f;
- if (dataspace == ui::Dataspace::BT2020_HLG) {
+ if (enableLocalTonemapping) {
+ // Boost the whole scene so that SDR white is at 1.0 while still communicating the hdr
+ // sdr ratio via display brightness / sdrWhite nits.
+ targetBrightness = sdrWhitePointNits / displayBrightnessNits;
+ } else if (dataspace == ui::Dataspace::BT2020_HLG) {
const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203;
// With a low dimming ratio, don't fit the entire curve. Otherwise mixed content
// will appear way too bright.
@@ -8435,7 +8451,8 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
.treat170mAsSrgb = mTreat170mAsSrgb,
.dimInGammaSpaceForEnhancedScreenshots =
dimInGammaSpaceForEnhancedScreenshots,
- .isProtected = isProtected});
+ .isProtected = isProtected,
+ .enableLocalTonemapping = enableLocalTonemapping});
const float colorSaturation = grayscale ? 0 : 1;
compositionengine::CompositionRefreshArgs refreshArgs{
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index cfc8e99f49..97bd79fcee 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -592,4 +592,41 @@ TEST_F(LayerLifecycleManagerTest, layerSecureChangesSetsVisibilityChangeFlag) {
mLifecycleManager.commitChanges();
}
+TEST_F(LayerLifecycleManagerTest, isSimpleBufferUpdate) {
+ auto layer = rootLayer(1);
+
+ // no buffer changes
+ EXPECT_FALSE(layer->isSimpleBufferUpdate({}));
+
+ {
+ layer_state_t state;
+ state.what = layer_state_t::eBufferChanged;
+ EXPECT_TRUE(layer->isSimpleBufferUpdate(state));
+ }
+
+ {
+ layer_state_t state;
+ state.what = layer_state_t::eReparent | layer_state_t::eBufferChanged;
+ EXPECT_FALSE(layer->isSimpleBufferUpdate(state));
+ }
+
+ {
+ layer_state_t state;
+ state.what = layer_state_t::ePositionChanged | layer_state_t::eBufferChanged;
+ state.x = 9;
+ state.y = 10;
+ EXPECT_FALSE(layer->isSimpleBufferUpdate(state));
+ }
+
+ {
+ layer->x = 9;
+ layer->y = 10;
+ layer_state_t state;
+ state.what = layer_state_t::ePositionChanged | layer_state_t::eBufferChanged;
+ state.x = 9;
+ state.y = 10;
+ EXPECT_TRUE(layer->isSimpleBufferUpdate(state));
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 7ea98f5469..3f89960e32 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -964,14 +964,20 @@ PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) {
PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName) {
const ProcHook* hook = GetProcHook(pName);
+ PFN_vkVoidFunction drv_func = GetData(device).driver.GetDeviceProcAddr(device, pName);
+
if (!hook)
- return GetData(device).driver.GetDeviceProcAddr(device, pName);
+ return drv_func;
if (hook->type != ProcHook::DEVICE) {
ALOGE("internal vkGetDeviceProcAddr called for %s", pName);
return nullptr;
}
+ // Don't hook if we don't have a device entry function below for the core function.
+ if (!drv_func && (hook->extension >= ProcHook::EXTENSION_CORE_1_0))
+ return nullptr;
+
return (GetData(device).hook_extensions[hook->extension]) ? hook->proc
: nullptr;
}
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index 78b550c202..48c0ae9304 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -239,6 +239,8 @@ struct ProcHook {
f.write(gencom.indent(2) + gencom.base_ext_name(ext) + ',\n')
f.write('\n')
+ # EXTENSION_CORE_xxx API list must be the last set of enums after the extensions.
+ # This allows to easily identify "a" core function hook
for version in gencom.version_code_list:
f.write(gencom.indent(2) + 'EXTENSION_CORE_' + version + ',\n')