鸿蒙图显源码解析一:屏幕旋转流程
本文详细分析了OpenHarmony系统中屏幕旋转的完整流程,整个流程涉及DisplayManager、ScreenController、RenderService和HWC等多个系统模块的协同工作,形成完整的屏幕旋转处理机制。
系列文章目录

文章目录
前言
在 HarmonyOS 中,屏幕的默认方向可以通过display_manager_config.xml 进行配置。
系统启动后,DisplayManager 会读取该配置,并在屏幕连接时计算最终的旋转角度,然后通过 RenderService 与 HWC 完成最终显示。
本文从源码角度梳理 屏幕默认方向 → 旋转计算 → RenderService → HWC 的完整流程。
Harmony 屏幕旋转机制与图形合成流程解析
文章目录
前言
在 OpenHarmony 图形子系统中,屏幕旋转并不是简单地设置一个 rotation 属性,而是涉及多个模块协同工作:
- DisplayManager
- ScreenController
- RenderService
- HDI Backend
- Hardware Composer(HWC)
下面结合源码分析整个旋转流程。
一、默认屏幕方向配置
默认方向通过display_manager_config.xml中关键字段的buildInDefaultOrientation配置。
例如:
<buildInDefaultOrientation>1</buildInDefaultOrientation>
DisplayManager 启动时会读取该配置。
1. 方向配置
enum class Orientation : uint32_t {
BEGIN = 0,
UNSPECIFIED = BEGIN,
VERTICAL = 1,
HORIZONTAL = 2,
REVERSE_VERTICAL = 3,
REVERSE_HORIZONTAL = 4,
SENSOR = 5,
SENSOR_VERTICAL = 6,
SENSOR_HORIZONTAL = 7,
AUTO_ROTATION_RESTRICTED = 8,
AUTO_ROTATION_PORTRAIT_RESTRICTED = 9,
AUTO_ROTATION_LANDSCAPE_RESTRICTED = 10,
LOCKED = 11,
FOLLOW_RECENT = 12,
AUTO_ROTATION_UNSPECIFIED = 13,
USER_ROTATION_PORTRAIT = 14,
USER_ROTATION_LANDSCAPE = 15,
USER_ROTATION_PORTRAIT_INVERTED = 16,
USER_ROTATION_LANDSCAPE_INVERTED = 17,
FOLLOW_DESKTOP = 18,
END = FOLLOW_DESKTOP,
};
2. XML 解析入口
window_manager/dmserver/src/display_manager_config.cpp
bool DisplayManagerConfig::LoadConfigXml()
{
/* 定义文档 */
auto configFilePath = GetConfigPath("etc/window/resources/display_manager_config.xml");
...
/* 读取 buildInDefaultOrientation 到 intNumbersConfig_ */
ReadIntNumbersConfigInfo(curNodePtr);
}
/*window_scene/screen_session_manager/src/screen_scene_config.cpp*/
void ScreenSceneConfig::ParseNodeConfig(const xmlNodePtr& currNode)
{
/* 解析 */
}
二、DisplayManagerService 初始化
window_manager/dmserver/src/display_manager_service.cpp
/* 服务开启 */
DisplayManagerService::Init()
{
/* 解析文档 */
if (DisplayManagerConfig::LoadConfigXml()) {
/* 根据xml 赋值 buildInDefaultOrientation_ */
ConfigureDisplayManagerService();
}
}
在 DisplayManager 服务启动时加载显示配置,核心代码:
void DisplayManagerService::ConfigureDisplayManagerService()
{
/* 获取intNumbersConfig_ */
auto numbersConfig = DisplayManagerConfig::GetIntNumbersConfig();
...
if (numbersConfig.count("buildInDefaultOrientation") != 0) {
Orientation orientation = static_cast<Orientation>(numbersConfig["buildInDefaultOrientation"][0]);
abstractScreenController_->SetBuildInDefaultOrientation(orientation);
}
}
/* window_manager/dmserver/src/abstract_screen_controller.cpp */
void AbstractScreenController::SetBuildInDefaultOrientation(Orientation orientation)
{
buildInDefaultOrientation_ = orientation;
}
该函数会把 XML 中的数值保存到intNumbersConfig_,结构为map<string, vector<int>>
例如:
{
"buildInDefaultOrientation" : [1]
}
从配置中读取默认方向,并传递给 ScreenController。这里只是简单保存默认方向:
buildInDefaultOrientation_,后续在屏幕连接时使用。
三、DMS Server 初始化
window/window_manager/dmserver/src/abstract_screen_controller.cpp
void AbstractScreenController::Init()
{
/* 注册服务 */
TLOGI(WmsLogTag::DMS, "screen controller init");
RegisterRsScreenConnectionChangeListener();
}
/* 注册 RenderService 屏幕连接状态监听 */
void AbstractScreenController::RegisterRsScreenConnectionChangeListener()
{
TLOGI(WmsLogTag::DMS, "RegisterRsScreenConnectionChangeListener");
/* 调用 RenderService 接口 SetScreenChangeCallback 注册回调。
Lambda 捕获 this,当屏幕连接/断开事件触发时,会回调 */
auto res = rsInterface_.SetScreenChangeCallback(
[this](ScreenId rsScreenId, ScreenEvent screenEvent, ScreenChangeReason reason) {
OnRsScreenConnectionChange(rsScreenId, screenEvent);
});
/* 如果注册失败,创建一个lambda任务,再次调用RegisterRsScreenConnectionChangeListener()尝试注册。*/
if (res != StatusCode::SUCCESS) {
auto task = [this] {
RegisterRsScreenConnectionChangeListener();
};
/* 通过 controllerHandler_->PostTask() 延迟 50ms 执行任务,并设置优先级为 HIGH。*/
controllerHandler_->PostTask(task, "wms:RegisterRsScreenConnectionChangeListener",
50, AppExecFwk::EventQueue::Priority::HIGH);
}
}
在 AbstractScreenController 中注册 RenderService 屏幕连接回调,确保屏幕热插拔事件能够被捕获处理,并且在注册失败时通过延迟重试。
void AbstractScreenController::OnRsScreenConnectionChange(ScreenId rsScreenId, ScreenEvent screenEvent)
{
TLOGI(WmsLogTag::DMS, "RS screen event. rsScreenId:%{public}" PRIu64", defaultRsScreenId_:%{public}" PRIu64", "
"event:%{public}u", rsScreenId, static_cast<uint64_t>(defaultRsScreenId_), static_cast<uint32_t>(screenEvent));
/* 连接屏幕的状态,直接进入该函数 */
if (screenEvent == ScreenEvent::CONNECTED) {
auto task = [this, rsScreenId] {
ProcessScreenConnected(rsScreenId);
};
controllerHandler_->PostTask(task, "wms:OnRsScreenConnectionChange", 0, AppExecFwk::EventQueue::Priority::HIGH);
} else if (screenEvent == ScreenEvent::DISCONNECTED) {
auto task = [this, rsScreenId] {
ProcessScreenDisconnected(rsScreenId);
};
...
}
}
作为 RenderService 屏幕事件回调入口,根据事件类型(连接或断开)异步投递任务到控制线程处理屏幕初始化或资源释放。
void AbstractScreenController::ProcessScreenConnected(ScreenId rsScreenId)
{
if (rsScreenId == rsInterface_.GetDefaultScreenId() && absScreen->rsDisplayNode_ != nullptr) {
/* 将方向赋值给屏幕,并计算出rotation */
absScreen->screenRequestedOrientation_ = buildInDefaultOrientation_;
/* 根据屏幕的宽高比(是否为竖屏)和请求的方向,计算出最终的旋转角度 */
Rotation rotationAfter = absScreen->CalcRotation(absScreen->screenRequestedOrientation_);
if (!IsVertical(rotationAfter)) {
std::swap(w, h); /* 交换宽高 */
...
}
absScreen->rsDisplayNode_->SetFrame(x, y, w, h);
absScreen->rsDisplayNode_->SetBounds(x, y, w, h);
/* 使用-90.0f是为了将系统内部的旋转表示转换为RSDisplayNode所需的坐标系方向 */
absScreen->rsDisplayNode_->SetRotation(-90.0f * static_cast<uint32_t>(rotationAfter));
absScreen->SetOrientation(absScreen->screenRequestedOrientation_);
}
处理 RenderService 通知的屏幕连接事件,初始化屏幕对象、加入屏幕组、计算默认旋转、更新 RenderService 显示节点,并广播屏幕连接状态。
但其实从源码看CalcRotation函数会发现,根据长宽比较确定当前是不是竖屏。
整个函数用于判断屏幕旋转后的方向是否为竖屏,辅助屏幕宽高计算和显示节点布局调整。
四、RSProcessor 初始化
RSProcessor是 RenderService(RS)中负责单屏渲染处理的核心类。
graphic/graphic_2d/rosen/modules/render_service/core/pipeline/rs_processor.cpp
bool RSProcessor::Init(RSScreenRenderNode& node, int32_t offsetX, int32_t offsetY, ScreenId mirroredId,
std::shared_ptr<RSBaseRenderEngine> renderEngine)
{
...
auto screenManager = CreateOrGetScreenManager();
...
renderEngine_ = renderEngine;
offsetX_ = offsetX;
offsetY_ = offsetY;
mirroredId_ = mirroredId;
screenInfo_ = screenManager->QueryScreenInfo(node.GetScreenId());
auto mirrorNode = node.GetMirrorSource().lock();
CalculateScreenTransformMatrix(mirrorNode ? *mirrorNode : node);
if (mirroredId_ != INVALID_SCREEN_ID) {
mirroredScreenInfo_ = screenManager->QueryScreenInfo(mirroredId_);
CalculateMirrorAdaptiveMatrix();
}
// set default render frame config
renderFrameConfig_ = RSBaseRenderUtil::GetFrameBufferRequestConfig(screenInfo_);
return true;
}
初始化屏幕渲染处理器,包括绑定渲染引擎、屏幕信息、偏移量、旋转变换矩阵以及镜像屏幕适配矩阵,并设置默认渲染帧配置,保证 RenderService 可以正确在屏幕上渲染内容。
graphic/graphic_2d/rosen/modules/render_service/core/screen_manager/rs_screen_manager.cpp
ScreenInfo RSScreenManager::QueryScreenInfo(ScreenId id) const
{
...
info.id = id;
info.width = screen->Width();
info.height = screen->Height();
info.phyWidth = screen->PhyWidth() ? screen->PhyWidth() : screen->Width();
info.phyHeight = screen->PhyHeight() ? screen->PhyHeight() : screen->Height();
info.offsetX = screen->GetOffsetX();
info.offsetY = screen->GetOffsetY();
info.isSamplingOn = screen->IsSamplingOn();
info.samplingTranslateX = screen->GetSamplingTranslateX();
info.samplingTranslateY = screen->GetSamplingTranslateY();
info.samplingScale = screen->GetSamplingScale();
...
info.skipFrameInterval = screen->GetScreenSkipFrameInterval();
info.expectedRefreshRate = screen->GetScreenExpectedRefreshRate();
info.skipFrameStrategy = screen->GetScreenSkipFrameStrategy();
info.isEqualVsyncPeriod = screen->GetEqualVsyncPeriod();
screen->GetPixelFormat(info.pixelFormat);
screen->GetScreenHDRFormat(info.hdrFormat);
info.whiteList = screen->GetWhiteList();
info.enableVisibleRect = screen->GetEnableVisibleRect();
info.activeRect = screen->GetActiveRect();
info.maskRect = screen->GetMaskRect();
info.reviseRect = screen->GetReviseRect();
return info;
}
查询指定屏幕 ID 的完整屏幕信息,包括尺寸、偏移、采样、色域、状态、刷新策略和显示区域,为屏幕渲染、旋转计算和 RSProcessor 初始化提供基础数据。会发现,RSProcessor 不需要传递旋转值,因为旋转角度由 AbstractScreenController 或屏幕对象管理,RSProcessor 只用屏幕信息(宽高、偏移、物理尺寸)计算变换矩阵即可。旋转是通过矩阵计算间接体现,而不是外部直接传入的参数。
五、ComposerAdapter层初始化
这里先区别:RSPhysicalScreenProcessor 是 物理屏幕渲染处理器,继承自 RSProcessor,对接不同地方:
| 层级 | Init 调用 | 作用 |
|---|---|---|
| RSProcessor | RSProcessor::Init |
初始化屏幕基础数据、变换矩阵、帧缓冲信息 |
| RSPhysicalScreenProcessor | composerAdapter_->Init |
初始化物理屏幕的合成器适配器,将数据映射到 GPU/HWC,注册回调 |
bool RSPhysicalScreenProcessor::Init(RSScreenRenderNode& node, int32_t offsetX, int32_t offsetY, ScreenId mirroredId,
std::shared_ptr<RSBaseRenderEngine> renderEngine)
{
#ifdef RS_ENABLE_GPU
// planning: adapt isRenderThread
if (!RSProcessor::Init(node, offsetX, offsetY, mirroredId, renderEngine)) {
return false;
}
#endif
return composerAdapter_->Init(node, screenInfo_, mirroredScreenInfo_, mirrorAdaptiveCoefficient_,
[this](const auto& surface, const auto& layers) { Redraw(surface, layers); });
}
这里的mirroredScreenInfo_主要是提供给双显以上或者折叠屏。
graphic/graphic_2d/rosen/modules/render_service/core/pipeline/render_thread/rs_composer_adapter.cpp
bool RSComposerAdapter::Init(const RSScreenRenderNode& node, const ScreenInfo& screenInfo,
const ScreenInfo& mirroredScreenInfo, float mirrorAdaptiveCoefficient, const FallbackCallback& cb)
{
/* hdi_backend 初始化,这个对接hwc的hdi后端实现模块 */
hdiBackend_ = HdiBackend::GetInstance();
if (hdiBackend_ == nullptr) {
RS_LOGE("RSComposerAdapter::Init: hdiBackend is nullptr");
return false;
}
auto screenManager = CreateOrGetScreenManager();
if (screenManager == nullptr) {
RS_LOGE("RSComposerAdapter::Init: ScreenManager is nullptr");
return false;
}
output_ = screenManager->GetOutput(ToScreenPhysicalId(screenInfo.id));
if (output_ == nullptr) {
RS_LOGE("RSComposerAdapter::Init: output_ is nullptr");
return false;
}
fallbackCb_ = cb;
auto onPrepareCompleteFunc = [this](auto& surface, const auto& param, void* data) {
OnPrepareComplete(surface, param, data);
};
hdiBackend_->RegPrepareComplete(onPrepareCompleteFunc, this);
offsetX_ = node.GetScreenOffsetX();
offsetY_ = node.GetScreenOffsetY();
screenInfo_ = screenInfo;
mirroredScreenInfo_ = mirroredScreenInfo;
mirrorAdaptiveCoefficient_ = mirrorAdaptiveCoefficient;
GraphicIRect damageRect { 0, 0, static_cast<int32_t>(screenInfo_.width), static_cast<int32_t>(screenInfo_.height) };
....
}
初始化RSComposerAdapter,用于将 Render Service 层的屏幕渲染任务映射到底层 GPU/HWC 层,实现物理屏幕显示和镜像屏幕适配。
其实还涉及到srcRect、dstRect、cropRect 的具体计算逻辑将在后续部分详细说明。
总结:

整个流程大致如下:
display_manager_config.xml
↓
DisplayManagerService
↓
AbstractScreenController
↓
RSDisplayNode
↓
RenderService
↓
RSProcessor
↓
HDI Backend
↓
Hardware Composer
↓
Display Engine / GPU
关键点:
1、默认方向读取(RSProcessor / RSPhysicalScreenProcessor)
XML 配置文件指定屏幕默认方向,RSProcessor 负责保存屏幕信息,RSPhysicalScreenProcessor 继承并准备物理屏幕渲染数据。
2、屏幕连接旋转计算(RSPhysicalScreenProcessor)
屏幕连接时,RSPhysicalScreenProcessor 根据屏幕宽高和请求方向计算最终 rotation,并更新 RSDisplayNode 的请求方向。
3、RenderService 旋转显示(RSDisplayNode / RSProcessor)
RSDisplayNode 通过 rotation 参数对屏幕内容进行旋转,RSProcessor 提供基础矩阵和帧缓冲配置支持渲染。
4、HDI 层转换(RSComposerAdapter → HDI Backend)
RSComposerAdapter 将渲染层信息传给 HDI Backend,由 HDI Backend 转换成硬件可识别的 layer 信息,准备发送到 GPU 或 HWC。
5、GPU合成(HDI Backend / GPU)
如果硬件不支持旋转或 layer 操作,HDI Backend 使用 GPU 进行旋转和合成,保证屏幕显示正确。
更多推荐



所有评论(0)