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

系列文章目录


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

文章目录


前言

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


一、DMS流程

DMS(Display Manager Service)在启动阶段的核心职责是构建显示拓扑并同步渲染状态。其工作流程如下:

1、屏幕发现与抽象 :通过 RSInterface 注册屏幕连接回调。一旦检测到物理屏幕连接,立即实例化 AbsScreen,并通过接口获取分辨率、刷新率等硬件参数完成初始化。

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

cpp 复制代码
/* 这里根据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流程

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

三、横屏旋转异常

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

原因

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

cpp 复制代码
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的宽高已经发生错乱,导致srcRectdstRect 计算错误:

graphic/graphic_2d/rosen/modules/render_service_base/src/pipeline/rs_surface_render_node.cpp

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

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

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创建大小问题就可以解决。

相关推荐
键盘鼓手苏苏2 小时前
Flutter 组件 reaxdb_dart 适配鸿蒙 HarmonyOS 实战:响应式 NoSQL 数据库,构建高性能本地持久化与分布式状态同步架构
flutter·harmonyos·鸿蒙·openharmony·reaxdb_dart
亚历克斯神2 小时前
Flutter for OpenHarmony: Flutter 三方库 mongo_dart 助力鸿蒙应用直连 NoSQL 数据库构建高效的数据流转系统(纯 Dart 驱动方案)
android·数据库·flutter·华为·nosql·harmonyos
加农炮手Jinx2 小时前
Flutter for OpenHarmony:postgres 直连 PostgreSQL 数据库,实现 Dart 原生的高效读写(数据库驱动) 深度解析与鸿蒙适配指南
网络·数据库·flutter·华为·postgresql·harmonyos·鸿蒙
前端不太难2 小时前
鸿蒙 AI App 的技术架构解析
人工智能·架构·harmonyos
ujainu2 小时前
Electron 主进程与渲染进程通信详解:HarmonyOS PC基于 `ipcRenderer.send` 与 `ipcMain.on` 的双向数据传输
javascript·electron·harmonyos
大雷神3 小时前
HarmonyOS APP<玩转React>开源教程七:HarmonyOS 数据存储方案
react.js·开源·harmonyos
枫叶丹43 小时前
【HarmonyOS 6.0】Camera Kit 微距状态监听能力详解
开发语言·华为·harmonyos
科技前沿资讯3 小时前
聚焦AWE 2026:华为鸿蒙智家,定义全场景智慧生活新范式
华为·生活·harmonyos
不吃鱼的猫7484 小时前
【从零开始学 OpenGL:现代图形渲染实战】第01篇-环境搭建与第一个窗口
图形渲染