diff options
-rw-r--r-- | services/surfaceflinger/Android.bp | 1 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayDevice.cpp | 44 | ||||
-rw-r--r-- | services/surfaceflinger/DisplayDevice.h | 13 | ||||
-rw-r--r-- | services/surfaceflinger/HdrSdrRatioOverlay.cpp | 178 | ||||
-rw-r--r-- | services/surfaceflinger/HdrSdrRatioOverlay.h | 45 | ||||
-rw-r--r-- | services/surfaceflinger/RefreshRateOverlay.cpp | 133 | ||||
-rw-r--r-- | services/surfaceflinger/RefreshRateOverlay.h | 33 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 41 | ||||
-rw-r--r-- | services/surfaceflinger/SurfaceFlinger.h | 9 | ||||
-rw-r--r-- | services/surfaceflinger/Utils/OverlayUtils.h | 146 | ||||
-rw-r--r-- | services/surfaceflinger/tests/Android.bp | 3 | ||||
-rw-r--r-- | services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp | 89 |
12 files changed, 588 insertions, 147 deletions
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 89c80bc83a..326645e596 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -168,6 +168,7 @@ filegroup { "FrameTracer/FrameTracer.cpp", "FrameTracker.cpp", "HdrLayerInfoReporter.cpp", + "HdrSdrRatioOverlay.cpp", "WindowInfosListenerInvoker.cpp", "Layer.cpp", "LayerFE.cpp", diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index f6ca9e2856..32bd890aee 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -37,11 +37,11 @@ #include <configstore/Utils.h> #include <log/log.h> #include <system/window.h> -#include <ui/GraphicTypes.h> #include "Display/DisplaySnapshot.h" #include "DisplayDevice.h" #include "FrontEnd/DisplayInfo.h" +#include "HdrSdrRatioOverlay.h" #include "Layer.h" #include "RefreshRateOverlay.h" #include "SurfaceFlinger.h" @@ -261,6 +261,9 @@ void DisplayDevice::setLayerFilter(ui::LayerFilter filter) { if (mRefreshRateOverlay) { mRefreshRateOverlay->setLayerStack(filter.layerStack); } + if (mHdrSdrRatioOverlay) { + mHdrSdrRatioOverlay->setLayerStack(filter.layerStack); + } } void DisplayDevice::setFlags(uint32_t flags) { @@ -274,10 +277,14 @@ void DisplayDevice::setDisplaySize(int width, int height) { if (mRefreshRateOverlay) { mRefreshRateOverlay->setViewport(size); } + if (mHdrSdrRatioOverlay) { + mHdrSdrRatioOverlay->setViewport(size); + } } void DisplayDevice::setProjection(ui::Rotation orientation, Rect layerStackSpaceRect, Rect orientedDisplaySpaceRect) { + mIsOrientationChanged = mOrientation != orientation; mOrientation = orientation; // We need to take care of display rotation for globalTransform for case if the panel is not @@ -411,6 +418,26 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { capabilities.getDesiredMinLuminance()); } +void DisplayDevice::enableHdrSdrRatioOverlay(bool enable) { + if (!enable) { + mHdrSdrRatioOverlay.reset(); + return; + } + + mHdrSdrRatioOverlay = std::make_unique<HdrSdrRatioOverlay>(); + mHdrSdrRatioOverlay->setLayerStack(getLayerStack()); + mHdrSdrRatioOverlay->setViewport(getSize()); + updateHdrSdrRatioOverlayRatio(mHdrSdrRatio); +} + +void DisplayDevice::updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio) { + ATRACE_CALL(); + mHdrSdrRatio = currentHdrSdrRatio; + if (mHdrSdrRatioOverlay) { + mHdrSdrRatioOverlay->changeHdrSdrRatio(currentHdrSdrRatio); + } +} + void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate, bool showInMiddle) { if (!enable) { @@ -463,10 +490,23 @@ bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredMod return false; } -void DisplayDevice::animateRefreshRateOverlay() { +void DisplayDevice::animateOverlay() { if (mRefreshRateOverlay) { mRefreshRateOverlay->animate(); } + if (mHdrSdrRatioOverlay) { + // hdr sdr ratio is designed to be on the top right of the screen, + // therefore, we need to re-calculate the display's width and height + if (mIsOrientationChanged) { + auto width = getWidth(); + auto height = getHeight(); + if (mOrientation == ui::ROTATION_90 || mOrientation == ui::ROTATION_270) { + std::swap(width, height); + } + mHdrSdrRatioOverlay->setViewport({width, height}); + } + mHdrSdrRatioOverlay->animate(); + } } auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force) diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index dc5f8a85af..e92125a45d 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -55,6 +55,7 @@ namespace android { class Fence; class HWComposer; +class HdrSdrRatioOverlay; class IGraphicBufferProducer; class Layer; class RefreshRateOverlay; @@ -235,13 +236,19 @@ public: return mRefreshRateSelector; } + void animateOverlay(); + // Enables an overlay to be displayed with the current refresh rate void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate, bool showInMiddle) REQUIRES(kMainThreadContext); void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false); bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; } bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired); - void animateRefreshRateOverlay(); + + // Enables an overlay to be display with the hdr/sdr ratio + void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext); + void updateHdrSdrRatioOverlayRatio(float currentHdrSdrRatio); + bool isHdrSdrRatioOverlayEnabled() const { return mHdrSdrRatioOverlay != nullptr; } nsecs_t getVsyncPeriodFromHWC() const; @@ -271,6 +278,7 @@ private: const ui::Rotation mPhysicalOrientation; ui::Rotation mOrientation = ui::ROTATION_0; + bool mIsOrientationChanged = false; // Allow nullopt as initial power mode. using TracedPowerMode = TracedOrdinal<hardware::graphics::composer::hal::PowerMode>; @@ -297,6 +305,9 @@ private: std::shared_ptr<scheduler::RefreshRateSelector> mRefreshRateSelector; std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay; + std::unique_ptr<HdrSdrRatioOverlay> mHdrSdrRatioOverlay; + // This parameter is only used for hdr/sdr ratio overlay + float mHdrSdrRatio = 1.0f; mutable std::mutex mActiveModeLock; ActiveModeInfo mDesiredActiveMode GUARDED_BY(mActiveModeLock); diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp new file mode 100644 index 0000000000..2c0f5180cc --- /dev/null +++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp @@ -0,0 +1,178 @@ +/** + * Copyright (C) 2023 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. + */ +// #define LOG_NDEBUG 0 +#include <algorithm> + +#include "HdrSdrRatioOverlay.h" + +#include <SkSurface.h> + +#undef LOG_TAG +#define LOG_TAG "HdrSdrRatioOverlay" + +namespace android { + +void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCanvas& canvas) { + if (!isfinite(number) || number >= 10.f) return; + // We assume that the number range is [1.f, 10.f) + // and the decimal places are 2. + int value = static_cast<int>(number * 100); + SegmentDrawer::drawDigit(value / 100, left, color, canvas); + + left += kDigitWidth + kDigitSpace; + SegmentDrawer::drawSegment(SegmentDrawer::Segment::DecimalPoint, left, color, canvas); + left += kDigitWidth + kDigitSpace; + + SegmentDrawer::drawDigit((value / 10) % 10, left, color, canvas); + left += kDigitWidth + kDigitSpace; + SegmentDrawer::drawDigit(value % 10, left, color, canvas); +} + +sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color, + ui::Transform::RotationFlags rotation) { + SkMatrix canvasTransform = SkMatrix(); + const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> { + switch (rotation) { + case ui::Transform::ROT_90: + canvasTransform.setTranslate(kBufferHeight, 0); + canvasTransform.preRotate(90.f); + return {kBufferHeight, kBufferWidth}; + case ui::Transform::ROT_270: + canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f); + return {kBufferHeight, kBufferWidth}; + default: + return {kBufferWidth, kBufferHeight}; + } + }(); + + const auto kUsageFlags = static_cast<uint64_t>( + GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE); + sp<GraphicBuffer> buffer = + sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth), + static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888, + 1u, kUsageFlags, "HdrSdrRatioOverlay"); + + const status_t bufferStatus = buffer->initCheck(); + LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d", + bufferStatus); + + sk_sp<SkSurface> surface = + SkSurfaces::Raster(SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight)); + SkCanvas* canvas = surface->getCanvas(); + canvas->setMatrix(canvasTransform); + + drawNumber(currentHdrSdrRatio, 0, color, *canvas); + + void* pixels = nullptr; + buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); + + const SkImageInfo& imageInfo = surface->imageInfo(); + const size_t dstRowBytes = buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel()); + + canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0); + buffer->unlock(); + return buffer; +} + +HdrSdrRatioOverlay::HdrSdrRatioOverlay() + : mSurfaceControl( + SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) { + if (!mSurfaceControl) { + ALOGE("%s: Failed to create buffer state layer", __func__); + return; + } + SurfaceComposerClient::Transaction() + .setLayer(mSurfaceControl->get(), INT32_MAX - 2) + .setTrustedOverlay(mSurfaceControl->get(), true) + .apply(); +} + +void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) { + mCurrentHdrSdrRatio = currentHdrSdrRatio; + animate(); +} + +void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) { + SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply(); +} + +void HdrSdrRatioOverlay::setViewport(ui::Size viewport) { + constexpr int32_t kMaxWidth = 1000; + const auto width = std::min({kMaxWidth, viewport.width, viewport.height}); + const auto height = 2 * width; + Rect frame((5 * width) >> 4, height >> 5); + // set the ratio frame to the top right of the screen + frame.offsetBy(viewport.width - frame.width(), height >> 4); + + SurfaceComposerClient::Transaction() + .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth), + 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight)) + .setPosition(mSurfaceControl->get(), frame.left, frame.top) + .apply(); +} + +auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const sp<GraphicBuffer> { + static const sp<GraphicBuffer> kNoBuffer; + if (!mSurfaceControl) return kNoBuffer; + + const auto transformHint = + static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint()); + + // Tell SurfaceFlinger about the pre-rotation on the buffer. + const auto transform = [&] { + switch (transformHint) { + case ui::Transform::ROT_90: + return ui::Transform::ROT_270; + case ui::Transform::ROT_270: + return ui::Transform::ROT_90; + default: + return ui::Transform::ROT_0; + } + }(); + + SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply(); + + constexpr SkColor kMinRatioColor = SK_ColorBLUE; + constexpr SkColor kMaxRatioColor = SK_ColorGREEN; + constexpr float kAlpha = 0.8f; + + // 9.f is picked here as ratio range, given that we assume that + // hdr/sdr ratio is [1.f, 10.f) + const float scale = currentHdrSdrRatio / 9.f; + + SkColor4f colorBase = SkColor4f::FromColor(kMaxRatioColor) * scale; + const SkColor4f minRatioColor = SkColor4f::FromColor(kMinRatioColor) * (1 - scale); + + colorBase.fR = colorBase.fR + minRatioColor.fR; + colorBase.fG = colorBase.fG + minRatioColor.fG; + colorBase.fB = colorBase.fB + minRatioColor.fB; + colorBase.fA = kAlpha; + + const SkColor color = colorBase.toSkColor(); + + auto buffer = draw(currentHdrSdrRatio, color, transformHint); + return buffer; +} + +void HdrSdrRatioOverlay::animate() { + if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return; + + SurfaceComposerClient::Transaction() + .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio)) + .apply(); +} + +} // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.h b/services/surfaceflinger/HdrSdrRatioOverlay.h new file mode 100644 index 0000000000..8a2586e9f8 --- /dev/null +++ b/services/surfaceflinger/HdrSdrRatioOverlay.h @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2023 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 "Utils/OverlayUtils.h" + +#include <ui/Size.h> +#include <utils/StrongPointer.h> + +class SkCanvas; + +namespace android { +class HdrSdrRatioOverlay { +public: + HdrSdrRatioOverlay(); + void setLayerStack(ui::LayerStack); + void setViewport(ui::Size); + void animate(); + void changeHdrSdrRatio(float currentRatio); + +private: + float mCurrentHdrSdrRatio = 1.f; + + static sp<GraphicBuffer> draw(float currentHdrSdrRatio, SkColor, ui::Transform::RotationFlags); + static void drawNumber(float number, int left, SkColor, SkCanvas&); + + const sp<GraphicBuffer> getOrCreateBuffers(float currentHdrSdrRatio); + + const std::unique_ptr<SurfaceControlHolder> mSurfaceControl; +}; +} // namespace android diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 607bec2e3f..577211f461 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -16,104 +16,20 @@ #include <algorithm> -#include "BackgroundExecutor.h" #include "Client.h" #include "Layer.h" #include "RefreshRateOverlay.h" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#include <SkCanvas.h> -#include <SkPaint.h> -#pragma clang diagnostic pop -#include <SkBlendMode.h> -#include <SkRect.h> #include <SkSurface.h> -#include <gui/SurfaceControl.h> #undef LOG_TAG #define LOG_TAG "RefreshRateOverlay" namespace android { -namespace { -constexpr int kDigitWidth = 64; -constexpr int kDigitHeight = 100; -constexpr int kDigitSpace = 16; - -constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1; -constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace; -constexpr int kBufferHeight = kDigitHeight; - -} // namespace - -SurfaceControlHolder::~SurfaceControlHolder() { - // Hand the sp<SurfaceControl> to the helper thread to release the last - // reference. This makes sure that the SurfaceControl is destructed without - // SurfaceFlinger::mStateLock held. - BackgroundExecutor::getInstance().sendCallbacks( - {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }}); -} - -void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color, - SkCanvas& canvas) { - const SkRect rect = [&]() { - switch (segment) { - case Segment::Upper: - return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace); - case Segment::UpperLeft: - return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2); - case Segment::UpperRight: - return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth, - kDigitHeight / 2); - case Segment::Middle: - return SkRect::MakeLTRB(left, kDigitHeight / 2 - kDigitSpace / 2, - left + kDigitWidth, kDigitHeight / 2 + kDigitSpace / 2); - case Segment::LowerLeft: - return SkRect::MakeLTRB(left, kDigitHeight / 2, left + kDigitSpace, kDigitHeight); - case Segment::LowerRight: - return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2, - left + kDigitWidth, kDigitHeight); - case Segment::Bottom: - return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth, - kDigitHeight); - } - }(); - - SkPaint paint; - paint.setColor(color); - paint.setBlendMode(SkBlendMode::kSrc); - canvas.drawRect(rect, paint); -} - -void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, SkColor color, - SkCanvas& canvas) { - if (digit < 0 || digit > 9) return; - - if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 || - digit == 8 || digit == 9) - drawSegment(Segment::Upper, left, color, canvas); - if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) - drawSegment(Segment::UpperLeft, left, color, canvas); - if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 || - digit == 8 || digit == 9) - drawSegment(Segment::UpperRight, left, color, canvas); - if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || - digit == 9) - drawSegment(Segment::Middle, left, color, canvas); - if (digit == 0 || digit == 2 || digit == 6 || digit == 8) - drawSegment(Segment::LowerLeft, left, color, canvas); - if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || - digit == 7 || digit == 8 || digit == 9) - drawSegment(Segment::LowerRight, left, color, canvas); - if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 || - digit == 9) - drawSegment(Segment::Bottom, left, color, canvas); -} - -auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, SkColor color, - ui::Transform::RotationFlags rotation, - ftl::Flags<Features> features) -> Buffers { +auto RefreshRateOverlay::draw(int displayFps, int renderFps, SkColor color, + ui::Transform::RotationFlags rotation, ftl::Flags<Features> features) + -> Buffers { const size_t loopCount = features.test(Features::Spinner) ? 6 : 1; Buffers buffers; @@ -159,22 +75,27 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, if (features.test(Features::Spinner)) { switch (i) { case 0: - drawSegment(Segment::Upper, left, color, *canvas); + SegmentDrawer::drawSegment(SegmentDrawer::Segment::Upper, left, color, *canvas); break; case 1: - drawSegment(Segment::UpperRight, left, color, *canvas); + SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperRight, left, color, + *canvas); break; case 2: - drawSegment(Segment::LowerRight, left, color, *canvas); + SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerRight, left, color, + *canvas); break; case 3: - drawSegment(Segment::Bottom, left, color, *canvas); + SegmentDrawer::drawSegment(SegmentDrawer::Segment::Bottom, left, color, + *canvas); break; case 4: - drawSegment(Segment::LowerLeft, left, color, *canvas); + SegmentDrawer::drawSegment(SegmentDrawer::Segment::LowerLeft, left, color, + *canvas); break; case 5: - drawSegment(Segment::UpperLeft, left, color, *canvas); + SegmentDrawer::drawSegment(SegmentDrawer::Segment::UpperLeft, left, color, + *canvas); break; } } @@ -200,34 +121,27 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int displayFps, int renderFps, return buffers; } -void RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, int left, SkColor color, - SkCanvas& canvas) { +void RefreshRateOverlay::drawNumber(int number, int left, SkColor color, SkCanvas& canvas) { if (number < 0 || number >= 1000) return; if (number >= 100) { - drawDigit(number / 100, left, color, canvas); + SegmentDrawer::drawDigit(number / 100, left, color, canvas); } left += kDigitWidth + kDigitSpace; if (number >= 10) { - drawDigit((number / 10) % 10, left, color, canvas); + SegmentDrawer::drawDigit((number / 10) % 10, left, color, canvas); } left += kDigitWidth + kDigitSpace; - drawDigit(number % 10, left, color, canvas); -} - -std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() { - sp<SurfaceControl> surfaceControl = - SurfaceComposerClient::getDefault() - ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceBufferState); - return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl)); + SegmentDrawer::drawDigit(number % 10, left, color, canvas); } RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> features) - : mFpsRange(fpsRange), mFeatures(features), mSurfaceControl(createSurfaceControlHolder()) { + : mFpsRange(fpsRange), + mFeatures(features), + mSurfaceControl( + SurfaceControlHolder::createSurfaceControlHolder(String8("RefreshRateOverlay"))) { if (!mSurfaceControl) { ALOGE("%s: Failed to create buffer state layer", __func__); return; @@ -296,8 +210,7 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> co const SkColor color = colorBase.toSkColor(); - auto buffers = SevenSegmentDrawer::draw(displayIntFps, renderIntFps, color, transformHint, - mFeatures); + auto buffers = draw(displayIntFps, renderIntFps, color, transformHint, mFeatures); it = mBufferCache .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers)) .first; diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index 0b89b8e3a1..65c61cb4ca 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -16,12 +16,12 @@ #pragma once -#include <SkColor.h> +#include "Utils/OverlayUtils.h" + #include <vector> #include <ftl/flags.h> #include <ftl/small_map.h> -#include <gui/SurfaceComposerClient.h> #include <ui/LayerStack.h> #include <ui/Size.h> #include <ui/Transform.h> @@ -34,22 +34,8 @@ class SkCanvas; namespace android { class GraphicBuffer; -class SurfaceControl; class SurfaceFlinger; -// Helper class to delete the SurfaceControl on a helper thread as -// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held. -class SurfaceControlHolder { -public: - explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){}; - ~SurfaceControlHolder(); - - const sp<SurfaceControl>& get() const { return mSurfaceControl; } - -private: - sp<SurfaceControl> mSurfaceControl; -}; - class RefreshRateOverlay { public: enum class Features { @@ -70,18 +56,9 @@ public: private: using Buffers = std::vector<sp<GraphicBuffer>>; - class SevenSegmentDrawer { - public: - static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags, - ftl::Flags<Features>); - - private: - enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Bottom }; - - static void drawSegment(Segment, int left, SkColor, SkCanvas&); - static void drawDigit(int digit, int left, SkColor, SkCanvas&); - static void drawNumber(int number, int left, SkColor, SkCanvas&); - }; + static Buffers draw(int displayFps, int renderFps, SkColor, ui::Transform::RotationFlags, + ftl::Flags<Features>); + static void drawNumber(int number, int left, SkColor, SkCanvas&); const Buffers& getOrCreateBuffers(Fps, Fps); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index c46da11a03..8646c0cd02 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1963,6 +1963,11 @@ status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, FTL_FAKE_GUARD(kMainThreadContext, display->stageBrightness(brightness.displayBrightness)); + float currentHdrSdrRatio = + compositionDisplay->editState().displayBrightnessNits / + compositionDisplay->editState().sdrWhitePointNits; + FTL_FAKE_GUARD(kMainThreadContext, + display->updateHdrSdrRatioOverlayRatio(currentHdrSdrRatio)); if (brightness.sdrWhitePointNits / brightness.displayBrightnessNits != currentDimmingRatio) { @@ -2454,10 +2459,10 @@ bool SurfaceFlinger::commit(const scheduler::FrameTarget& pacesetterFrameTarget) mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod); } - if (mRefreshRateOverlaySpinner) { + if (mRefreshRateOverlaySpinner || mHdrSdrRatioOverlay) { Mutex::Autolock lock(mStateLock); if (const auto display = getDefaultDisplayDeviceLocked()) { - display->animateRefreshRateOverlay(); + display->animateOverlay(); } } @@ -6462,9 +6467,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { code == IBinder::SYSPROPS_TRANSACTION) { return OK; } - // Numbers from 1000 to 1042 are currently used for backdoors. The code + // Numbers from 1000 to 1043 are currently used for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1042) { + if (code >= 1000 && code <= 1043) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -6925,6 +6930,24 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r reply->writeInt32(NO_ERROR); return NO_ERROR; } + // hdr sdr ratio overlay + case 1043: { + auto future = mScheduler->schedule( + [&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) { + n = data.readInt32(); + mHdrSdrRatioOverlay = n != 0; + switch (n) { + case 0: + case 1: + enableHdrSdrRatioOverlay(mHdrSdrRatioOverlay); + break; + default: + reply->writeBool(isHdrSdrRatioOverlayEnabled()); + } + }); + future.wait(); + return NO_ERROR; + } } } return err; @@ -8023,6 +8046,16 @@ void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { } } +void SurfaceFlinger::enableHdrSdrRatioOverlay(bool enable) { + for (const auto& [id, display] : mPhysicalDisplays) { + if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) { + if (const auto device = getDisplayDeviceLocked(id)) { + device->enableHdrSdrRatioOverlay(enable); + } + } + } +} + int SurfaceFlinger::getGpuContextPriority() { return getRenderEngine().getContextPriority(); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index d97a7478dd..49aff143e6 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -672,6 +672,8 @@ private: bool mRefreshRateOverlayRenderRate = false; // Show render rate overlay offseted to the middle of the screen (e.g. for circular displays) bool mRefreshRateOverlayShowInMiddle = false; + // Show hdr sdr ratio overlay + bool mHdrSdrRatioOverlay = false; void setDesiredActiveMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock); @@ -1288,7 +1290,6 @@ private: ui::Dataspace mDefaultCompositionDataspace; ui::Dataspace mWideColorGamutCompositionDataspace; ui::Dataspace mColorSpaceAgnosticDataspace; - float mDimmingRatio = -1.f; std::unique_ptr<renderengine::RenderEngine> mRenderEngine; std::atomic<int> mNumTrustedPresentationListeners = 0; @@ -1336,6 +1337,8 @@ private: void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext); + void enableHdrSdrRatioOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext); + // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; @@ -1381,6 +1384,10 @@ private: return hasDisplay( [](const auto& display) { return display.isRefreshRateOverlayEnabled(); }); } + bool isHdrSdrRatioOverlayEnabled() const REQUIRES(mStateLock) { + return hasDisplay( + [](const auto& display) { return display.isHdrSdrRatioOverlayEnabled(); }); + } std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots( std::optional<ui::LayerStack> layerStack, uint32_t uid, std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)> diff --git a/services/surfaceflinger/Utils/OverlayUtils.h b/services/surfaceflinger/Utils/OverlayUtils.h new file mode 100644 index 0000000000..0ef0be1101 --- /dev/null +++ b/services/surfaceflinger/Utils/OverlayUtils.h @@ -0,0 +1,146 @@ +/** + * Copyright (C) 2023 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. + */ + +// #define LOG_NDEBUG 0 +#pragma once + +#include "BackgroundExecutor.h" + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#include <SkCanvas.h> +#include <SkPaint.h> +#pragma clang diagnostic pop + +#include <gui/SurfaceComposerClient.h> +#include <utils/StrongPointer.h> + +namespace android { + +inline constexpr int kDigitWidth = 64; +inline constexpr int kDigitHeight = 100; +inline constexpr int kDigitSpace = 16; + +// HdrSdrRatioOverlay re-uses this value though it doesn't really need such amount buffer. +// for output good-looking and code conciseness. +inline constexpr int kMaxDigits = /*displayFps*/ 3 + /*renderFps*/ 3 + /*spinner*/ 1; +inline constexpr int kBufferWidth = kMaxDigits * kDigitWidth + (kMaxDigits - 1) * kDigitSpace; +inline constexpr int kBufferHeight = kDigitHeight; + +class SurfaceControl; + +// Helper class to delete the SurfaceControl on a helper thread as +// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held. +class SurfaceControlHolder { +public: + explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){}; + + ~SurfaceControlHolder() { + // Hand the sp<SurfaceControl> to the helper thread to release the last + // reference. This makes sure that the SurfaceControl is destructed without + // SurfaceFlinger::mStateLock held. + BackgroundExecutor::getInstance().sendCallbacks( + {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }}); + } + + static std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder(const String8& name) { + sp<SurfaceControl> surfaceControl = + SurfaceComposerClient::getDefault() + ->createSurface(name, kBufferWidth, kBufferHeight, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl)); + } + + const sp<SurfaceControl>& get() const { return mSurfaceControl; } + +private: + sp<SurfaceControl> mSurfaceControl; +}; + +// Helper class to draw digit and decimal point. +class SegmentDrawer { +public: + enum class Segment { + Upper, + UpperLeft, + UpperRight, + Middle, + LowerLeft, + LowerRight, + Bottom, + DecimalPoint + }; + static void drawSegment(Segment segment, int left, SkColor color, SkCanvas& canvas) { + const SkRect rect = [&]() { + switch (segment) { + case Segment::Upper: + return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace); + case Segment::UpperLeft: + return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2.); + case Segment::UpperRight: + return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth, + kDigitHeight / 2.); + case Segment::Middle: + return SkRect::MakeLTRB(left, kDigitHeight / 2. - kDigitSpace / 2., + left + kDigitWidth, + kDigitHeight / 2. + kDigitSpace / 2.); + case Segment::LowerLeft: + return SkRect::MakeLTRB(left, kDigitHeight / 2., left + kDigitSpace, + kDigitHeight); + case Segment::LowerRight: + return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2., + left + kDigitWidth, kDigitHeight); + case Segment::Bottom: + return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth, + kDigitHeight); + case Segment::DecimalPoint: + return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitSpace, + kDigitHeight); + } + }(); + + SkPaint paint; + paint.setColor(color); + paint.setBlendMode(SkBlendMode::kSrc); + canvas.drawRect(rect, paint); + } + + static void drawDigit(int digit, int left, SkColor color, SkCanvas& canvas) { + if (digit < 0 || digit > 9) return; + + if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 || + digit == 8 || digit == 9) + drawSegment(Segment::Upper, left, color, canvas); + if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) + drawSegment(Segment::UpperLeft, left, color, canvas); + if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 || + digit == 8 || digit == 9) + drawSegment(Segment::UpperRight, left, color, canvas); + if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || + digit == 9) + drawSegment(Segment::Middle, left, color, canvas); + if (digit == 0 || digit == 2 || digit == 6 || digit == 8) + drawSegment(Segment::LowerLeft, left, color, canvas); + if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || + digit == 7 || digit == 8 || digit == 9) + drawSegment(Segment::LowerRight, left, color, canvas); + if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 || + digit == 9) + drawSegment(Segment::Bottom, left, color, canvas); + } +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 62b539a888..b5168b0f01 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -37,8 +37,9 @@ cc_test { "DisplayConfigs_test.cpp", "DisplayEventReceiver_test.cpp", "EffectLayer_test.cpp", - "LayerBorder_test.cpp", + "HdrSdrRatioOverlay_test.cpp", "InvalidHandles_test.cpp", + "LayerBorder_test.cpp", "LayerCallback_test.cpp", "LayerRenderTypeTransaction_test.cpp", "LayerState_test.cpp", diff --git a/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp new file mode 100644 index 0000000000..77a8f9c79b --- /dev/null +++ b/services/surfaceflinger/tests/HdrSdrRatioOverlay_test.cpp @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2023 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 <thread> + +#include <gtest/gtest.h> + +#include <gui/SurfaceComposerClient.h> +#include <private/gui/ComposerService.h> +#include <chrono> + +using ::std::literals::chrono_literals::operator""s; + +static constexpr int kHdrSdrRatioOverlayCode = 1043; +static constexpr int kHdrSdrRatioOverlayEnable = 1; +static constexpr int kHdrSdrRatioOverlayDisable = 0; +static constexpr int kHdrSdrRatioOverlayQuery = 2; + +// These values must match the ones we used for developer options in +// com.android.settings.development.ShowHdrSdrRatioPreferenceController +static_assert(kHdrSdrRatioOverlayCode == 1043); +static_assert(kHdrSdrRatioOverlayEnable == 1); +static_assert(kHdrSdrRatioOverlayDisable == 0); +static_assert(kHdrSdrRatioOverlayQuery == 2); + +namespace android { + +namespace { +void sendCommandToSf(int command, Parcel& reply) { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + Parcel request; + request.writeInterfaceToken(String16("android.ui.ISurfaceComposer")); + request.writeInt32(command); + ASSERT_EQ(NO_ERROR, + IInterface::asBinder(sf)->transact(kHdrSdrRatioOverlayCode, request, &reply)); +} + +bool isOverlayEnabled() { + Parcel reply; + sendCommandToSf(kHdrSdrRatioOverlayQuery, reply); + return reply.readBool(); +} + +void waitForOverlay(bool enabled) { + static constexpr auto kTimeout = std::chrono::nanoseconds(1s); + static constexpr auto kIterations = 10; + for (int i = 0; i < kIterations; i++) { + if (enabled == isOverlayEnabled()) { + return; + } + std::this_thread::sleep_for(kTimeout / kIterations); + } +} + +void toggleOverlay(bool enabled) { + if (enabled == isOverlayEnabled()) { + return; + } + + Parcel reply; + const auto command = enabled ? kHdrSdrRatioOverlayEnable : kHdrSdrRatioOverlayDisable; + sendCommandToSf(command, reply); + waitForOverlay(enabled); + ASSERT_EQ(enabled, isOverlayEnabled()); +} + +} // namespace + +TEST(HdrSdrRatioOverlayTest, enableAndDisableOverlay) { + toggleOverlay(true); + toggleOverlay(false); + + toggleOverlay(true); + toggleOverlay(false); +} + +} // namespace android |