Loading services/surfaceflinger/RefreshRateOverlay.cpp +152 −7 Original line number Diff line number Diff line Loading @@ -18,26 +18,139 @@ #include "Client.h" #include "Layer.h" #include <gui/IProducerListener.h> #undef LOG_TAG #define LOG_TAG "RefreshRateOverlay" namespace android { 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; } for (int32_t i = r.left; i < r.right; i++) { if (i >= buffer->getWidth()) { break; } 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); } } } void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels) { const Rect rect = [&]() { switch (segment) { case Segment::Upper: return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE); case Segment::UpperLeft: return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2); case Segment::UpperRight: return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH, DIGIT_HEIGHT / 2); case Segment::Middle: return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH, DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2); case Segment::LowerLeft: return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT); 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); } }(); drawRect(rect, color, buffer, pixels); } void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels) { 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); if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) drawSegment(Segment::UpperLeft, left, color, buffer, pixels); if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 || digit == 8 || digit == 9) drawSegment(Segment::UpperRight, left, color, buffer, pixels); if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) drawSegment(Segment::Middle, left, color, buffer, pixels); if (digit == 0 || digit == 2 || digit == 6 || digit == 8) drawSegment(Segment::LowerLeft, left, color, buffer, pixels); 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); if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 || digit == 9) drawSegment(Segment::Buttom, left, color, buffer, pixels); } sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, const half4& color) { if (number < 0 || number > 1000) return nullptr; const auto hundreds = number / 100; const auto tens = (number / 10) % 10; const auto ones = number % 10; sp<GraphicBuffer> buffer = new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_WRITE_RARELY, "RefreshRateOverlayBuffer"); uint8_t* pixels; buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); int left = 0; if (hundreds != 0) { drawDigit(hundreds, left, color, buffer, pixels); left += DIGIT_WIDTH + DIGIT_SPACE; } if (tens != 0) { drawDigit(tens, left, color, buffer, pixels); left += DIGIT_WIDTH + DIGIT_SPACE; } drawDigit(ones, left, color, buffer, pixels); buffer->unlock(); return buffer; } RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger) : mFlinger(flinger), mClient(new Client(&mFlinger)) { createLayer(); primeCache(); } bool RefreshRateOverlay::createLayer() { const status_t ret = mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor, LayerMetadata(), &mIBinder, &mGbp, nullptr); mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(), PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(), &mIBinder, &mGbp, nullptr); if (ret) { ALOGE("failed to create color layer"); ALOGE("failed to create buffer state layer"); return false; } Mutex::Autolock _l(mFlinger.mStateLock); mLayer = mClient->getLayerUser(mIBinder); mLayer->setCrop_legacy(Rect(50, 70, 200, 100)); // setting Layer's Z requires resorting layersSortedByZ ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer); Loading @@ -49,9 +162,41 @@ bool RefreshRateOverlay::createLayer() { return true; } void RefreshRateOverlay::primeCache() { auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates(); if (allRefreshRates.size() == 1) { auto fps = allRefreshRates.begin()->second.fps; half4 color = {LOW_FPS_COLOR, ALPHA}; mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color)); return; } std::vector<uint32_t> supportedFps; supportedFps.reserve(allRefreshRates.size()); for (auto [ignored, refreshRate] : allRefreshRates) { supportedFps.push_back(refreshRate.fps); } std::sort(supportedFps.begin(), supportedFps.end()); const auto mLowFps = supportedFps[0]; const auto mHighFps = supportedFps[supportedFps.size() - 1]; for (auto fps : supportedFps) { const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps); 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; mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color)); } } void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) { const half3& color = (refreshRate.fps > 65.0f) ? GREEN : RED; mLayer->setColor(color); auto buffer = mBufferCache[refreshRate.fps]; mLayer->setBuffer(buffer, 0, 0, {}); mLayer->setFrame(Rect(20, 120, 20 + SevenSegmentDrawer::getWidth(), 120 + SevenSegmentDrawer::getHeight())); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } Loading services/surfaceflinger/RefreshRateOverlay.h +30 −2 Original line number Diff line number Diff line Loading @@ -28,7 +28,32 @@ public: void changeRefreshRate(const RefreshRate& refreshRate); private: class SevenSegmentDrawer { public: static sp<GraphicBuffer> drawNumber(int number, const half4& color); static uint32_t getHeight() { return BUFFER_HEIGHT; } static uint32_t getWidth() { return BUFFER_WIDTH; } private: enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom }; static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels); static void drawSegment(Segment segment, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels); static void drawDigit(int digit, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels); static constexpr uint32_t DIGIT_HEIGHT = 100; static constexpr uint32_t DIGIT_WIDTH = 64; static constexpr uint32_t DIGIT_SPACE = 16; static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT; static constexpr uint32_t BUFFER_WIDTH = 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit }; bool createLayer(); void primeCache(); SurfaceFlinger& mFlinger; sp<Client> mClient; Loading @@ -36,8 +61,11 @@ private: sp<IBinder> mIBinder; sp<IGraphicBufferProducer> mGbp; const half3 RED = half3(1.0f, 0.0f, 0.0f); const half3 GREEN = half3(0.0f, 1.0f, 0.0f); std::unordered_map<int, sp<GraphicBuffer>> mBufferCache; static constexpr float ALPHA = 0.8f; const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f); const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f); }; }; // namespace android services/surfaceflinger/SurfaceFlinger.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -4844,15 +4844,15 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return NO_ERROR; } case 1034: { // TODO(b/129297325): expose this via developer menu option n = data.readInt32(); if (n && !mRefreshRateOverlay && mRefreshRateConfigs->refreshRateSwitchingSupported()) { if (n == 1 && !mRefreshRateOverlay) { mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this); auto current = mRefreshRateConfigs->getCurrentRefreshRate(); mRefreshRateOverlay->changeRefreshRate(current); } else if (!n) { } else if (n == 0) { mRefreshRateOverlay.reset(); } else { reply->writeBool(mRefreshRateOverlay != nullptr); } return NO_ERROR; } Loading Loading
services/surfaceflinger/RefreshRateOverlay.cpp +152 −7 Original line number Diff line number Diff line Loading @@ -18,26 +18,139 @@ #include "Client.h" #include "Layer.h" #include <gui/IProducerListener.h> #undef LOG_TAG #define LOG_TAG "RefreshRateOverlay" namespace android { 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; } for (int32_t i = r.left; i < r.right; i++) { if (i >= buffer->getWidth()) { break; } 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); } } } void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels) { const Rect rect = [&]() { switch (segment) { case Segment::Upper: return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE); case Segment::UpperLeft: return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2); case Segment::UpperRight: return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH, DIGIT_HEIGHT / 2); case Segment::Middle: return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH, DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2); case Segment::LowerLeft: return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT); 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); } }(); drawRect(rect, color, buffer, pixels); } void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels) { 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); if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) drawSegment(Segment::UpperLeft, left, color, buffer, pixels); if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 || digit == 8 || digit == 9) drawSegment(Segment::UpperRight, left, color, buffer, pixels); if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9) drawSegment(Segment::Middle, left, color, buffer, pixels); if (digit == 0 || digit == 2 || digit == 6 || digit == 8) drawSegment(Segment::LowerLeft, left, color, buffer, pixels); 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); if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 || digit == 9) drawSegment(Segment::Buttom, left, color, buffer, pixels); } sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number, const half4& color) { if (number < 0 || number > 1000) return nullptr; const auto hundreds = number / 100; const auto tens = (number / 10) % 10; const auto ones = number % 10; sp<GraphicBuffer> buffer = new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_SW_WRITE_RARELY, "RefreshRateOverlayBuffer"); uint8_t* pixels; buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels)); int left = 0; if (hundreds != 0) { drawDigit(hundreds, left, color, buffer, pixels); left += DIGIT_WIDTH + DIGIT_SPACE; } if (tens != 0) { drawDigit(tens, left, color, buffer, pixels); left += DIGIT_WIDTH + DIGIT_SPACE; } drawDigit(ones, left, color, buffer, pixels); buffer->unlock(); return buffer; } RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger) : mFlinger(flinger), mClient(new Client(&mFlinger)) { createLayer(); primeCache(); } bool RefreshRateOverlay::createLayer() { const status_t ret = mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor, LayerMetadata(), &mIBinder, &mGbp, nullptr); mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(), PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(), &mIBinder, &mGbp, nullptr); if (ret) { ALOGE("failed to create color layer"); ALOGE("failed to create buffer state layer"); return false; } Mutex::Autolock _l(mFlinger.mStateLock); mLayer = mClient->getLayerUser(mIBinder); mLayer->setCrop_legacy(Rect(50, 70, 200, 100)); // setting Layer's Z requires resorting layersSortedByZ ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer); Loading @@ -49,9 +162,41 @@ bool RefreshRateOverlay::createLayer() { return true; } void RefreshRateOverlay::primeCache() { auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates(); if (allRefreshRates.size() == 1) { auto fps = allRefreshRates.begin()->second.fps; half4 color = {LOW_FPS_COLOR, ALPHA}; mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color)); return; } std::vector<uint32_t> supportedFps; supportedFps.reserve(allRefreshRates.size()); for (auto [ignored, refreshRate] : allRefreshRates) { supportedFps.push_back(refreshRate.fps); } std::sort(supportedFps.begin(), supportedFps.end()); const auto mLowFps = supportedFps[0]; const auto mHighFps = supportedFps[supportedFps.size() - 1]; for (auto fps : supportedFps) { const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps); 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; mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color)); } } void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) { const half3& color = (refreshRate.fps > 65.0f) ? GREEN : RED; mLayer->setColor(color); auto buffer = mBufferCache[refreshRate.fps]; mLayer->setBuffer(buffer, 0, 0, {}); mLayer->setFrame(Rect(20, 120, 20 + SevenSegmentDrawer::getWidth(), 120 + SevenSegmentDrawer::getHeight())); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } Loading
services/surfaceflinger/RefreshRateOverlay.h +30 −2 Original line number Diff line number Diff line Loading @@ -28,7 +28,32 @@ public: void changeRefreshRate(const RefreshRate& refreshRate); private: class SevenSegmentDrawer { public: static sp<GraphicBuffer> drawNumber(int number, const half4& color); static uint32_t getHeight() { return BUFFER_HEIGHT; } static uint32_t getWidth() { return BUFFER_WIDTH; } private: enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom }; static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels); static void drawSegment(Segment segment, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels); static void drawDigit(int digit, int left, const half4& color, const sp<GraphicBuffer>& buffer, uint8_t* pixels); static constexpr uint32_t DIGIT_HEIGHT = 100; static constexpr uint32_t DIGIT_WIDTH = 64; static constexpr uint32_t DIGIT_SPACE = 16; static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT; static constexpr uint32_t BUFFER_WIDTH = 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit }; bool createLayer(); void primeCache(); SurfaceFlinger& mFlinger; sp<Client> mClient; Loading @@ -36,8 +61,11 @@ private: sp<IBinder> mIBinder; sp<IGraphicBufferProducer> mGbp; const half3 RED = half3(1.0f, 0.0f, 0.0f); const half3 GREEN = half3(0.0f, 1.0f, 0.0f); std::unordered_map<int, sp<GraphicBuffer>> mBufferCache; static constexpr float ALPHA = 0.8f; const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f); const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f); }; }; // namespace android
services/surfaceflinger/SurfaceFlinger.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -4844,15 +4844,15 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r return NO_ERROR; } case 1034: { // TODO(b/129297325): expose this via developer menu option n = data.readInt32(); if (n && !mRefreshRateOverlay && mRefreshRateConfigs->refreshRateSwitchingSupported()) { if (n == 1 && !mRefreshRateOverlay) { mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this); auto current = mRefreshRateConfigs->getCurrentRefreshRate(); mRefreshRateOverlay->changeRefreshRate(current); } else if (!n) { } else if (n == 0) { mRefreshRateOverlay.reset(); } else { reply->writeBool(mRefreshRateOverlay != nullptr); } return NO_ERROR; } Loading