当前位置: 首页 > news >正文

OpenHarmony送显流程分析

    OpenHarmony送显流程分析

引言

  本文档主要记录OpenHarmony在渲染完成之后如何进行合成和送显流程的。这个过程牵涉的代码很多,而且流程也是比较繁琐的。所以我一定要坚持下来。千万不能半途而废,也不要想着一口气吃出一个胖子,路漫漫其修远兮吾将上下而求索!




一. 应用端发送NextVSnc请求,服务端响应

  还记得那年大明湖畔的夏雨荷吗!错了,还记得前面我们在分析OH的buffer管理的时候,当bootamition渲染完成之后会调用flush将buffer的使用权还给render_server吗,不记得也没有关系,我们重新来看下!

RSRenderServiceListener::OnBufferAvailable(...)RSMainThread::Instance()->RequestNextVSync(...)//触发下次vsyncvoid RSMainThread::RequestNextVSync()
{RS_TRACE_FUNC();VSyncReceiver::FrameCallback fcb = {.userData_ = this,.callback_ = [this](uint64_t timestamp, void* data) { OnVsync(timestamp, data); },//回调函数};if (receiver_ != nullptr) {requestNextVsyncNum_++;if (requestNextVsyncNum_ > RUQUEST_VSYNC_NUMBER_LIMIT) {RS_LOGW("RSMainThread::RequestNextVSync too many times:%d", requestNextVsyncNum_);}receiver_->RequestNextVSync(fcb);//这里我们重点分析下receiver_,详见章节1.1}
}void RSMainThread::OnVsync(uint64_t timestamp, void* data)
{ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "RSMainThread::OnVsync");timestamp_ = timestamp;requestNextVsyncNum_ = 0;if (isUniRender_) {MergeToEffectiveTransactionDataMap(cachedTransactionDataMap_);RSUnmarshalThread::Instance().PostTask(unmarshalBarrierTask_);}mainLoop_();if (handler_) {auto screenManager_ = CreateOrGetScreenManager();if (screenManager_ != nullptr) {PostTask([=]() { screenManager_->ProcessScreenHotPlugEvents(); });}}ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);
}

1.1 VSyncReceiver初始化

代码分析,无需多言。直接干就完了,上代码直接开撸!

