欢迎光临
我们一直在努力

鸿蒙 6.0横屏显示时画面旋转错误

系列文章目录


在这里插入图片描述

针对源码解析一:旋转值来解决一个bug

文章目录

  • 系列文章目录
  • 前言
  • 一、DMS流程
  • 二、RS流程
  • 三、横屏旋转异常
    • 原因
  • 解决方式

前言

提示:初始化流程可以参考 源码解析一


一、DMS流程

DMS(Display Manager Service)在启动阶段的核心职责是构建显示拓扑并同步渲染状态。其工作流程如下: 1、屏幕发现与抽象:通过 RSInterface 注册屏幕连接回调。一旦检测到物理屏幕连接,立即实例化 AbsScreen,并通过接口获取分辨率、刷新率等硬件参数完成初始化。

2、组管理与节点创建:创建 ScreenGroup 并将 AbsScreen 纳入管理。随后,为屏幕初始化对应的 RSDisplayNode,并向 RenderService 提交事务以确立渲染树节点。

/* 这里根据rotationAfter数值来设旋转值给node */
void AbstractScreenController::SetDisplayNode(Rotation rotationAfter,
const std::shared_ptr<RSDisplayNode>& displayNode, struct ScreenRect srect)
{
displayNode->SetRotation(90.f * static_cast<uint32_t>(rotationAfter)); // 90.f is base degree
/* 这里有node 的setFrame和setBounds 两个Rect */
displayNode->SetFrame(srect.x, srect.y, srect.w, srect.h);
displayNode->SetBounds(srect.x, srect.y, srect.w, srect.h);
}

3、逻辑显示构建:基于初始化完成的 AbsScreen,创建 AbstractDisplay 对象。该对象不仅继承物理屏的参数,还依据宽高比例计算虚拟像素比 (Virtual Pixel Ratio),最终广播“Display 就绪”事件通知各订阅方。 4、窗口系统联动:WMS 监听 DMS 的 Display 变更事件(如尺寸调整、横竖屏切换)。收到通知后,WMS 驱动 WindowNodeContainer 更新 Display 状态并触发全局窗口重布局。 5、生命周期协同:当触发 BEFORE_SUSPEND 事件(如进入锁屏)时,WMS 经由 WindowNodeContainer 将所有窗口 (WindowImpl) 状态置为 STATE_FROZEN,同时通知 AMS 将持有窗口的 Ability 迁移至后台,确保系统资源有序释放。

