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在渲染完成之后如何进行合成和送显流程的。这个过程牵涉的代码很多,而且流程也是比较繁琐的。所以我一定要坚持下来。千万不能半途而废,也不要想着一口气吃出一个胖子,路漫漫其修远兮…...
Java面试题字节流字符流
String 编码UTF-8 和GBK的区别 GBK编码:是指中国的中文字符,其实它包含了简体中文与繁体中文字符,另外还有一种字符 “gb2312”,这种字符仅能存储简体中文字符。 UTF-8编码:它是一种全国家通过的一种编码&#x…...
Self-Attention结构细节及计算过程
一、结构 上面那个图其实不是那么重要,只要知道将输入的x矩阵转换成三个矩阵进行计算即可。自注意力结构的输入为 输入矩阵的三个变形 Q(query矩阵)、K(key矩阵)、V(value矩阵)构成,…...
在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初始化 另外,还可以用UDF通过坐标判断的方式予以初始化。 但是这些初始化方法都没办法利用以前计算过…...
第二十九章 使用消息订阅发布实现组件通信
PubSubJS库介绍 如果你想在React中使用第三方库来实现Pub/Sub机制,PubSubJS是一个不错的选择。它是一个轻量级的库,可以在浏览器和Node.js环境中使用。 PubSubJS提供了一个简单的API,可以让你在应用程序中订阅和发布消息。你可以使用npm来安…...
Transformer的位置编码
1. 什么是位置编码,为什么要使用位置编码 简单来说位置编码就是给一个句子中的每个token一个位置信息,通过位置编码可以明确token的前后顺序关系。 对任何语言来说,句子中词汇的顺序和位置都是非常重要的。它们定义了语法,从而定…...
Python学习简记
做题时遇到的不知道的知识点会更新在此: python中的int()函数可以用于进制转换 该函数最为常见的使用是用于强制类型转换,实际上,它可以有两个参数 值得强调的是当传入两个参数时第一个参数一定要是字符串类型 字符串方法: lower(…...
windows搭建一个FTP服务器超详细
一.场景: 在开发过程中需要FTP文件上传下载功能,需要在本地或者服务器上搭建一个FTP服务器。 二.详细步骤: 1. 安装FTP服务器支持和配置IIS web服务器 打卡“启动关闭Window功能” 控制面板>程序>启动或关闭Windows功能 或者选择快…...
u01使用率100%报错归档满的问题
今天下午客户报数据库无法连接了,我也立刻登录查看 因为显示orcl1归档满了,我就登录查看磁盘组的空间,发现空间空余很多 就sqlpus登录了,发现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个人,发现这个测试基础题都答不上来...
一般面试我都会问一两道很基础的题目,来考察候选人的“地基”是否扎实,有些是操作系统层面的,有些是 python语言方面的,还有些… 深耕IT行业多年,我们发现,对于一个程序员而言,能去到一线互联网…...
JAVA基于Springboot框架的停车场管理系统开发实践和实现【附源码】
运行环境: jdk1.8idea/eclipsemaven3mysql5.6 项目技术: Java,Springboot,mybatis,mysql,jquery,html 该系统为停车场管理人员提供了对停车场中车辆,车位和财务的管理。操作员可以灵活地使用相关权限开展工作。在车位管理,车辆的停放和驶离、缴费&a…...
[230501] 4月29日考试真题第一篇|Temporary Pools
题目来源:http://t.csdn.cn/goCDT 正确率: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:支持分阶段安装7.2.5 安装目录的变量7.2.6 用户标准目标7.2…...
DX算法还原
早在之前作者就写过一篇关于顶象的滑块验证,潦潦草草几句话就带过了。 出于互相学习的想法,给了一个大学生,奈何不讲武德把源码甩群里了,虽然在大佬们眼里不难, 不过拿着别人的东西乱传还是不太好。自认倒霉…...
多媒体API
私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版,配图更多,CSDN博文图片需要手动上传,因此文章配图较少,看不懂的可以去菜鸡博客参考一下配图! 系列文章目录 前端系列文章——传送门 后端系列文章——传送…...
免费矢量图标网站有哪些?
图标作为UI设计的必要元素,矢量图标是质量的保证。据说完美的用户体验应该从灵活性开始 。在响应设计盛行的当下,灵活矢量图标的重要性不言而喻。在这种情况下,风格齐全、质量上乘的矢量图标网站堪称设计宝藏。在这篇文章中,我们…...
基于Redis的分布式限流详解
前言 Redis除了能用作缓存外,还有很多其他用途,比如分布式锁,分布式限流,分布式唯一主键等,本文将和大家分享下基于Redis分布式限流的各种实现方案。 一、为什么需要限流 用最简单的话来说:外部请求是不可…...
权限提升:漏洞探针.(Linux系统)
权限提升:漏洞探针. 权限提升简称提权,由于操作系统都是多用户操作系统,用户之间都有权限控制,比如通过 Web 漏洞拿到的是 Web 进程的权限,往往 Web 服务都是以一个权限很低的账号启动的,因此通过 Webshel…...
python-11-多线程模块threading
python使用多线程实例讲解 1 进程和线程 1.1 进程和线程的概念 进程(process)和线程(thread)是操作系统的基本概念。 进程是资源分配的最小单位,线程是CPU调度的最小单位。 线程是程序中一个单一的顺序控制流程,进程内一个相对独立的、可调度的执行单…...
动态gif图片如何在线做?轻松实现图片在线生成gif
常见的jpg、png格式的静态图片想要变成gif格式的动态图片时,要怎么办呢?有没有什么简单实用的gif制作工具呢? 一、什么工具能够在线制作gif? GIF中文网作为一款专业的gif制作(https://www.gif.cn/)工具&a…...
浅谈联网汽车安全漏洞
“智能网联汽车存在内生共性问题,即软硬件的漏洞后门,基于此进行的网络攻击可以直接带来勒索、盗窃、大规模车辆恶意操控风险,还有数据泄露等网络安全事件。如果内生的漏洞后门问题不解决,系统自身难保,很难谈系统安…...
深入理解SeaTunnel:易用、高性能、支持实时流式和离线批处理的海量数据集成平台
深入理解SeaTunnel:易用、高性能、支持实时流式和离线批处理的海量数据集成平台 一、认识SeaTunnel二、SeaTunnel 系统架构、工作流程与特性三、SeaTunnel工作架构四、部署SeaTunnel1.安装Java2.下载SeaTunnel3.安装连接器 五、快速启动作业1.添加作业配置文件以定义…...
项目上线 | 兰精携手盖雅工场,数智驱动绿色转型
近年来,纺织纤维行业零碳行动如火如荼。作为低碳环保消费新时尚引领者,同时也是纤维领域隐形冠军,兰精在推进绿色发展的同时,也在不断向内探索企业数字化转型之道,以此反哺业务快速扩张。 数智转型,管理先…...
102-Linux_I/O复用方法之poll
文章目录 1.poll系统调用的作用2.poll的原型3.poll支持的事件类型4.poll实现TCP服务器(1)服务器端代码:(2)客户端代码:(3)运行结果截图: 1.poll系统调用的作用 poll 系统调用和 select 类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有…...
【VAR模型 | 时间序列】帮助文档:VAR模型的引入和Python实践(含源代码)
向量自回归 (VAR) 是一种随机过程模型,用于捕获多个时间序列之间的线性相互依赖性。 VAR 模型通过允许多个进化变量来概括单变量自回归模型(AR 模型)。 VAR 中的所有变量都以相同的方式进入模型:每个变量都有一个方程式ÿ…...
Spark任务提交流程
1. yarn-client Driver在任务提交的本地机器上运行,Driver启动后会和ResourceManager通讯,申请启动ApplicationMaster; 随后ResourceManager分配Container,在合适的NodeManager上启动ApplicationMaster,此时的ApplicationMaster的…...
其它区便宜营销型网站建设/宁波seo搜索优化费用
eclipse界面定制,让eclipse看着更清爽转载于:https://www.cnblogs.com/passer1991/p/3227755.html...
国内做赌博网站代理怎么样/全网优化推广
下载地址:https://download.csdn.net/download/qq_41570658/15766712 写在最后: 微信小程序源码-合集6 https://blog.csdn.net/qq_41570658/article/details/114752580 微信小程序源码-合集5 https://blog.csdn.net/qq_41570658/article/details/11475…...
学校网站模板下载/兰蔻搜索引擎营销案例
转自:https://blog.csdn.net/xzmeasy/article/details/75103431 为什么要用jspf文件 写jsp页面时,是不是:css和js引用特别多,而且有些页面头部信息包括css、js引用都一样,这时候聪明的程序猿肯定会想到把这一部分抽取出…...
百度seo和sem/优化营商环境指什么
有两种解决方案: 1. 客户端button一定得是button类型,而不能是submit,且此服务器端的button只能用width0 height0 来隐藏,而不能用style"display:none;" 或visiblefalse来进行隐藏! 2. 在服务器上注册:this.btnStat.At…...
微信支付 网站建设/下载百度安装
互联网流量向手机倾斜,而手机端的APP又让浏览器变得不再举足轻重。但对于电脑用户而言,浏览器依然是大家日常离不开的工具:Opera、Chrome、IE、Edge(新老不同)、Firefox。在这其中又有多款浏览器使用了相同的内核(渲染及脚本引擎),…...
网站突然消失了/域名怎么注册
构造思路: 1.socket 连接获取 Banner --> 2.与存在漏洞的 Banner 集合进行对比 中间细节: 1.需要判断用户所给参数是否存在且是否有读权限 2.需要判断 Banner 是否存在,处理异常 学习 os sys socket 各个模块的基本使用 直接上代码&…...