//rs_main_thread.cpp
void RSMainThread::Init()
{...//这套就是模仿Android做的,通过LocalSocketPairhandler_ = std::make_shared<AppExecFwk::EventHandler>(runner_);sptr<VSyncConnection> conn = new VSyncConnection(rsVSyncDistributor_, "rs");rsVSyncDistributor_->AddConnection(conn);receiver_ = std::make_shared<VSyncReceiver>(conn, handler_);receiver_->Init();...}

1.2 RequestNextVSync

//vsync_receiver.cpp
VsyncError VSyncReceiver::RequestNextVSync(FrameCallback callback)
{std::lock_guard<std::mutex> locker(initMutex_);if (!init_) {return VSYNC_ERROR_API_FAILED;}listener_->SetCallback(callback);ScopedBytrace func("VSyncReceiver::RequestNextVSync_pid:" + std::to_string(GetRealPid()) + "_name:" + name_);return connection_->RequestNextVSync();
}

1.3 VSyncConnection::RequestNextVSync

VsyncError VSyncConnection::RequestNextVSync()
{...return distributor->RequestNextVSync(this);
}VsyncError VSyncDistributor::RequestNextVSync(const sptr<VSyncConnection>& connection)
{...if (connection->rate_ < 0) {...//此时会唤醒VSyncDistributor线程con_.notify_all();//notify all tasks...}...
}void VSyncDistributor::ThreadMain()
{...while (vsyncThreadRunning_ == true) {for (uint32_t i = 0; i < conns.size(); i++) {int32_t ret = conns[i]->PostEvent(timestamp);}...}}int32_t VSyncConnection::PostEvent(int64_t now)
{//这个地方发给谁接收了,得找找找int32_t ret = socketPair_->SendData(&now, sizeof(int64_t));}//上面分析感觉没有和底层的vsync对接上,吗

1.4 遗留问题

这里有一个遗留问题就是vsync间隔时间是怎么计算出来的,这里我没有找到是怎么和底层的vblack关联起来的,还有它走的是soft vsync还是硬的。它的计算规则是怎样的!




二. 应用端响应VSync事件

这里还是以bootamition举例,其它的应用流程也基本相似(主要是我对其它的应用也不是很熟悉,所以只能重复的以bootamition举例来说明了.)

std::shared_ptr<OHOS::Rosen::VSyncReceiver> receiver_ = nullptr;
void BootAnimation::Init(int32_t width, int32_t height)
{...auto& rsClient = OHOS::Rosen::RSInterfaces::GetInstance();/**RSInterfaces &RSInterfaces::GetInstance(){static RSInterfaces instance;return instance;}RSInterfaces::RSInterfaces() : renderServiceClient_(std::make_unique<RSRenderServiceClient>()){}**/while (receiver_ == nullptr) {receiver_ = rsClient.CreateVSyncReceiver("BootAnimation", mainHandler_);//这里获取的是代理端/*** std::shared_ptr<VSyncReceiver> RSInterfaces::CreateVSyncReceiver(const std::string& name,const std::shared_ptr<OHOS::AppExecFwk::EventHandler> &looper){return renderServiceClient_->CreateVSyncReceiver(name, looper);}std::shared_ptr<VSyncReceiver> RSRenderServiceClient::CreateVSyncReceiver(const std::string& name,const std::shared_ptr<OHOS::AppExecFwk::EventHandler> &looper){auto renderService = RSRenderServiceConnectHub::GetRenderService();if (renderService == nullptr) {return nullptr;}sptr<IVSyncConnection> conn = renderService->CreateVSyncConnection(name);return std::make_shared<VSyncReceiver>(conn, looper, name);}***/}VsyncError ret = receiver_->Init();OHOS::Rosen::VSyncReceiver::FrameCallback fcb = {.userData_ = this,.callback_ = std::bind(&BootAnimation::OnVsync, this),};int32_t changefreq = static_cast<int32_t>((1000.0 / freq_) / 16);ret = receiver_->SetVSyncRate(fcb, changefreq);...
}
  • 应用端接收和响应vsync事件,主要用于自身的绘制和渲染工作。当然也可以不干任何事情,仅仅接收。这个取决于业务逻辑,这块不是我们重点讨论的内容!



三. render_service响应VSync事件

这里注意,是render_service响应VSync事件,不是NextVsync。理解清楚了上述点,下面我们来接着分析!

//rs_main_thread.cpp
void RSMainThread::OnVsync(uint64_t timestamp, void* data)
{...mainLoop_();//详见章节3.1if (handler_) {auto screenManager_ = CreateOrGetScreenManager();if (screenManager_ != nullptr) {PostTask([=]() { screenManager_->ProcessScreenHotPlugEvents(); });//这个地方后续详细分析}}...
}

3.1 mainLoop_流程分析

C++的语法经常会有一些牛鬼蛇神的用法,所以我们要报着见怪不怪的心态,开搞就好!

//rs_main_thread.cpp  mainLoop_ = [&]() {RS_LOGD("RsDebug mainLoop start");PerfMultiWindow();RenderFrameTrace::GetInstance().RenderStartFrameTrace(RS_INTERVAL_NAME);SetRSEventDetectorLoopStartTag();ROSEN_TRACE_BEGIN(HITRACE_TAG_GRAPHIC_AGP, "RSMainThread::DoComposition");ConsumeAndUpdateAllNodes();//详见章节3.2WaitUntilUnmarshallingTaskFinished();ProcessCommand();Animate(timestamp_);CheckColdStartMap();CheckDelayedSwitchTask();Render();//详见章节3.3ReleaseAllNodesBuffer();SendCommands();ROSEN_TRACE_END(HITRACE_TAG_GRAPHIC_AGP);SetRSEventDetectorLoopFinishTag();rsEventManager_.UpdateParam();RS_LOGD("RsDebug mainLoop end");};//在最最开始会创建DisplayRenderNode,然后添加到RootNode里面去,注意只会添加一次
01-01 08:00:14.535   670  1395 E C01400/OHOS::ROSEN: RSDisplayNode::Create, id:2877628088321
01-01 08:00:14.662   670  1395 E C01400/OHOS::ROSEN: RSDisplayNode::Create Tid:1395 comm:EventRunner#1
01-01 08:00:14.662   670  1395 E C01400/OHOS::ROSEN: #00 pc 0000000000018ff4 /system/lib64/lib_dfx_dump_catcher.z.so(OHOS::HiviewDFX::DfxDumpCatcher::DumpCatch(int, int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char> >&)+152)
01-01 08:00:14.662   670  1395 E C01400/OHOS::ROSEN: #01 pc 00000000000887f0 /system/lib64/librender_service_client.z.so(OHOS::Rosen::RSDisplayNode::Create(OHOS::Rosen::RSDisplayNodeConfig const&)+600)
01-01 08:00:14.662   670  1395 E C01400/OHOS::ROSEN: #02 pc 000000000003bae4 /system/lib64/libdms.z.so(OHOS::Rosen::AbstractScreen::InitRSDisplayNode(OHOS::Rosen::RSDisplayNodeConfig&, OHOS::Rosen::Point&)+88)
01-01 08:00:14.662   670  1395 E C01400/OHOS::ROSEN: #03 pc 000000000003d3dc /system/lib64/libdms.z.so(OHOS::Rosen::AbstractScreenGroup::AddChild(OHOS::sptr<OHOS::Rosen::AbstractScreen>&, OHOS::Rosen::Point&)+172)
01-01 08:00:14.662   670  1395 E C01400/OHOS::ROSEN: #04 pc 0000000000044ea4 /system/lib64/libdms.z.so(OHOS::Rosen::AbstractScreenController::AddAsFirstScreenLocked(OHOS::sptr<OHOS::Rosen::AbstractScreen>)+832)
01-01 08:00:14.662   670  1395 E C01400/OHOS::ROSEN: #05 pc 0000000000043088 /system/lib64/libdms.z.so(OHOS::Rosen::AbstractScreenController::AddToGroupLocked(OHOS::sptr<OHOS::Rosen::Abstract01-01 08:00:14.554   690   690 E C01400/OHOS::RS: RSDisplayRenderNode create Tid:690 comm:render_service
01-01 08:00:14.554   690   690 E C01400/OHOS::RS: #00 pc 0000000000018ff4 /system/lib64/lib_dfx_dump_catcher.z.so(OHOS::HiviewDFX::DfxDumpCatcher::DumpCatch(int, int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char> >&)+152)
01-01 08:00:14.554   690   690 E C01400/OHOS::RS: #01 pc 00000000000d5b84 /system/lib64/librender_service_base.z.so(OHOS::Rosen::RSDisplayRenderNode::RSDisplayRenderNode(unsigned long, OHOS::Rosen::RSDisplayNodeConfig const&, std::__h::weak_ptr<OHOS::Rosen::RSContext>)+384)
01-01 08:00:14.554   690   690 E C01400/OHOS::RS: #02 pc 00000000000bd7c4 /system/lib64/librender_service_base.z.so
01-01 08:00:14.554   690   690 E C01400/OHOS::RS: #03 pc 00000000000bcf24 /system/lib64/librender_service_base.z.so(OHOS::Rosen::DisplayNodeCommandHelper::Create(OHOS::Rosen::RSContext&, unsigned long, OHOS::Rosen::RSDisplayNodeConfig const&)+108)
01-01 08:00:14.554   690   690 E C01400/OHOS::RS: #04 pc 000000000006dcd8 /system/lib64/librender_service.z.so(OHOS::Rosen::RSMainThread::ProcessCommandForDividedRender()+1796)
01-01 08:00:14.554   690   690 E C01400/OHOS::RS: #05 pc 0000000000074398 /system/lib64/librender_service.z.so
01-01 08:00:14.554   690   690 E C01400/OHOS::RS: #06 pc 000000000007187c /system/lib64/librender_service.z.so(OHOS::Rosen::RSMainT

3.2 ConsumeAndUpdateAllNodes

这个函数是一个好名字,让人一看就知道是干啥的了。我们直接撸代码,开始撸代码!

//rs_main_thread.cpp
void RSMainThread::ConsumeAndUpdateAllNodes()
{bool needRequestNextVsync = false;const auto& nodeMap = GetContext().GetNodeMap();nodeMap.TraverseSurfaceNodes(//遍历渲染完成的nodemap,此处是一个lamda表达式[this, &needRequestNextVsync](const std::shared_ptr<RSSurfaceRenderNode>& surfaceNode) mutable {auto& surfaceHandler = static_cast<RSSurfaceHandler&>(*surfaceNode);surfaceHandler.ResetCurrentFrameBufferConsumed();if (RSBaseRenderUtil::ConsumeAndUpdateBuffer(surfaceHandler)) {this->bufferTimestamps_[surfaceNode->GetId()] = static_cast<uint64_t>(surfaceNode->GetTimestamp());}// still have buffer(s) to consume.if (surfaceHandler.GetAvailableBufferCount() > 0) {needRequestNextVsync = true;}});if (needRequestNextVsync) {RequestNextVSync();}
}//rs_base_render_util.cpp
bool RSBaseRenderUtil::ConsumeAndUpdateBuffer(RSSurfaceHandler& surfaceHandler)
{auto availableBufferCnt = surfaceHandler.GetAvailableBufferCount();if (availableBufferCnt <= 0) {// this node has no new buffer, try use old buffer.return true;}auto& consumer = surfaceHandler.GetConsumer();if (consumer == nullptr) {return false;}DropFrameProcess(surfaceHandler);sptr<SurfaceBuffer> buffer;sptr<SyncFence> acquireFence = SyncFence::INVALID_FENCE;int64_t timestamp = 0;Rect damage;auto ret = consumer->AcquireBuffer(buffer, acquireFence, timestamp, damage);if (buffer == nullptr || ret != SURFACE_ERROR_OK) {RS_LOGE("RsDebug surfaceHandler(id: %" PRIu64 ") AcquireBuffer failed(ret: %d)!",surfaceHandler.GetNodeId(), ret);return false;}surfaceHandler.SetBuffer(buffer, acquireFence, damage, timestamp);surfaceHandler.SetCurrentFrameBufferConsumed();RS_LOGD("RsDebug surfaceHandler(id: %" PRIu64 ") AcquireBuffer success, timestamp = %" PRId64 ".",surfaceHandler.GetNodeId(), timestamp);availableBufferCnt = surfaceHandler.ReduceAvailableBuffer();return true;
}

上述代码的核心,可以归纳总结为如下两点:

  • 遍历所有的surfacenode,然后通过AcquireBuffer获取渲染完成的buffer
  • 判断surfacenode,是否还有需要消费的buffer,如果还有则继续调用RequestNextVSync,请求下一次VSync事件

3.3 Render

继续开撸,感觉还是一眼望不到头啊。莫停下来,继续开搞!

void RSMainThread::Render()
{const std::shared_ptr<RSBaseRenderNode> rootNode = context_->GetGlobalRootRenderNode();if (rootNode == nullptr) {RS_LOGE("RSMainThread::Render GetGlobalRootRenderNode fail");return;}if (RSSystemProperties::GetRenderNodeTraceEnabled()) {RSPropertyTrace::GetInstance().RefreshNodeTraceInfo();}RS_LOGD("RSMainThread::Render isUni:%d", IfUseUniVisitor());if (IfUseUniVisitor()) {} else {auto rsVisitor = std::make_shared<RSRenderServiceVisitor>();rsVisitor->SetAnimateState(doWindowAnimate_);rootNode->Prepare(rsVisitor);CalcOcclusion();bool doParallelComposition = false;if (!rsVisitor->ShouldForceSerial() && RSInnovation::GetParallelCompositionEnabled()) {doParallelComposition = DoParallelComposition(rootNode);}if (doParallelComposition) {renderEngine_->ShrinkCachesIfNeeded();return;}rootNode->Process(rsVisitor);}renderEngine_->ShrinkCachesIfNeeded();PerfForBlurIfNeeded();
}/**
继续跟踪rootNode->Process(rsVisitor);
这里的rootNode为std::shared_ptr<RSBaseRenderNode> globalRootRenderNode_ = std::make_shared<RSBaseRenderNode>(0, true);
rsVisitor为auto rsVisitor = std::make_shared<RSRenderServiceVisitor>(),我们接着往下看
**/RSBaseRenderNode::Process(...)visitor->ProcessBaseRenderNode(...)//rs_render_service_vistor.cppfor (auto& child : node.GetSortedChildren()) {child->Process(shared_from_this());//这个地方是核心}visitor->ProcessSurfaceRenderNode(*this);void RSRenderServiceVisitor::ProcessSurfaceRenderNode(RSSurfaceRenderNode& node)
{if (!processor_) {RS_LOGE("RSRenderServiceVisitor::ProcessSurfaceRenderNode processor is nullptr");return;}if (isSecurityDisplay_ && node.GetSecurityLayer()) {RS_LOGI("RSRenderServiceVisitor::ProcessSurfaceRenderNode node[%" PRIu64 "] process paused because of \security DisplayNode.",node.GetId());return;}if (!node.ShouldPaint()) {RS_LOGD("RSRenderServiceVisitor::ProcessSurfaceRenderNode node : %" PRIu64 " is invisible", node.GetId());return;}if (!node.GetOcclusionVisible() && !doAnimate_ && RSSystemProperties::GetOcclusionEnabled()) {return;}if (mParallelEnable) {node.ParallelVisitLock();}ProcessBaseRenderNode(node);node.SetGlobalZOrder(globalZOrder_);globalZOrder_ = globalZOrder_ + 1;processor_->ProcessSurface(node);//核心点,在于这个函数processor_是怎么被初始化的RSBaseRenderUtil::WriteSurfaceRenderNodeToPng(node);//这是一个调试手段,将buffer保存成pngif (mParallelEnable) {node.ParallelVisitUnlock();}
}
  • 这一块的逻辑主要是将前端gralloc 生成buffer,转换成为后端hdi layer,供hdi output石永红

3.4 RSProcessor processor_的创建

为了追溯这个问题,我倒腾了很久才搞明白创建的流程!首先foundatio进程会创建display_render_node,然后将其添加到root_node里面去,当root_node在进行process处理的时候,就会调用到display_render_node中的RSDisplayRenderNode::Prepare和RSDisplayRenderNode::Process,最后会调用到RSRenderServiceVisitor::ProcessDisplayRenderNode,我们对此展开分析!

//rs_render_service_vistor.cpp
void RSRenderServiceVisitor::ProcessDisplayRenderNode(RSDisplayRenderNode& node)
{//这里从打印,以及调试情况来看RSDisplayRenderNode只有一个,每次的id都木有变化isSecurityDisplay_ = node.GetSecurityDisplay();RS_LOGD("RsDebug RSRenderServiceVisitor::ProcessDisplayRenderNode: nodeid:[%" PRIu64 "] screenid:[%" PRIu64 "] \isSecurityDisplay:[%s] child size:[%d] total size:[%d]",node.GetId(), node.GetScreenId(), isSecurityDisplay_ ? "true" : "false", node.GetChildrenCount(),node.GetSortedChildren().size());globalZOrder_ = 0.0f;sptr<RSScreenManager> screenManager = CreateOrGetScreenManager();if (!screenManager) {RS_LOGE("RSRenderServiceVisitor::ProcessDisplayRenderNode ScreenManager is nullptr");return;}ScreenInfo curScreenInfo = screenManager->QueryScreenInfo(node.GetScreenId());RS_TRACE_NAME("ProcessDisplayRenderNode[" + std::to_string(node.GetScreenId()) + "]");// skip frame according to skipFrameInterval value of SetScreenSkipFrameInterval interfaceif (node.SkipFrame(curScreenInfo.skipFrameInterval)) {return;}//此时创建的是RSPhysicalScreenProcessorprocessor_ = RSProcessorFactory::CreateProcessor(node.GetCompositeType());if (processor_ == nullptr) {RS_LOGE("RSRenderServiceVisitor::ProcessDisplayRenderNode: RSProcessor is null!");return;}auto mirrorNode = node.GetMirrorSource().lock();//详见章节3.4.1if (!processor_->Init(node, node.GetDisplayOffsetX(), node.GetDisplayOffsetY(),mirrorNode ? mirrorNode->GetScreenId() : INVALID_SCREEN_ID)) {RS_LOGE("RSRenderServiceVisitor::ProcessDisplayRenderNode: processor init failed!");return;}if (node.IsMirrorDisplay()) {...} else {ProcessBaseRenderNode(node);}processor_->PostProcess();//详见3.4.2
}

3.4.1 RSPhysicalScreenProcessor::Init

分析源码,就是烦啊!只能fuck you!我草草!

//rs_physical_screen_processor.cpp
bool RSPhysicalScreenProcessor::Init(RSDisplayRenderNode& node, int32_t offsetX, int32_t offsetY, ScreenId mirroredId)
{if (!RSProcessor::Init(node, offsetX, offsetY, mirroredId)) {return false;}if (mirroredId != INVALID_SCREEN_ID) {SetMirrorScreenSwap(node);}//注意这个地方的回调Redraw很重要,当进行GPU合成的时候,会调用该回调,并且应用到屏幕上面return composerAdapter_->Init(screenInfo_, offsetX, offsetY, mirrorAdaptiveCoefficient_,[this](const auto& surface, const auto& layers) { Redraw(surface, layers); });
}//rs_composer_adapter.cpp
bool RSComposerAdapter::Init(const ScreenInfo& screenInfo, int32_t offsetX, int32_t offsetY,float mirrorAdaptiveCoefficient, const FallbackCallback& cb)
{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,这个地方得又牵涉到output怎么来的了//screenManager_最开始的初始化是在RsRenderService里面//这里的output表示输出设备output_ = screenManager->GetOutput(ToScreenPhysicalId(screenInfo.id));/**bool RSScreenManager::Init() noexcept{//这个地方又牵涉到HdiBackend这个类的实现它的调用逻辑如下:// HdiBackend ---> HdiDevice composer_ = HdiBackend::GetInstance();if (composer_ == nullptr) {RS_LOGE("RSScreenManager %s: Failed to get composer.", __func__);return false;}//它的调用逻辑如下://  HdiBackend::RegScreenHotplug//  HdiBackend::InitDevice//device_->RegHotPlugCallback(HdiBackend::OnHdiBackendHotPlugEvent, this);// 其中HdiDevice.RegHotPlugCallback// deviceFuncs_->RegHotPlugCallback//HdiSession::GetInstance().RegHotPlugCallback(callback, data);// 主要是drm中的connector//这里的compser搞了忽悠人的名字,其实它是HdiBackendif (composer_->RegScreenHotplug(&RSScreenManager::OnHotPlug, this) == -1) {RS_LOGE("RSScreenManager %s: Failed to register OnHotPlug Func to composer.", __func__);return false;}// call ProcessScreenHotPlugEvents() for primary screen immediately in main thread.ProcessScreenHotPlugEvents();return true;}RosenError HdiBackend::InitDevice(){if (device_ != nullptr) {return ROSEN_ERROR_OK;}device_ = HdiDevice::GetInstance();if (device_ == nullptr) {HLOGE("Get HdiDevice failed");return ROSEN_ERROR_NOT_INIT;}int32_t ret = device_->RegHotPlugCallback(HdiBackend::OnHdiBackendHotPlugEvent, this);if (ret != DISPLAY_SUCCESS) {HLOGE("RegHotPlugCallback failed, ret is %{public}d", ret);return ROSEN_ERROR_API_FAILED;}HLOGI("Init device succeed");return ROSEN_ERROR_OK;}RosenError HdiBackend::RegScreenHotplug(OnScreenHotplugFunc func, void* data){if (func == nullptr) {HLOGE("OnScreenHotplugFunc is null");return ROSEN_ERROR_INVALID_ARGUMENTS;}onScreenHotplugCb_ = func;onHotPlugCbData_ = data;return InitDevice();}RosenError HdiBackend::InitDevice(){if (device_ != nullptr) {return ROSEN_ERROR_OK;}device_ = HdiDevice::GetInstance();if (device_ == nullptr) {HLOGE("Get HdiDevice failed");return ROSEN_ERROR_NOT_INIT;}int32_t ret = device_->RegHotPlugCallback(HdiBackend::OnHdiBackendHotPlugEvent, this);if (ret != DISPLAY_SUCCESS) {HLOGE("RegHotPlugCallback failed, ret is %{public}d", ret);return ROSEN_ERROR_API_FAILED;}HLOGI("Init device succeed");return ROSEN_ERROR_OK;}void HdiBackend::OnHdiBackendHotPlugEvent(uint32_t screenId, bool connected, void *data){HLOGI("HotPlugEvent, screenId is %{public}u, connected is %{public}u", screenId, connected);HdiBackend *hdiBackend = nullptr;if (data != nullptr) {hdiBackend = static_cast<HdiBackend *>(data);} else {hdiBackend = HdiBackend::GetInstance();}hdiBackend->OnHdiBackendConnected(screenId, connected);}void HdiBackend::OnHdiBackendConnected(uint32_t screenId, bool connected){if (connected) {CreateHdiOutput(screenId);}OnScreenHotplug(screenId, connected);}void HdiBackend::CreateHdiOutput(uint32_t screenId){OutputPtr newOutput = HdiOutput::CreateHdiOutput(screenId);newOutput->Init();outputs_.emplace(screenId, newOutput);}std::shared_ptr<HdiOutput> RSScreenManager::GetOutput(ScreenId id) const{std::lock_guard<std::mutex> lock(mutex_);// assert screens_.count(id) == 1if (screens_.count(id) == 0) {RS_LOGW("RSScreenManager::GetOutput: There is no screen for id %" PRIu64 ".", id);return nullptr;}//其中screens_调用逻辑老复杂了,通过层层回调/** HdiBackend::OnHdiBackendHotPlugEvent*   HdiBackend::OnHdiBackendConnectedHdiBackend::OnHdiBackendConnected*    HdiBackend::CreateHdiOutput*       OutputPtr newOutput = HdiOutput::CreateHdiOutput(screenId);*       newOutput->Init();*       outputs_.emplace(screenId, newOutput);*    HdiBackend::OnScreenHotplug  OnScreenHidlConnected(screenId, connected);*       onScreenHotplugCb_(...)*          RSScreenManager::OnHotPlug(...)*            screenManager->OnHotPlugEvent(output, connected)*               pendingHotPlugEvents_.emplace_back(ScreenHotPlugEvent{output, connected});*               mainThread->RequestNextVSync();//此处会调用ProcessScreenHotPlugEvents()*                 RSScreenManager::ProcessScreenHotPlugEvents(...)*                   RSScreenManager::ProcessScreenConnectedLocked(...)*                     screens_[id] = std::make_unique<RSScreen>(id, isVirtual, output, nullptr);** /return screens_.at(id)->GetOutput();}**/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_ = offsetX;offsetY_ = offsetY;mirrorAdaptiveCoefficient_ = mirrorAdaptiveCoefficient;screenInfo_ = screenInfo;IRect damageRect {0, 0, static_cast<int32_t>(screenInfo_.width), static_cast<int32_t>(screenInfo_.height)};output_->SetOutputDamage(1, damageRect);bool directClientCompEnableStatus = RSSystemProperties::GetDirectClientCompEnableStatus();output_->SetDirectClientCompEnableStatus(directClientCompEnableStatus);#if (defined RS_ENABLE_GL) && (defined RS_ENABLE_EGLIMAGE)// enable direct GPU composition.output_->SetLayerCompCapacity(LAYER_COMPOSITION_CAPACITY);
#else // (defined RS_ENABLE_GL) && (defined RS_ENABLE_EGLIMAGE)output_->SetLayerCompCapacity(LAYER_COMPOSITION_CAPACITY_INVALID);
#endif // (defined RS_ENABLE_GL) && (defined RS_ENABLE_EGLIMAGE)return true;
}

3.4.2 RSPhysicalScreenProcessor::PostProcess

跟踪代码,有点搞谍报工作似的。稍微一不留声,代码就不知道溜到那里去了!

void RSPhysicalScreenProcessor::PostProcess()
{//这里的核心是这些layers_是怎么来的,当然是RSSurfaceRenderNodecomposerAdapter_->CommitLayers(layers_);//详见章节3.5MultiLayersPerf(layers_.size());
}void RSPhysicalScreenProcessor::ProcessSurface(RSSurfaceRenderNode &node)
{auto layer = composerAdapter_->CreateLayer(node);if (layer == nullptr) {RS_LOGD("RSPhysicalScreenProcessor::ProcessSurface: failed to createLayer for node(id: %" PRIu64 ")", node.GetId());return;}layers_.emplace_back(layer);
}

3.5 RSComposerAdapter::CommitLayers

历经磨难,我们来到了这里。但是取经的道路还远没有结束,战斗还在继续,药不能听,让我们继续手撕代码!

//rs_composer_adapter.cc
void RSComposerAdapter::CommitLayers(const std::vector<LayerInfoPtr>& layers)
{// do composition.//layers里面只有surfacenode,没有displaynode//它接着会调用CreateLayer()output_->SetLayerInfo(layers);std::vector<std::shared_ptr<HdiOutput>> outputs {output_};hdiBackend_->Repaint(outputs);//详见章节3.7
}//hdi_output.cpp
int32_t HdiOutput::CreateLayer(uint64_t surfaceId, const LayerInfoPtr &layerInfo)
{LayerPtr layer = HdiLayer::CreateHdiLayer(screenId_);/*** HdiLayer::Init*  CreateLayer   //hdi_output.cpp*   InitDevice()*     device_->CreateLayer //hdi_device.cpp*      HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::CreateLayer, layerInfo, layerId);//hdi_session.cpp*       HdiDisplay::CreateLayer//hdi_display.cpp*    **/if (!layer->Init(layerInfo)) {HLOGE("Init hdiLayer failed");return DISPLAY_FAILURE;}layer->UpdateLayerInfo(layerInfo);uint32_t layerId = layer->GetLayerId();layerIdMap_[layerId] = layer;surfaceIdMap_[surfaceId] = layer;return DISPLAY_SUCCESS;
}void HdiOutput::SetLayerInfo(const std::vector<LayerInfoPtr> &layerInfos)
{for (auto &layerInfo : layerInfos) {if (layerInfo == nullptr || layerInfo->GetSurface() == nullptr) {HLOGE("current layerInfo or layerInfo's cSurface is null");continue;}uint64_t surfaceId = layerInfo->GetSurface()->GetUniqueId();auto iter = surfaceIdMap_.find(surfaceId);if (iter != surfaceIdMap_.end()) {const LayerPtr &layer = iter->second;const LayerInfoPtr &info = layer->GetLayerInfo();if (info->GetLayerSize().w == layerInfo->GetLayerSize().w &&info->GetLayerSize().h == layerInfo->GetLayerSize().h){layer->UpdateLayerInfo(layerInfo);continue;}}int32_t ret = CreateLayer(surfaceId, layerInfo);if (ret != DISPLAY_SUCCESS) {return;}}DeletePrevLayers();ResetLayerStatus();
}

3.6 HdiDevice的初始化

本来后续分析的好好的,尼玛但是发现这个地方还是跳不过。还是得回来把这个地方分析明白了,后续才好继续分析。尼玛!

//打印的关于该初始化的相关逻辑
01-01 08:00:10.908   782   782 E C01400/Composer: Init: HdiDevice::Init Tid:782 comm:render_service
01-01 08:00:10.908   782   782 E C01400/Composer: #00 pc 0000000000018ff4 /system/lib64/lib_dfx_dump_catcher.z.so(OHOS::HiviewDFX::DfxDumpCatcher::DumpCatch(int, int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char> >&)+152)
01-01 08:00:10.908   782   782 E C01400/Composer: #01 pc 0000000000017fd8 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::Init()+100)
01-01 08:00:10.908   782   782 E C01400/Composer: #02 pc 0000000000017eb4 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::GetInstance()+72)
01-01 08:00:10.908   782   782 E C01400/Composer: #03 pc 0000000000013b64 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiBackend::RegScreenHotplug(std::__h::function<void (std::__h::shared_ptr<OHOS::Rosen::HdiOutput>&, bool, void*)>, void*)+268)
01-01 08:00:10.908   782   782 E C01400/Composer: #04 pc 00000000000ce50c /system/lib64/librender_service.z.so(OHOS::Rosen::impl::RSScreenManager::Init()+88)
01-01 08:00:10.908   782   782 E C01400/Composer: #05 pc 000000000008a6f8 /system/lib64/librender_service.z.so(OHOS::Rosen::RSRenderService::Init()+92)
01-01 08:00:10.908   782   782 E C01400/Composer: #06 pc 0000000000006f28 /system/bin/render_service
01-01 08:00:10.908   782   782 E C01400/Composer: #07 pc 00000000000da6f4 /system/lib/ld-musl-aarch64.so.1
01-01 08:00:11.002   782   782 I C01400/Composer: OnHdiBackendHotPlugEvent: HotPlugEvent, screenId is 2, connected is 1
01-01 08:00:11.004   782   782 I C01400/Composer: InitDevice: Init device succeed
01-01 08:00:11.004   782   782 I C01400/Composer: HdiScreen: Create screen, screenId is 2
01-01 08:00:11.026   782   782 E C01400/Composer: Init: HdiDevice::Init Tid:782 comm:render_service
01-01 08:00:11.026   782   782 E C01400/Composer: #00 pc 0000000000018ff4 /system/lib64/lib_dfx_dump_catcher.z.so(OHOS::HiviewDFX::DfxDumpCatcher::DumpCatch(int, int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char> >&)+152)
01-01 08:00:11.026   782   782 E C01400/Composer: #01 pc 0000000000017fd8 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::Init()+100)
01-01 08:00:11.026   782   782 E C01400/Composer: #02 pc 0000000000017eb4 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::GetInstance()+72)
01-01 08:00:11.026   782   782 E C01400/Composer: #03 pc 000000000002d5fc /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiScreen::Init()+60)
01-01 08:00:11.026   782   782 E C01400/Composer: #04 pc 00000000000c9644 /system/lib64/librender_service.z.so(OHOS::Rosen::impl::RSScreen::PhysicalScreenInit()+116)
01-01 08:00:11.026   782   782 E C01400/Composer: #05 pc 00000000000c959c /system/lib64/librender_service.z.so(OHOS::Rosen::impl::RSScreen::RSScreen(unsigned long, bool, std::__h::shared_ptr<OHOS::Rosen::HdiOutput>, OHOS::sptr<OHOS::Surface>)+376)
01-01 08:00:11.026   782   782 E C01400/Composer: #06 pc 00000000000ceb48 /system/lib64/librender_service.z.so(OHOS::Rosen::impl::RSScreenManager::ProcessScreenConnectedLocked(std::__h::shared_ptr<OHOS::Rosen::HdiOutput>&
01-01 08:00:11.026   782   782 I C01400/Composer: Init: Init hdiScreen succeed
01-01 08:00:14.504   782   782 I C01400/Composer: Repaint: layer map is empty, drop this frame
01-01 08:00:17.519   782   782 I C01400/Composer: Repaint: layer map is empty, drop this frame
01-01 08:00:20.435   782   782 E C01400/Composer: Init: HdiDevice::Init Tid:782 comm:render_service
01-01 08:00:20.435   782   782 E C01400/Composer: #00 pc 0000000000018ff4 /system/lib64/lib_dfx_dump_catcher.z.so(OHOS::HiviewDFX::DfxDumpCatcher::DumpCatch(int, int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char> >&)+152)
01-01 08:00:20.435   782   782 E C01400/Composer: #01 pc 0000000000017fd8 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::Init()+100)
01-01 08:00:20.435   782   782 E C01400/Composer: #02 pc 0000000000017eb4 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::GetInstance()+72)
01-01 08:00:20.435   782   782 E C01400/Composer: #03 pc 0000000000022674 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiLayer::CreateLayer(std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+68)
01-01 08:00:20.435   782   782 E C01400/Composer: #04 pc 0000000000022610 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiLayer::Init(std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+16)
01-01 08:00:20.435   782   782 E C01400/Composer: #05 pc 0000000000026c88 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiOutput::CreateLayer(unsigned long, std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+68)
01-01 08:00:20.435   782   782 E C01400/Composer: #06 pc 0000000000026bdc /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiOutput::SetLayerInfo(std::__h::vector<std::__
01-01 08:00:30.950   782   782 E C01400/Composer: Init: HdiDevice::Init Tid:782 comm:render_service
01-01 08:00:30.950   782   782 E C01400/Composer: #00 pc 0000000000018ff4 /system/lib64/lib_dfx_dump_catcher.z.so(OHOS::HiviewDFX::DfxDumpCatcher::DumpCatch(int, int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char> >&)+152)
01-01 08:00:30.950   782   782 E C01400/Composer: #01 pc 0000000000017fd8 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::Init()+100)
01-01 08:00:30.950   782   782 E C01400/Composer: #02 pc 0000000000017eb4 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::GetInstance()+72)
01-01 08:00:30.950   782   782 E C01400/Composer: #03 pc 0000000000022674 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiLayer::CreateLayer(std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+68)
01-01 08:00:30.950   782   782 E C01400/Composer: #04 pc 0000000000022610 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiLayer::Init(std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+16)
01-01 08:00:30.950   782   782 E C01400/Composer: #05 pc 0000000000026c88 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiOutput::CreateLayer(unsigned long, std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+68)
01-01 08:00:30.950   782   782 E C01400/Composer: #06 pc 0000000000026bdc /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiOutput::SetLayerInfo(std::__h::vector<std::__
01-01 08:00:41.683   782   782 E C01400/Composer: Init: HdiDevice::Init Tid:782 comm:render_service
01-01 08:00:41.683   782   782 E C01400/Composer: #00 pc 0000000000018ff4 /system/lib64/lib_dfx_dump_catcher.z.so(OHOS::HiviewDFX::DfxDumpCatcher::DumpCatch(int, int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char> >&)+152)
01-01 08:00:41.683   782   782 E C01400/Composer: #01 pc 0000000000017fd8 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::Init()+100)
01-01 08:00:41.683   782   782 E C01400/Composer: #02 pc 0000000000017eb4 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiDevice::GetInstance()+72)
01-01 08:00:41.683   782   782 E C01400/Composer: #03 pc 0000000000022674 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiLayer::CreateLayer(std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+68)
01-01 08:00:41.683   782   782 E C01400/Composer: #04 pc 0000000000022610 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiLayer::Init(std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+16)
01-01 08:00:41.683   782   782 E C01400/Composer: #05 pc 0000000000026c88 /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiOutput::CreateLayer(unsigned long, std::__h::shared_ptr<OHOS::Rosen::HdiLayerInfo> const&)+68)
01-01 08:00:41.683   782   782 E C01400/Composer: #06 pc 0000000000026bdc /system/lib64/libcomposer.z.so(OHOS::Rosen::HdiOutput::SetLayerInfo(std::__h::vector<std::__//hdi_device.cpp
RosenError HdiDevice::Init()
{/*** * 这里我们可以看出HdiDevice有两个比较重要的钩子* deviceFuncs_和layerFuncs_* 它们的头文件分别是:* drivers/peripheral/display/interfaces/include/display_device.h* drivers/peripheral/display/interfaces/include/display_layer.h* 且这一块的代码,都是device或者vendor厂家自己实现了,和OpenHarmony关系不大了* **/if (deviceFuncs_ == nullptr) {int32_t ret = DeviceInitialize(&deviceFuncs_);if (ret != DISPLAY_SUCCESS || deviceFuncs_ == nullptr) {HLOGE("DeviceInitialize failed, ret is %{public}d", ret);return ROSEN_ERROR_NOT_INIT;}}if (layerFuncs_ == nullptr) {int32_t ret = LayerInitialize(&layerFuncs_);if (ret != DISPLAY_SUCCESS || layerFuncs_ == nullptr) {Destroy();HLOGE("LayerInitialize failed, ret is %{public}d", ret);return ROSEN_ERROR_NOT_INIT;}}return ROSEN_ERROR_OK;
}extern "C" {
int32_t DeviceInitialize(DeviceFuncs **funcs)
{DISPLAY_CHK_RETURN((funcs == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("in funcs is null"));DeviceFuncs *dFuncs = (DeviceFuncs *)calloc(1, sizeof(DeviceFuncs));DISPLAY_CHK_RETURN((dFuncs == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("can not calloc"));dFuncs->RegHotPlugCallback = RegHotPlugCallback;dFuncs->GetDisplayCapability = GetDisplayCapability;dFuncs->GetDisplaySupportedModes = GetDisplaySupportedModes;dFuncs->GetDisplayMode = GetDisplayMode;dFuncs->SetDisplayMode = SetDisplayMode;dFuncs->GetDisplayPowerStatus = GetDisplayPowerStatus;dFuncs->SetDisplayPowerStatus = SetDisplayPowerStatus;dFuncs->PrepareDisplayLayers = PrepareDisplayLayers;dFuncs->GetDisplayBacklight = GetDisplayBacklight;dFuncs->SetDisplayBacklight = SetDisplayBacklight;dFuncs->GetDisplayProperty = GetDisplayProperty;dFuncs->GetDisplayCompChange = GetDisplayCompChange;dFuncs->SetDisplayClientCrop = SetDisplayClientCrop;dFuncs->SetDisplayClientDestRect = SetDisplayClientDestRect;dFuncs->SetDisplayClientBuffer = SetDisplayClientBuffer;dFuncs->SetDisplayClientDamage = SetDisplayClientDamage;dFuncs->SetDisplayVsyncEnabled = SetDisplayVsyncEnabled;dFuncs->RegDisplayVBlankCallback = RegDisplayVBlankCallback;dFuncs->GetDisplayReleaseFence = GetDisplayReleaseFence;dFuncs->CreateVirtualDisplay = CreateVirtualDisplay;dFuncs->DestroyVirtualDisplay = DestroyVirtualDisplay;dFuncs->SetVirtualDisplayBuffer = SetVirtualDisplayBuffer;dFuncs->SetDisplayProperty = SetDisplayProperty;dFuncs->Commit = Commit;*funcs = dFuncs;HdiSession::GetInstance();//详见章节3.6.1return DISPLAY_SUCCESS;
}int32_t LayerInitialize(LayerFuncs **funcs)
{DISPLAY_CHK_RETURN((funcs == nullptr), DISPLAY_NULL_PTR, DISPLAY_LOGE("the in funcs is nullptr"));LayerFuncs *lFuncs = (LayerFuncs *)calloc(1, sizeof(LayerFuncs));DISPLAY_CHK_RETURN((lFuncs == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("can not calloc errno: %{public}d", errno));lFuncs->SetLayerAlpha = SetLayerAlpha;lFuncs->CreateLayer = CreateLayer;lFuncs->CloseLayer = CloseLayer;lFuncs->SetLayerSize = SetLayerSize;lFuncs->SetLayerCrop = SetLayerCrop;lFuncs->SetLayerZorder = SetLayerZorder;lFuncs->SetLayerPreMulti = SetLayerPreMulti;lFuncs->SetTransformMode = SetTransformMode;lFuncs->SetLayerDirtyRegion = SetLayerDirtyRegion;lFuncs->SetLayerVisibleRegion = SetLayerVisibleRegion;lFuncs->SetLayerBuffer = SetLayerBuffer;lFuncs->SetLayerCompositionType = SetLayerCompositionType;lFuncs->SetLayerBlendType = SetLayerBlendType;lFuncs->SetLayerVisible = SetLayerVisible;*funcs = lFuncs;DISPLAY_LOGD("layer initialize success");return DISPLAY_SUCCESS;
}}

3.6.1 HdiSession::GetInstance

干到这里,只能一个字形容累。但是,累也得继续搞,搞搞搞!

//hdi_session.cpp
HdiSession &HdiSession::GetInstance()
{static HdiSession instance;static std::once_flag once;std::call_once(once, [&]() { instance.Init(); });return instance;
}void HdiSession::Init()
{DISPLAY_LOGD();//获取一个drmDevicemHdiDevices = HdiDeviceInterface::DiscoveryDevice();DISPLAY_LOGD("devices size %{public}zd", mHdiDevices.size());mHdiDisplays.clear();for (auto device : mHdiDevices) {auto displays = device->DiscoveryDisplay();mHdiDisplays.insert(displays.begin(), displays.end());}
}//KMS的一些常规操作,主要通过libdrm库的API调用来实现。
//drm_device.cpp
std::unordered_map<uint32_t, std::shared_ptr<HdiDisplay>> DrmDevice::DiscoveryDisplay()
{mDisplays.clear();drmModeResPtr res = drmModeGetResources(GetDrmFd());DISPLAY_CHK_RETURN((res == nullptr), mDisplays, DISPLAY_LOGE("can not get drm resource"));// discovery all drm resourceFindAllCrtc(res);FindAllEncoder(res);FindAllConnector(res);FindAllPlane();DISPLAY_LOGD();// travel all connectorfor (auto &connectorPair : mConnectors) {auto connector = connectorPair.second;uint32_t crtcId = 0; int32_t ret;ret = connector->PickIdleCrtcId(mEncoders, mCrtcs, crtcId);if (ret != DISPLAY_SUCCESS) {continue;}DISPLAY_LOGD();auto crtcIter = mCrtcs.find(crtcId);if (crtcIter == mCrtcs.end()) {DISPLAY_LOGE("can not find crtc for the id %{public}d", connector->GetId());continue;}DISPLAY_LOGD();auto crtc = crtcIter->second;DISPLAY_LOGD("crtc %{public}p", crtc.get());// create the displaystd::shared_ptr<HdiDisplay> display = std::make_shared<DrmDisplay>(connector, crtc, mInstance);DISPLAY_LOGD();display->Init();//详见章节3.6.2mDisplays.emplace(display->GetId(), std::move(display));}return mDisplays;
}

3.6.2 DrmDisplay::init

猴子的快乐你不懂,而我们程序员的快乐你也不懂。但是我懂!因为我也是一个!

/*** 在源码开始分析前,这里我们要知道DrmDisplay继承于HdiDisplay**///drm_display.cpp
int32_t DrmDisplay::Init()
{int ret;DISPLAY_CHK_RETURN((mCrtc == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("crtc is null"));DISPLAY_CHK_RETURN((mConnector == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("connector is null"));DISPLAY_CHK_RETURN((mDrmDevice == nullptr), DISPLAY_FAILURE, DISPLAY_LOGE("drmDevice is null"));ret = HdiDisplay::Init();DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("init failed"));DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("can not init HdiGfxComposition"));auto postComp = std::make_unique<HdiDrmComposition>(mConnector, mCrtc, mDrmDevice);DISPLAY_CHK_RETURN((postComp == nullptr), DISPLAY_FAILURE,DISPLAY_LOGE("can not new HdiDrmComposition errno %{public}d", errno));ret = postComp->Init();DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("can not init HdiDrmComposition"));/*** HdiDrmComposition继承于HdiComposer* 这里的HdiComposer里的mComposer指向了HdiDrmComposition**/mComposer = std::make_unique<HdiComposer>(std::move(postComp));ret = mCrtc->BindToDisplay(GetId());DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("bind crtc failed"));ret = ChosePreferenceMode();DISPLAY_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_LOGE("choose preference mode fialed"));return DISPLAY_SUCCESS;
}

3.6.3 小结

通过前面的分析可知,我们可以把hdibackend理解为后端,其调用逻辑我们可以使用如下的伪代码来表示:

    HdiBackend --->HdiSession-------->HdiDisplay

3.7 HdiBackend::Repaint

前途是光明的,道路是曲折的。狭路相逢勇者胜,木有啥好说的,继续撸起袖子干就是了!

//hdi_backend.cpp
void HdiBackend::Repaint(std::vector<OutputPtr> &outputs)
{...int32_t ret = DISPLAY_SUCCESS;for (auto &output : outputs) {//遍历output,通常只有一个输出实例...//获取所有已知ID的Layerconst std::unordered_map<uint32_t, LayerPtr> &layersMap = output->GetLayers();...bool needFlush = false;ret = PreProcessLayersComp(output, layersMap, needFlush);//详见章节3.8...uint32_t screenId = output->GetScreenId();std::vector<LayerPtr> compClientLayers;std::vector<LayerInfoPtr> newLayerInfos;for (auto iter = layersMap.begin(); iter != layersMap.end(); ++iter) {const LayerPtr &layer = iter->second;newLayerInfos.emplace_back(layer->GetLayerInfo());if (layer->GetLayerInfo()->GetCompositionType() == GraphicCompositionType::GRAPHIC_COMPOSITION_CLIENT) {compClientLayers.emplace_back(layer);}}if (compClientLayers.size() > 0) {needFlush = true;HLOGD("Need flush framebuffer, client composition layer num is %{public}zu", compClientLayers.size());}/*** 此时的needFlush为true* **/OnPrepareComplete(needFlush, output, newLayerInfos);//详见章节3.9sptr<SurfaceBuffer> frameBuffer = nullptr;if (needFlush) {/*** 此时的needFlush为true,compClientLayers表示需要通过GPU合成的layers**/if (FlushScreen(output, compClientLayers, frameBuffer) != DISPLAY_SUCCESS) {//详见章节3.10// return}}sptr<SyncFence> fbFence = SyncFence::INVALID_FENCE;ret = device_->Commit(screenId, fbFence);//详见章节3.11...output->UpdatePrevLayerInfo();//交换output中前后buffer...//释放framebufferReleaseFramebuffer(output, fbFence, frameBuffer);lastPresentFence_ = fbFence;HLOGD("%{public}s: end", __func__);}
}

3.8 HdiDevice::PrepareScreenLayers

辛亏老子以前分析过,Android的逻辑代码。不然OpenHarmony里面的各种弯弯绕绕,不得搞死人去啊。

//hdi_device.cpp
int32_t HdiDevice::PrepareScreenLayers(uint32_t screenId, bool &needFlush)
{CHECK_FUNC(deviceFuncs_, deviceFuncs_->PrepareDisplayLayers);return deviceFuncs_->PrepareDisplayLayers(screenId, &needFlush);
}
//此时的needFlushFb为false
//hdi_session.cpp
HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::PrepareDisplayLayers, needFlushFb);  //hdi_session.cppHdiDisplay::PrepareDisplayLayers(...)/*** 其中mClientLayer是在HdiDisplay::Init被初始化的* auto layer = CreateHdiLayer(LAYER_TYPE_GRAPHIC);* mClientLayer = std::move(layer);**/mComposer->Prepare(layers, *mClientLayer)//其中mComposer指向HdiComposer,layers为前面章节3.5 layer->Init传递过来的mPostComp->SetLayers(layers, clientLayer);//hdi_composer.cpp,其中mPostComp指向HdiDrmCompositionHdiDrmComposition::SetLayers(...)for (auto &layer : layers) {layer->SetDeviceSelect(COMPOSITION_CLIENT);}mCompLayers.push_back(&clientLayer);                       
  • PrepareScreenLayers函数的核心功能,一句话概括就是对要进行合成的buffer做一些预处理

3.9 HdiBackend::OnPrepareComplete

分析代码,真是累啊。搞了一天,还没有分析完。革命尚未成功,同志还需努力!OnPrepareComplete的核心功能为:

  • 主要是对渲染完成的layer,进行GPU合成操作,并将其写入到FBO中。
  • 在合成完成后,将FBO写入到屏幕上。
//hdi_backend.cpp
void HdiBackend::OnPrepareComplete(bool needFlush, OutputPtr &output, std::vector<LayerInfoPtr> &newLayerInfos)
{if (needFlush) {ReorderLayerInfo(newLayerInfos);}struct PrepareCompleteParam param = {.needFlushFramebuffer = needFlush,.layers = newLayerInfos,};/*** 这个地方获取的是Surface* 此处的fbSurface_ = HdiFramebufferSurface::CreateFramebufferSurface();**//*** SurfaceError HdiFramebufferSurface::CreateSurface(sptr<HdiFramebufferSurface> &fbSurface){consumerSurface_ = Surface::CreateSurfaceAsConsumer("FrameBuffer");sptr<IBufferProducer> producer = consumerSurface_->GetProducer();producerSurface_ = Surface::CreateSurfaceAsProducer(producer);sptr<IBufferConsumerListener> listener = fbSurface;SurfaceError ret = consumerSurface_->RegisterConsumerListener(listener);if (ret != SURFACE_ERROR_OK) {return SURFACE_ERROR_NO_CONSUMER;}return SURFACE_ERROR_OK;}sptr<HdiFramebufferSurface> HdiFramebufferSurface::CreateFramebufferSurface(){sptr<HdiFramebufferSurface> fbSurface = new HdiFramebufferSurface();SurfaceError ret = fbSurface->CreateSurface(fbSurface);if (ret != SURFACE_ERROR_OK) {HLOGE("FramebufferSurface CreateSurface failed, ret is %{public}d", ret);return nullptr;}ret = fbSurface->SetBufferQueueSize(MAX_BUFFER_SIZE);if (ret != SURFACE_ERROR_OK) {HLOGE("FramebufferSurface SetBufferQueueSize failed, ret is %{public}d", ret);return nullptr;}return fbSurface;}**/auto fbSurface = output->GetFrameBufferSurface();if (onPrepareCompleteCb_ != nullptr) {//这个地方是个回调函数,它的执行逻辑如下/**onPrepareCompleteCb_(...)//hdi_backend.cpp*   RSComposerAdapter::OnPrepareComplete(...)//rs_composer_adapter.cpp*      RSPhysicalScreenProcessor::Redraw(...)//rs_physical_screen_processor.cpp,详见章节3.9.1**/onPrepareCompleteCb_(fbSurface, param, onPrepareCompleteCbData_);}
}

3.9.1 RSPhysicalScreenProcessor::Redraw

继续执行RSComposerAdapter::OnPrepareComplete函数,将其传递到rs_physical_screen_processor.cpp中。
在rs_physical_screen_processor.cpp中执行RSPhysicalScreenProcessor::Redraw函数。

//rs_physical_screen_processor.cpp
void RSPhysicalScreenProcessor::Redraw(const sptr<Surface>& surface, const std::vector<LayerInfoPtr>& layers)
{RS_TRACE_NAME("Redraw");//此处的流程和bootamition中的何其相似RS_LOGD("RsDebug RSPhysicalScreenProcessor::Redraw flush frame buffer start");bool forceCPU = RSBaseRenderEngine::NeedForceCPU(layers);auto renderFrame = renderEngine_->RequestFrame(surface, renderFrameConfig_, forceCPU);auto canvas = renderFrame->GetCanvas();if (canvas == nullptr) {RS_LOGE("RsDebug RSPhysicalScreenProcessor::Redraw:canvas is nullptr.");return;}canvas->concat(screenTransformMatrix_);//此处的forceCPU为false,renderEngine_为rs_render_engine.cpp中的一个引用renderEngine_->DrawLayers(*canvas, layers, forceCPU, mirrorAdaptiveCoefficient_);/*** 此处调用surface的flaush,然后触发HdiFramebufferSurface::OnBufferAvailable()* 详见章节3.9.2**/renderFrame->Flush();RS_LOGD("RsDebug RSPhysicalScreenProcessor::Redraw flush frame buffer end");
}

3.9.2 HdiFramebufferSurface::OnBufferAvailable

兜兜转转,一路走来。不容易,既然选择了,那就干就是了。坚信自己是最牛逼的!

//hdi_framebuffer_surface.cpp
void HdiFramebufferSurface::OnBufferAvailable()
{sptr<SurfaceBuffer> buffer;int64_t timestamp = 0;Rect damage = {0};sptr<SyncFence> acquireFence = SyncFence::INVALID_FENCE;//获取可用bufferSurfaceError ret = consumerSurface_->AcquireBuffer(buffer, acquireFence,timestamp, damage);if (ret != SURFACE_ERROR_OK || buffer == nullptr) {HLOGE("AcquireBuffer failed, ret is %{public}d", ret);return;}std::lock_guard<std::mutex> lock(mutex_);//放入availableBuffers_.push(std::make_unique<FrameBufferEntry>(buffer, acquireFence, timestamp, damage));bufferCond_.notify_one();//唤醒等待
}

总结上述代码的核心逻辑就是:

  • 消费前面的合成后图层的buffer并放入AvailableBuffers_中
  • 然后唤醒bufferCond_等待事件

3.10 HdiBackend::FlushScreen

离成功越来越近了,再坚持一把,冲一冲,前途是美好的,未来是光明的,道路是曲折的。此时不拼搏,更待何时!

//hdi_backend.cpp
int32_t HdiBackend::FlushScreen(const OutputPtr &output, std::vector<LayerPtr> &compClientLayers, sptr<SurfaceBuffer> &buffer)
{//获取前面合成好的图层bufferauto fbEntry = output->GetFramebuffer();const auto& fbAcquireFence = fbEntry->acquireFence;for (auto &layer : compClientLayers) {layer->MergeWithFramebufferFence(fbAcquireFence);//这个地方,没有看出来有啥用}buffer = fbEntry->buffer;return SetScreenClientInfo(*fbEntry, output);//继续接着往下看3.10.1
}

3.10.1 HdiBackend::SetScreenClientInfo

别人说见色起意,这里我们见名思意。这个函数是干嘛的呢,我们一道来分析!

//hdi_backend.cpp
int32_t HdiBackend::SetScreenClientInfo(const OutputPtr &output, const OutputPtr &clientOutput)
{if (buffer == nullptr) {return;}if (lastFrameBuffers_.find(output->GetScreenId()) != lastFrameBuffers_.end()) {// wrong check(void)output->ReleaseFramebuffer(lastFrameBuffers_[output->GetScreenId()], presentFence);}lastFrameBuffers_[output->GetScreenId()] = buffer;
}int32_t HdiBackend::SetScreenClientInfo(const FrameBufferEntry &fbEntry, const OutputPtr &output)
{.../*** 设置上屏buffer,它的调用逻辑如下:*  HdiDevice::SetScreenClientBuffer*   SetDisplayClientBuffer(...)//hdi_session.cpp*      HdiDisplay::SetDisplayClientBuffer()//hdi_display.cpp*           mClientLayer->SetLayerBuffer(buffer, fence)//这里的mClientLayer是在hdi_display在init中被初始化的,但是没有提供buffer实质性的内容*                  std::unique_ptr<HdiLayerBuffer> layerbuffer = std::make_unique<HdiLayerBuffer>(*buffer);*                  mHdiBuffer = std::move(layerbuffer)**/int ret = device_->SetScreenClientBuffer(output->GetScreenId(),fbEntry.buffer->GetBufferHandle(), fbEntry.acquireFence);//这个函数毛都木有做ret = device_->SetScreenClientDamage(output->GetScreenId(), output->GetOutputDamageNum(),output->GetOutputDamage());...return DISPLAY_SUCCESS;    
}

3.10.2 小结

FlushScreen的核心功能可以概括为:

  • 将合成OK之后的图层送到hdioupt,接着送往hdidisplay等待显示

3.11 HdiDevice::Commit

前面的铺垫,都是为了最后将图层应用到屏幕上,而此处便是最后一搏了,单车能否变摩托!

//hdi_device.cpp
int32_t HdiDevice::Commit(uint32_t screenId, sptr<SyncFence> &fence)
{...int32_t ret = deviceFuncs_->Commit(screenId, &fenceFd);...return ret;
}/*** 其中deviceFuncs_->Commit的调用逻辑如下:*  Commit(...)//hdi_session.cpp*     HdiSession::GetInstance().CallDisplayFunction(devId, &HdiDisplay::Commit, fence)*       HdiDisplay::Commit(...)//hdi_display.cpp*          mComposer->Commit(false)//hdi_composer.cpp*              mPostComp->Apply(modeSet)//hdi_drm_composition.cpp*                  接下来就是一番drm送显示的一番常规操作了**/



写在最后

   到这里,OpenHarmony送显流程分析介绍就告一段落了!编写该文档的过程我也是小心再小心,谨慎再谨慎!但是难免其中可能会有一些概念或者理论不对,这个希望各位能多多指针!就我分析个人感觉,OH的很多理念都是和Android非常相似,基本只是换了一套实现而已。看来OpenHarmony还有很长的路要走大!路漫漫其修,吾将上下而求索,总之,青山不改绿水长流先到这里了,各位江湖见!由于本人也是接触OpenHarmony不是很长时间,各位大大勿喷!

其中涉及的一些私有代码,参见仓库:
Phytium嵌入式软件 / Phytium-OpenHarmony-D2000-X100

相关文章:

OpenHarmony送显流程分析

OpenHarmony送显流程分析 引言 本文档主要记录OpenHarmony在渲染完成之后如何进行合成和送显流程的。这个过程牵涉的代码很多&#xff0c;而且流程也是比较繁琐的。所以我一定要坚持下来。千万不能半途而废&#xff0c;也不要想着一口气吃出一个胖子&#xff0c;路漫漫其修远兮…...

Java面试题字节流字符流

String 编码UTF-8 和GBK的区别 GBK编码&#xff1a;是指中国的中文字符&#xff0c;其实它包含了简体中文与繁体中文字符&#xff0c;另外还有一种字符 “gb2312”&#xff0c;这种字符仅能存储简体中文字符。 UTF-8编码&#xff1a;它是一种全国家通过的一种编码&#x…...

Self-Attention结构细节及计算过程

一、结构 上面那个图其实不是那么重要&#xff0c;只要知道将输入的x矩阵转换成三个矩阵进行计算即可。自注意力结构的输入为 输入矩阵的三个变形 Q&#xff08;query矩阵&#xff09;、K&#xff08;key矩阵&#xff09;、V&#xff08;value矩阵&#xff09;构成&#xff0c;…...

在Ubuntu18.04中安装uWebSockets库

目录 1.下载uWebSockets库2.下载uSockets3.安装openssl开发包4.编译首先说明这里使用的Ubuntu版本为18.04。 1.下载uWebSockets库 下载uWebSockets库有两种方式,一是终端,从Github中克隆uWebSockets库到Ubuntu本地文件夹,二是打开uWebSockets库下载链接自己下载到Windows,然…...

【Fluent】接着上一次计算的结果继续计算,利用计算过程中得到的物理场(温度、速度、压力等)插值Interpolate文件初始化模型的方法

一、问题背景 因为fluent中支持的初始化无非三种类型。 1、Standard initialization 标准初始化 2、Hybridinitialization 混合初始化 3、FMG initialization FMG初始化 另外&#xff0c;还可以用UDF通过坐标判断的方式予以初始化。 但是这些初始化方法都没办法利用以前计算过…...

第二十九章 使用消息订阅发布实现组件通信

PubSubJS库介绍 如果你想在React中使用第三方库来实现Pub/Sub机制&#xff0c;PubSubJS是一个不错的选择。它是一个轻量级的库&#xff0c;可以在浏览器和Node.js环境中使用。 PubSubJS提供了一个简单的API&#xff0c;可以让你在应用程序中订阅和发布消息。你可以使用npm来安…...

Transformer的位置编码

1. 什么是位置编码&#xff0c;为什么要使用位置编码 简单来说位置编码就是给一个句子中的每个token一个位置信息&#xff0c;通过位置编码可以明确token的前后顺序关系。 对任何语言来说&#xff0c;句子中词汇的顺序和位置都是非常重要的。它们定义了语法&#xff0c;从而定…...

Python学习简记

做题时遇到的不知道的知识点会更新在此&#xff1a; python中的int()函数可以用于进制转换 该函数最为常见的使用是用于强制类型转换&#xff0c;实际上&#xff0c;它可以有两个参数 值得强调的是当传入两个参数时第一个参数一定要是字符串类型 字符串方法&#xff1a; lower(…...

windows搭建一个FTP服务器超详细

一.场景&#xff1a; 在开发过程中需要FTP文件上传下载功能&#xff0c;需要在本地或者服务器上搭建一个FTP服务器。 二.详细步骤&#xff1a; 1. 安装FTP服务器支持和配置IIS web服务器 打卡“启动关闭Window功能” 控制面板>程序>启动或关闭Windows功能 或者选择快…...

u01使用率100%报错归档满的问题

今天下午客户报数据库无法连接了&#xff0c;我也立刻登录查看 因为显示orcl1归档满了&#xff0c;我就登录查看磁盘组的空间&#xff0c;发现空间空余很多 就sqlpus登录了&#xff0c;发现u01使用率满了 [oracledb1 ~]$ sqlplus / as sysdba SQL*Plus: Release 11.2.0.4.0 …...

Packet Tracer - 配置扩展 ACL - 场景 2

Packet Tracer - 配置扩展 ACL - 场景 2 拓扑图 地址分配表 设备 接口 IP 地址 子网掩码 默认网关 RTA G0/0 10.101.117.49 255.255.255.248 不适用 G0/1 10.101.117.33 255.255.255.240 不适用 G0/2 10.101.117.1 255.255.255.224 不适用 PCA NIC 10.101…...

最近面了12个人,发现这个测试基础题都答不上来...

一般面试我都会问一两道很基础的题目&#xff0c;来考察候选人的“地基”是否扎实&#xff0c;有些是操作系统层面的&#xff0c;有些是 python语言方面的&#xff0c;还有些… 深耕IT行业多年&#xff0c;我们发现&#xff0c;对于一个程序员而言&#xff0c;能去到一线互联网…...

JAVA基于Springboot框架的停车场管理系统开发实践和实现【附源码】

运行环境: jdk1.8idea/eclipsemaven3mysql5.6 项目技术: Java,Springboot,mybatis,mysql,jquery,html 该系统为停车场管理人员提供了对停车场中车辆&#xff0c;车位和财务的管理。操作员可以灵活地使用相关权限开展工作。在车位管理&#xff0c;车辆的停放和驶离、缴费&a…...

[230501] 4月29日考试真题第一篇|Temporary Pools

题目来源&#xff1a;http://t.csdn.cn/goCDT 正确率&#xff1a;6/10 目录​​​​​​​ Temporary Pools 题目 Temporary Pools Paragraph 1: Temporary pools are freshwater habitats that retain water for only three to four months of the year or even shorter pe…...

Qt中QString, int等数据转换以及Qt与Halcon联编程序打包

一、Qt中QString, int等数据转换 在QT中,数值类型转为字符串类型,一般是可以使用 QString::number(); 示例: int num = 65; QString ab = QString::number(num); 其实还可以转为不同的进制字符串,比如 int num = 65; QString ab = QString::number(num,10); //十进…...

Android FlexboxLayout布局

FlexboxLayout 布局 一、简介二、使用三、功能详解FlexboxLayout属性flexWrapflexDirectionalignItemsjustifyContentalignContentdividerDrawableHorizontal、showDividerHorizontaldividerDrawableVertical、showDividerVerticaldividerDrawable、showDividermaxLine Flexbox…...

GNU编码标准(七)发布过程 相关内容摘录

仅对第7章The Release Process的内容进行摘录。 文章目录 7 发布过程7.1 configure应该如何工作7.2Makefile公约7.2.1 Makefile的一般公约7.2.2 Makefiles中的实用程序7.2.3 指定命令的变量7.2.4 DESTDIR&#xff1a;支持分阶段安装7.2.5 安装目录的变量7.2.6 用户标准目标7.2…...

DX算法还原

早在之前作者就写过一篇关于顶象的滑块验证&#xff0c;潦潦草草几句话就带过了。 出于互相学习的想法&#xff0c;给了一个大学生&#xff0c;奈何不讲武德把源码甩群里了&#xff0c;虽然在大佬们眼里不难&#xff0c; 不过拿着别人的东西乱传还是不太好。自认倒霉&#xf…...

多媒体API

私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版&#xff0c;配图更多&#xff0c;CSDN博文图片需要手动上传&#xff0c;因此文章配图较少&#xff0c;看不懂的可以去菜鸡博客参考一下配图&#xff01; 系列文章目录 前端系列文章——传送门 后端系列文章——传送…...

免费矢量图标网站有哪些?

图标作为UI设计的必要元素&#xff0c;矢量图标是质量的保证。据说完美的用户体验应该从灵活性开始 。在响应设计盛行的当下&#xff0c;灵活矢量图标的重要性不言而喻。在这种情况下&#xff0c;风格齐全、质量上乘的矢量图标网站堪称设计宝藏。在这篇文章中&#xff0c;我们…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

Kafka主题运维全指南:从基础配置到故障处理

#作者&#xff1a;张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1&#xff1a;主题删除失败。常见错误2&#xff1a;__consumer_offsets占用太多的磁盘。 主题日常管理 …...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号&#xff0c;此时&#xff0c;我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…...