二、RS流程

  • 启动准备:RSRenderService::Init() 初始化主线程、ScreenManager 和运行时环境,但不创建屏幕。
  • 屏幕绑定:DMS 调用 CreateDisplayNode(screenId),RS 通过 HDI 查询屏参,在 DMS 要求下 RSDisplayNode 和 RSPhysicalScreenProcessor。
  • 渲染提交:UI 调用 Flush() 触发合成,RSPhysicalScreenProcessor 收集图层并调用 CommitLayers() 提交到 HDI 硬件显示。
  • 三、横屏旋转异常

    现象:屏幕旋转配置未生效,且显示画面出现异常裁剪,呈现为正方形。

    原因

    1、node并没有传旋转值给到 RS,这里具体是RSRenderServiceVisitor:

    void RSRenderServiceVisitor::PrepareScreenRenderNode(RSScreenRenderNode& node)
    {
    /* 获取id */
    currentVisitDisplay_ = node.GetScreenId();
    displayHasSecSurface_.emplace(currentVisitDisplay_, false);
    sptr<RSScreenManager> screenManager = CreateOrGetScreenManager();
    if (!screenManager) {
    RS_LOGE("PrepareScreenRenderNode ScreenManager is nullptr");
    return;
    }
    node.SetScreenInfo(screenManager->QueryScreenInfo(node.GetScreenId()));
    ScreenInfo curScreenInfo = screenManager->QueryScreenInfo(node.GetScreenId());
    offsetX_ = curScreenInfo.offsetX;
    offsetY_ = curScreenInfo.offsetY;
    UpdateScreenNodeCompositeType(node, curScreenInfo);

    ResetSurfaceNodeAttrsInScreenNode(node);

    curScreenNode_ = node.shared_from_this()->ReinterpretCastTo<RSScreenRenderNode>();

    /* 此处直接获取 Frame 的宽高作为逻辑分辨率,缺失了对屏幕旋转状态的校验 */
    int32_t logicalScreenWidth = static_cast<int32_t>(node.GetRenderProperties().GetFrameWidth());
    int32_t logicalScreenHeight = static_cast<int32_t>(node.GetRenderProperties().GetFrameHeight());
    if (logicalScreenWidth <= 0 || logicalScreenHeight <= 0) {
    logicalScreenWidth = static_cast<int32_t>(curScreenInfo.width);
    logicalScreenHeight = static_cast<int32_t>(curScreenInfo.height);
    }
    ...
    /* 创建画布后已经和物理宽高一致,导致buffer宽高与物理不一致。 */
    CreateCanvas(logicalScreenWidth, logicalScreenHeight);
    ...
    }

    2、屏幕被裁剪成正方形:由于buffer的宽高已经发生错乱,导致srcRect和dstRect 计算错误: graphic/graphic_2d/rosen/modules/render_service_base/src/pipeline/rs_surface_render_node.cpp

    void RSSurfaceRenderNode::PrepareRenderBeforeChildren(RSPaintFilterCanvas& canvas)
    {
    auto deviceClipRect = canvas.GetDeviceClipBounds();
    UpdateSrcRect(canvas, deviceClipRect);
    RectI dstRect = { deviceClipRect.GetLeft() + offsetX_, deviceClipRect.GetTop() + offsetY_,
    deviceClipRect.GetWidth(), deviceClipRect.GetHeight() };
    }

    void RSSurfaceRenderNode::UpdateSrcRect(const Drawing::Canvas& canvas, const Drawing::RectI& dstRect,
    bool hasRotation)
    {
    ...
    auto localClipRect = RSPaintFilterCanvas::GetLocalClipBounds(canvas, &dstRect).value_or(Drawing::Rect());
    const RSProperties& properties = GetRenderProperties();
    /* 下面就是由于 比较都会被设为高度(默认为横屏情况)*/
    int left = std::clamp<int>(localClipRect.GetLeft(), 0, properties.GetBoundsWidth());
    int top = std::clamp<int>(localClipRect.GetTop(), 0, properties.GetBoundsHeight());
    int width = std::clamp<int>(std::ceil(localClipRect.GetWidth() RECT_CEIL_DEVIATION), 0,
    std::ceil(properties.GetBoundsWidth() left));
    int height = std::clamp<int>(std::ceil(localClipRect.GetHeight() RECT_CEIL_DEVIATION), 0,
    std::ceil(properties.GetBoundsHeight() top));
    RectI srcRect = {left, top, width, height};
    SetSrcRect(srcRect);
    ...
    }
    }

    故导致buffer被裁剪成正方形!


    解决方式

    1、针对旋转问题: rosen/modules/render_service/core/pipeline/rs_processor.cpp

    @@ 105,8 +106,21 @@ bool RSProcessor::Init(RSScreenRenderNode& node, int32_t offsetX, int32_t offset
    mirroredId_ = mirroredId;
    screenInfo_ = screenManager->QueryScreenInfo(node.GetScreenId());

    auto mirrorNode = node.GetMirrorSource().lock();
    CalculateScreenTransformMatrix(mirrorNode ? *mirrorNode : node);
    + auto children = node.GetChildrenList();
    + if (!children.empty()) {
    + std::shared_ptr<RSLogicalDisplayRenderNode> displayNode = nullptr;
    + for (const auto& child : children) {
    + if (auto node = child.lock()) {
    + displayNode = node->ReinterpretCastTo<RSLogicalDisplayRenderNode>();
    + break;
    + }
    + }
    + if (displayNode) {
    + screenInfo_.rotation = displayNode->GetRotation();
    + auto mirrorNode = displayNode->GetMirrorSource().lock();
    + CalculateScreenTransformMatrix(mirrorNode ? *mirrorNode : *displayNode);
    + }
    + }

    2、针对裁剪问题: main_thread/rs_render_service_visitor.cpp

    @@ 65,8 +65,9 @@ void RSRenderServiceVisitor::PrepareScreenRenderNode(RSScreenRenderNode& node)
    RS_LOGE("PrepareScreenRenderNode ScreenManager is nullptr");
    return;
    }
    node.SetScreenInfo(screenManager->QueryScreenInfo(node.GetScreenId()));
    +
    ScreenInfo curScreenInfo = screenManager->QueryScreenInfo(node.GetScreenId());
    + node.SetScreenInfo(curScreenInfo);
    offsetX_ = curScreenInfo.offsetX;
    offsetY_ = curScreenInfo.offsetY;
    UpdateScreenNodeCompositeType(node, curScreenInfo);
    @@ 74,15 +75,32 @@ void RSRenderServiceVisitor::PrepareScreenRenderNode(RSScreenRenderNode& node)
    ResetSurfaceNodeAttrsInScreenNode(node);

    curScreenNode_ = node.shared_from_this()->ReinterpretCastTo<RSScreenRenderNode>();
    + auto& boundsGeoPtr = (node.GetRenderProperties().GetBoundsGeometry());
    + RSBaseRenderUtil::SetNeedClient(boundsGeoPtr && boundsGeoPtr->IsNeedClientCompose());
    + PrepareChildren(node);
    + node.GetCurAllSurfaces().clear();
    + node.CollectSurface(node.shared_from_this(), node.GetCurAllSurfaces(), false, false);
    +}

    +void RSRenderServiceVisitor::PrepareLogicalDisplayRenderNode(RSLogicalDisplayRenderNode& node)
    +{
    + sptr<RSScreenManager> screenManager = CreateOrGetScreenManager();
    + if (!screenManager) {
    + RS_LOGE("PrepareScreenRenderNode ScreenManager is nullptr");
    + return;
    + }
    + ScreenInfo curScreenInfo = screenManager->QueryScreenInfo(node.GetScreenId());
    int32_t logicalScreenWidth = static_cast<int32_t>(node.GetRenderProperties().GetFrameWidth());
    int32_t logicalScreenHeight = static_cast<int32_t>(node.GetRenderProperties().GetFrameHeight());
    if (logicalScreenWidth <= 0 || logicalScreenHeight <= 0) {
    logicalScreenWidth = static_cast<int32_t>(curScreenInfo.width);
    logicalScreenHeight = static_cast<int32_t>(curScreenInfo.height);
    + auto rotation = node.GetRotation();
    + if (rotation == ScreenRotation::ROTATION_90 || rotation == ScreenRotation::ROTATION_270) {
    + std::swap(logicalScreenWidth, logicalScreenHeight);
    + }
    }

    if (node.IsMirrorScreen()) {
    + if (node.IsMirrorDisplay()) {
    auto mirrorSource = node.GetMirrorSource();
    auto existingSource = mirrorSource.lock();
    if (!existingSource) {
    @@ 93,18 +111,9 @@ void RSRenderServiceVisitor::PrepareScreenRenderNode(RSScreenRenderNode& node)
    CreateCanvas(logicalScreenWidth, logicalScreenHeight, true);
    }
    PrepareChildren(*existingSource);
    } else {
    auto& boundsGeoPtr = (node.GetRenderProperties().GetBoundsGeometry());
    RSBaseRenderUtil::SetNeedClient(boundsGeoPtr && boundsGeoPtr->IsNeedClientCompose());
    CreateCanvas(logicalScreenWidth, logicalScreenHeight);
    PrepareChildren(node);
    + return;
    }
    node.GetCurAllSurfaces().clear();
    node.CollectSurface(node.shared_from_this(), node.GetCurAllSurfaces(), false, false);
    }

    void RSRenderServiceVisitor::PrepareLogicalDisplayRenderNode(RSLogicalDisplayRenderNode& node)
    {
    + CreateCanvas(logicalScreenWidth, logicalScreenHeight);
    PrepareChildren(node);
    }

    @@ 139,7 +148,13 @@ void RSRenderServiceVisitor::ProcessScreenRenderNode(RSScreenRenderNode& node)
    return;
    }

    if (node.IsMirrorScreen()) {
    + ProcessChildren(node);
    + processor_->PostProcess();
    +}
    +
    +void RSRenderServiceVisitor::ProcessLogicalDisplayRenderNode(RSLogicalDisplayRenderNode& node)
    +{
    + if (node.IsMirrorDisplay()) {
    auto mirrorSource = node.GetMirrorSource();
    auto existingSource = mirrorSource.lock();
    if (!existingSource) {
    @@ 156,12 +171,7 @@ void RSRenderServiceVisitor::ProcessScreenRenderNode(RSScreenRenderNode& node)
    } else {
    ProcessChildren(node);
    }
    processor_->PostProcess();
    }

    void RSRenderServiceVisitor::ProcessLogicalDisplayRenderNode(RSLogicalDisplayRenderNode& node)
    {
    ProcessChildren(node);
    +
    for (auto& [_, funcs] : foregroundSurfaces_) {
    for (const auto& func : funcs) {
    func();

    主要是旋转值从node获取,并且对逻辑宽高重新修改,对画布canvas创建大小问题就可以解决。

    赞(0)
    未经允许不得转载:171主机测评 » 鸿蒙 6.0横屏显示时画面旋转错误
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址