系列文章目录

在这里插入图片描述



前言

在 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 进行旋转和合成,保证屏幕显示正确。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