summaryrefslogtreecommitdiff
path: root/services/surfaceflinger/RefreshRateOverlay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/surfaceflinger/RefreshRateOverlay.cpp')
-rw-r--r--services/surfaceflinger/RefreshRateOverlay.cpp350
1 files changed, 186 insertions, 164 deletions
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index bc32a1d66c..d4435c2818 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -14,283 +14,305 @@
* limitations under the License.
*/
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#pragma clang diagnostic ignored "-Wextra"
-
#include <algorithm>
#include "RefreshRateOverlay.h"
#include "Client.h"
#include "Layer.h"
-#include <gui/IProducerListener.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/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
#undef LOG_TAG
#define LOG_TAG "RefreshRateOverlay"
namespace android {
+namespace {
-void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
- const sp<GraphicBuffer>& buffer,
- uint8_t* pixels) {
- for (int32_t j = r.top; j < r.bottom; j++) {
- if (j >= buffer->getHeight()) {
- break;
- }
+constexpr int kDigitWidth = 64;
+constexpr int kDigitHeight = 100;
+constexpr int kDigitSpace = 16;
- for (int32_t i = r.left; i < r.right; i++) {
- if (i >= buffer->getWidth()) {
- break;
- }
+// Layout is digit, space, digit, space, digit, space, spinner.
+constexpr int kBufferWidth = 4 * kDigitWidth + 3 * kDigitSpace;
+constexpr int kBufferHeight = kDigitHeight;
- uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
- iter[0] = uint8_t(color.r * 255);
- iter[1] = uint8_t(color.g * 255);
- iter[2] = uint8_t(color.b * 255);
- iter[3] = uint8_t(color.a * 255);
- }
- }
+SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& surface) {
+ constexpr float kFrameRate = 0.f;
+ constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+ constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
+
+ return SurfaceComposerClient::Transaction().setFrameRate(surface, kFrameRate, kCompatibility,
+ kSeamlessness);
}
-void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
- const half4& color,
- const sp<GraphicBuffer>& buffer,
- uint8_t* pixels) {
- const Rect rect = [&]() {
+} // namespace
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color,
+ SkCanvas& canvas) {
+ const SkRect rect = [&]() {
switch (segment) {
case Segment::Upper:
- return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+ return SkRect::MakeLTRB(left, 0, left + kDigitWidth, kDigitSpace);
case Segment::UpperLeft:
- return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+ return SkRect::MakeLTRB(left, 0, left + kDigitSpace, kDigitHeight / 2);
case Segment::UpperRight:
- return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
- DIGIT_HEIGHT / 2);
+ return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, 0, left + kDigitWidth,
+ kDigitHeight / 2);
case Segment::Middle:
- return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
- DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+ return SkRect::MakeLTRB(left, kDigitHeight / 2 - kDigitSpace / 2,
+ left + kDigitWidth, kDigitHeight / 2 + kDigitSpace / 2);
case Segment::LowerLeft:
- return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+ return SkRect::MakeLTRB(left, kDigitHeight / 2, left + kDigitSpace, kDigitHeight);
case Segment::LowerRight:
- return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
- DIGIT_HEIGHT);
- case Segment::Buttom:
- return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+ return SkRect::MakeLTRB(left + kDigitWidth - kDigitSpace, kDigitHeight / 2,
+ left + kDigitWidth, kDigitHeight);
+ case Segment::Bottom:
+ return SkRect::MakeLTRB(left, kDigitHeight - kDigitSpace, left + kDigitWidth,
+ kDigitHeight);
}
}();
- drawRect(rect, color, buffer, pixels);
+ SkPaint paint;
+ paint.setColor(color);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas.drawRect(rect, paint);
}
-void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
- const sp<GraphicBuffer>& buffer,
- uint8_t* pixels) {
+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, buffer, pixels);
+ drawSegment(Segment::Upper, left, color, canvas);
if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
- drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ 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, buffer, pixels);
+ 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, buffer, pixels);
+ drawSegment(Segment::Middle, left, color, canvas);
if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
- drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ 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, buffer, pixels);
+ drawSegment(Segment::LowerRight, left, color, canvas);
if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
digit == 9)
- drawSegment(Segment::Buttom, left, color, buffer, pixels);
+ drawSegment(Segment::Bottom, left, color, canvas);
}
-std::vector<sp<GraphicBuffer>> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(
- int number, const half4& color, bool showSpinner) {
+auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color,
+ ui::Transform::RotationFlags rotation,
+ bool showSpinner) -> Buffers {
if (number < 0 || number > 1000) return {};
const auto hundreds = number / 100;
const auto tens = (number / 10) % 10;
const auto ones = number % 10;
- std::vector<sp<GraphicBuffer>> buffers;
- const auto loopCount = showSpinner ? 6 : 1;
- for (int i = 0; i < loopCount; i++) {
+ const size_t loopCount = showSpinner ? 6 : 1;
+
+ Buffers buffers;
+ buffers.reserve(loopCount);
+
+ for (size_t i = 0; i < loopCount; i++) {
+ // Pre-rotate the buffer before it reaches SurfaceFlinger.
+ 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};
+ }
+ }();
+
sp<GraphicBuffer> buffer =
- new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ new GraphicBuffer(static_cast<uint32_t>(bufferWidth),
+ static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888,
+ 1,
GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER |
GRALLOC_USAGE_HW_TEXTURE,
"RefreshRateOverlayBuffer");
+
const status_t bufferStatus = buffer->initCheck();
LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "RefreshRateOverlay: Buffer failed to allocate: %d",
bufferStatus);
- uint8_t* pixels;
- buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
- // Clear buffer content
- drawRect(Rect(BUFFER_WIDTH, BUFFER_HEIGHT), half4(0), buffer, pixels);
+
+ sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(bufferWidth, bufferHeight);
+ SkCanvas* canvas = surface->getCanvas();
+ canvas->setMatrix(canvasTransform);
+
int left = 0;
if (hundreds != 0) {
- drawDigit(hundreds, left, color, buffer, pixels);
+ drawDigit(hundreds, left, color, *canvas);
}
- left += DIGIT_WIDTH + DIGIT_SPACE;
+ left += kDigitWidth + kDigitSpace;
if (tens != 0) {
- drawDigit(tens, left, color, buffer, pixels);
+ drawDigit(tens, left, color, *canvas);
}
- left += DIGIT_WIDTH + DIGIT_SPACE;
+ left += kDigitWidth + kDigitSpace;
- drawDigit(ones, left, color, buffer, pixels);
- left += DIGIT_WIDTH + DIGIT_SPACE;
+ drawDigit(ones, left, color, *canvas);
+ left += kDigitWidth + kDigitSpace;
if (showSpinner) {
switch (i) {
case 0:
- drawSegment(Segment::Upper, left, color, buffer, pixels);
+ drawSegment(Segment::Upper, left, color, *canvas);
break;
case 1:
- drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ drawSegment(Segment::UpperRight, left, color, *canvas);
break;
case 2:
- drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ drawSegment(Segment::LowerRight, left, color, *canvas);
break;
case 3:
- drawSegment(Segment::Buttom, left, color, buffer, pixels);
+ drawSegment(Segment::Bottom, left, color, *canvas);
break;
case 4:
- drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ drawSegment(Segment::LowerLeft, left, color, *canvas);
break;
case 5:
- drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ drawSegment(Segment::UpperLeft, left, color, *canvas);
break;
}
}
+ 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();
- buffers.emplace_back(buffer);
+ buffers.push_back(std::move(buffer));
}
return buffers;
}
-RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger, uint32_t lowFps, uint32_t highFps,
- bool showSpinner)
- : mFlinger(flinger),
- mClient(new Client(&mFlinger)),
+RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner)
+ : mFpsRange(fpsRange),
mShowSpinner(showSpinner),
- mLowFps(lowFps),
- mHighFps(highFps) {
- createLayer();
+ mSurfaceControl(SurfaceComposerClient::getDefault()
+ ->createSurface(String8("RefreshRateOverlay"), kBufferWidth,
+ kBufferHeight, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState)) {
+ if (!mSurfaceControl) {
+ ALOGE("%s: Failed to create buffer state layer", __func__);
+ return;
+ }
+
+ createTransaction(mSurfaceControl)
+ .setLayer(mSurfaceControl, INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl, true)
+ .apply();
}
-bool RefreshRateOverlay::createLayer() {
- int32_t layerId;
- const status_t ret =
- mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
- SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
- &mIBinder, &mGbp, nullptr, &layerId);
- if (ret) {
- ALOGE("failed to create buffer state layer");
- return false;
- }
+auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& {
+ static const Buffers kNoBuffers;
+ if (!mSurfaceControl) return kNoBuffers;
+
+ const auto transformHint =
+ static_cast<ui::Transform::RotationFlags>(mSurfaceControl->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;
+ }
+ }();
- mLayer = mClient->getLayerUser(mIBinder);
- mLayer->setFrameRate(Layer::FrameRate(Fps(0.0f), Layer::FrameRateCompatibility::NoVote));
- mLayer->setIsAtRoot(true);
+ createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply();
- // setting Layer's Z requires resorting layersSortedByZ
- ssize_t idx = mFlinger.mDrawingState.layersSortedByZ.indexOf(mLayer);
- if (mLayer->setLayer(INT32_MAX - 2) && idx >= 0) {
- mFlinger.mDrawingState.layersSortedByZ.removeAt(idx);
- mFlinger.mDrawingState.layersSortedByZ.add(mLayer);
- }
+ BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint});
+ if (it == mBufferCache.end()) {
+ const int minFps = mFpsRange.min.getIntValue();
+ const int maxFps = mFpsRange.max.getIntValue();
- return true;
-}
+ // Clamp to the range. The current fps may be outside of this range if the display has
+ // changed its set of supported refresh rates.
+ const int intFps = std::clamp(fps.getIntValue(), minFps, maxFps);
-const std::vector<std::shared_ptr<renderengine::ExternalTexture>>&
-RefreshRateOverlay::getOrCreateBuffers(uint32_t fps) {
- if (mBufferCache.find(fps) == mBufferCache.end()) {
- // Ensure the range is > 0, so we don't divide by 0.
- const auto rangeLength = std::max(1u, mHighFps - mLowFps);
- // Clip values outside the range [mLowFps, mHighFps]. The current fps may be outside
- // of this range if the display has changed its set of supported refresh rates.
- fps = std::max(fps, mLowFps);
- fps = std::min(fps, mHighFps);
- const auto fpsScale = static_cast<float>(fps - mLowFps) / rangeLength;
- half4 color;
- color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
- color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
- color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
- color.a = ALPHA;
- auto buffers = SevenSegmentDrawer::drawNumber(fps, color, mShowSpinner);
- std::vector<std::shared_ptr<renderengine::ExternalTexture>> textures;
- std::transform(buffers.begin(), buffers.end(), std::back_inserter(textures),
- [&](const auto& buffer) -> std::shared_ptr<renderengine::ExternalTexture> {
- return std::make_shared<
- renderengine::ExternalTexture>(buffer,
- mFlinger.getRenderEngine(),
- renderengine::ExternalTexture::
- Usage::READABLE);
- });
- mBufferCache.emplace(fps, textures);
+ // Ensure non-zero range to avoid division by zero.
+ const float fpsScale = static_cast<float>(intFps - minFps) / std::max(1, maxFps - minFps);
+
+ constexpr SkColor kMinFpsColor = SK_ColorRED;
+ constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
+ constexpr float kAlpha = 0.8f;
+
+ SkColor4f colorBase = SkColor4f::FromColor(kMaxFpsColor) * fpsScale;
+ const SkColor4f minFpsColor = SkColor4f::FromColor(kMinFpsColor) * (1 - fpsScale);
+
+ colorBase.fR = colorBase.fR + minFpsColor.fR;
+ colorBase.fG = colorBase.fG + minFpsColor.fG;
+ colorBase.fB = colorBase.fB + minFpsColor.fB;
+ colorBase.fA = kAlpha;
+
+ const SkColor color = colorBase.toSkColor();
+
+ auto buffers = SevenSegmentDrawer::draw(intFps, color, transformHint, mShowSpinner);
+ it = mBufferCache.try_emplace({intFps, transformHint}, std::move(buffers)).first;
}
- return mBufferCache[fps];
+ return it->second;
}
void RefreshRateOverlay::setViewport(ui::Size viewport) {
constexpr int32_t kMaxWidth = 1000;
- const auto width = std::min(kMaxWidth, std::min(viewport.width, viewport.height));
+ const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
const auto height = 2 * width;
Rect frame((3 * width) >> 4, height >> 5);
frame.offsetBy(width >> 5, height >> 4);
- layer_state_t::matrix22_t matrix;
- matrix.dsdx = frame.getWidth() / static_cast<float>(SevenSegmentDrawer::getWidth());
- matrix.dtdx = 0;
- matrix.dtdy = 0;
- matrix.dsdy = frame.getHeight() / static_cast<float>(SevenSegmentDrawer::getHeight());
- mLayer->setMatrix(matrix, true);
- mLayer->setPosition(frame.left, frame.top);
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ createTransaction(mSurfaceControl)
+ .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0,
+ frame.getHeight() / static_cast<float>(kBufferHeight))
+ .setPosition(mSurfaceControl, frame.left, frame.top)
+ .apply();
}
-void RefreshRateOverlay::setLayerStack(uint32_t stack) {
- mLayer->setLayerStack(stack);
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) {
+ createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply();
}
-void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
- mCurrentFps = fps.getIntValue();
- auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{},
- nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+void RefreshRateOverlay::changeRefreshRate(Fps fps) {
+ mCurrentFps = fps;
+ const auto buffer = getOrCreateBuffers(fps)[mFrame];
+ createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
}
-void RefreshRateOverlay::onInvalidate() {
- if (!mCurrentFps.has_value()) return;
+void RefreshRateOverlay::animate() {
+ if (!mShowSpinner || !mCurrentFps) return;
const auto& buffers = getOrCreateBuffers(*mCurrentFps);
mFrame = (mFrame + 1) % buffers.size();
- auto buffer = buffers[mFrame];
- mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {},
- mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */),
- std::nullopt /* dequeueTime */, FrameTimelineInfo{},
- nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */);
-
- mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
+ const auto buffer = buffers[mFrame];
+ createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply();
}
} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"