Android下SF合成流程重学习之Refresh流程

        Android下SF合成流程重学习之Refresh流程



引言

在前面初步分析完成了Android下SF合成流程重学习之Invalidate流程,我们接下来继续下面的分析。当有事务的更新或者有Buffer的更新便会触发后面刷新的流程,即Refresh流程!




一. onMessageRefresh

文件:frameworks/native/services/surfaceflinger/Surfaceflinger.cpp
void SurfaceFlinger::onMessageRefresh() {ATRACE_CALL();mRefreshPending = false;//这里的主要逻辑是搜集需要送显的layer的情况,封装成compositionengine::CompositionRefreshArgs参数传递//给CompositionEngine做进一步操作compositionengine::CompositionRefreshArgs refreshArgs;const auto& displays = ON_MAIN_THREAD(mDisplays);// display 的数量,把当前displaydevice 记录下来refreshArgs.outputs.reserve(displays.size());for (const auto& [_, display] : displays) {refreshArgs.outputs.push_back(display->getCompositionDisplay());}// 把当前layer记录下来mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {if (auto layerFE = layer->getCompositionEngineLayerFE())refreshArgs.layers.push_back(layerFE);});// 把当前有queueframe的layer记录下来, 有Buffer的layerrefreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());for (sp<Layer> layer : mLayersWithQueuedFrames) {if (auto layerFE = layer->getCompositionEngineLayerFE())refreshArgs.layersWithQueuedFrames.push_back(layerFE);}...// 主要逻辑在present里面mCompositionEngine->present(refreshArgs);}

前面主要搜集需要送显的layer的情况,封装成compositionengine::CompositionRefreshArgs参数传递给CompositionEngine做进一步操作。并且最最主要合成及调用Hal composer送显的逻辑都在在present里面了。




二. CompositionEngine::present

文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cppvoid CompositionEngine::present(CompositionRefreshArgs& args) {ATRACE_CALL();ALOGV(__FUNCTION__);// 根据是否还有layer没有消费掉,判断再调起一次刷新preComposition(args);{// latchedLayers is used to track the set of front-end layer state that// has been latched across all outputs for the prepare step, and is not// needed for anything else.LayerFESet latchedLayers;for (const auto& output : args.outputs) {// 计算各个区域大小以及创建hwc layeroutput->prepare(args, latchedLayers);}}// 更新layerCompositionStateupdateLayerStateFromFE(args);for (const auto& output : args.outputs) {// 逻辑在output.cpp的present里面output->present(args);}
}

2.1 preComposition

在这里插入图片描述

void CompositionEngine::preComposition(CompositionRefreshArgs& args) {ATRACE_CALL();ALOGV(__FUNCTION__);bool needsAnotherUpdate = false;mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);//调用了layer->onPreComposition做composition前的准备。进去看没做啥实际操作for (auto& layer : args.layers) {if (layer->onPreComposition(mRefreshStartTime)) {needsAnotherUpdate = true;}}mNeedsAnotherUpdate = needsAnotherUpdate;
}

其主要逻辑就是调用了layer->onPreComposition做composition前的准备。进去看没做啥实际操作。


2.2 Output::prepare

在这里插入图片描述

prepare主要的作用是收集可见的Layers并计算各个区域大小以及创建hwc layer!

//frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp
void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs,LayerFESet& geomSnapshots) {...rebuildLayerStacks(refreshArgs, geomSnapshots);
}void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs,LayerFESet& layerFESet) {...// Process the layers to determine visibility and coveragecompositionengine::Output::CoverageState coverage{layerFESet};collectVisibleLayers(refreshArgs, coverage);...
}void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs,compositionengine::Output::CoverageState& coverage) {...for (auto layer : reversed(refreshArgs.layers)) {// Incrementally process the coverage for each layerensureOutputLayerIfVisible(layer, coverage);// TODO(b/121291683): Stop early if the output is completely covered and// no more layers could even be visible underneath the ones on top.}...
}

其主要逻辑在ensureOutputLayerIfVisible里面。

2.3 ensureOutputLayerIfVisible

文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cppvoid Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,compositionengine::Output::CoverageState& coverage) {....// 创建OutputLayerauto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);...}

各个layer区域的计算结果可以通过adb shell dumpsys SurfaceFlinger看到,通过区域的计算把区域为空的layer过滤掉.

区域

ensureOutputLayer将创建Output layer,这些layer最后是要显示到屏幕上的

文件: frameworks/native/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.hOutputLayer* ensureOutputLayer(std::optional<size_t> prevIndex,const sp<LayerFE>& layerFE) {// 判断当前的outputlayer 集合里面有没有当前的layer,如果没有则新创建一个auto outputLayer = (prevIndex && *prevIndex <= mCurrentOutputLayersOrderedByZ.size())? std::move(mCurrentOutputLayersOrderedByZ[*prevIndex]): BaseOutput::createOutputLayer(layerFE);auto result = outputLayer.get();mPendingOutputLayersOrderedByZ.emplace_back(std::move(outputLayer));return result;}文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Display.cppstd::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer(const sp<compositionengine::LayerFE>& layerFE) const {auto result = impl::createOutputLayer(*this, layerFE);if (result && mId) {auto& hwc = getCompositionEngine().getHwComposer();auto displayId = *mId;// 创建hwclayerauto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId),[&hwc, displayId](HWC2::Layer* layer) {hwc.destroyLayer(displayId, layer);});ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s",getName().c_str());// 更新state.hwcresult->setHwcLayer(std::move(hwcLayer));}return result;
}

可以看到prepare主要的作用是遍历layer计算各个区域大小,过滤掉空区域的layer以及创建hwc layer。


2.4 updateLayerStateFromFE

在这里插入图片描述

它的核心逻辑是跟新更新OutputlayerState的参数,即layer的状态信息


文件:frameworks/native/services/surfaceflinger/Layer.cppvoid Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {using StateSubset = compositionengine::LayerFE::StateSubset;switch (subset) {case StateSubset::BasicGeometry:prepareBasicGeometryCompositionState();break;case StateSubset::GeometryAndContent:prepareBasicGeometryCompositionState();prepareGeometryCompositionState();preparePerFrameCompositionState();break;case StateSubset::Content:preparePerFrameCompositionState();break;case StateSubset::Cursor:prepareCursorCompositionState();break;}
}C++
文件:frameworks/native/services/surfaceflinger/Layer.cppvoid Layer::prepareCompositionState(compositionengine::LayerFE::StateSubset subset) {using StateSubset = compositionengine::LayerFE::StateSubset;switch (subset) {case StateSubset::BasicGeometry:prepareBasicGeometryCompositionState();break;case StateSubset::GeometryAndContent:prepareBasicGeometryCompositionState();prepareGeometryCompositionState();preparePerFrameCompositionState();break;case StateSubset::Content:preparePerFrameCompositionState();break;case StateSubset::Cursor:prepareCursorCompositionState();break;}
}

这部分也可以通过adb shell dumpsys SurfaceFlinger看出来有哪些属性。

image




三. Output::present

如果说前面的都是洒洒水,开胃菜。那么从这里开始就是大餐了。

文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cppvoid Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {ATRACE_CALL();ALOGV(__FUNCTION__);//设置 Display的dataspace, colormodeupdateColorProfile(refreshArgs);// 设置outputlayer的dispFrame和sourceCrop等以及将outputlayer属性设给hwcupdateAndWriteCompositionState(refreshArgs);// 设置display的颜色矩阵,对全屏有效setColorTransform(refreshArgs);// FrameBuffer里面没做啥操作beginFrame();// 选择合成策略,判断是device还是GPU合成,如果是device合成,直接present,如果要走GPU合成则需要validateprepareFrame();// 一般不走devOptRepaintFlash(refreshArgs);// GPU合成主要逻辑在此,device合成没做啥finishFrame(refreshArgs);// device合成设置release fence,GPU合成需要present给hwcpostFramebuffer();
}

先关注device合成,涉及GPU部分单独分析(简单带过)!


3.1 updateAndWriteCompositionState


文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cppvoid Output::updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs& refreshArgs) {ATRACE_CALL();ALOGV(__FUNCTION__);if (!getState().isEnabled) {return;}mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition();// 如果是有背景模糊的layer则强制使用GPU合成bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr;for (auto* layer : getOutputLayersOrderedByZ()) {// 遍历outputlayer,计算DisplayFrame, SourceCrop 等参数layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame,refreshArgs.devOptForceClientComposition ||forceClientComposition,refreshArgs.internalDisplayRotationFlags);if (mLayerRequestingBackgroundBlur == layer) {forceClientComposition = false;}// 将layer属性设给hwclayer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);}

这部分参数也可以通过adb shell dumpsys SurfaceFlinger看到:

屏幕显示区域.png



3.2 setColorTransform

文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Display.cppvoid Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {Output::setColorTransform(args);if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {return;}auto& hwc = getCompositionEngine().getHwComposer();// 将上层设的colorTransformMatrix 设给hwc,这个效果作用于全屏,比如护眼模式下,改变的就是这个colorTransformMatrixstatus_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix);ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d",mId ? to_string(*mId).c_str() : "", result);
}

setColorTransform 设置颜色矩阵给屏幕,作用于屏幕显示,所有的layer都受影响,护眼效果就是改变的这个值。


3.3 beginFrame

在这里插入图片描述

最终调用到FramebufferSurface,并没有执行具体的逻辑:

//frameworks/native/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {return NO_ERROR;
}

3.4 prepareFrame

在这里插入图片描述

它的核心逻辑是选择合成策略,判断是device还是GPU合成,如果是device合成,直接present,如果要走GPU合成则需要validate。让我们通过代码具体分析:

文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cppvoid Output::prepareFrame() {...const auto& outputState = getState();if (!outputState.isEnabled) {return;}// 选择合成类型,如果是device合成,则跳过validate,直接present送显chooseCompositionStrategy();// 把合成类型送到frameBufferSurface,没啥逻辑mRenderSurface->prepareFrame(outputState.usesClientComposition,outputState.usesDeviceComposition);
}void Output::chooseCompositionStrategy() {// The base output implementation can only do client composition// 默认使用GPU合成,针对没有hwc的设备auto& outputState = editState();outputState.usesClientComposition = true;outputState.usesDeviceComposition = false;outputState.reusedClientComposition = false;
}文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Display.cppvoid Display::chooseCompositionStrategy() {...// Default to the base settings -- client composition only.Output::chooseCompositionStrategy();...// Get any composition changes requested by the HWC device, and apply them.std::optional<android::HWComposer::DeviceRequestedChanges> changes;auto& hwc = getCompositionEngine().getHwComposer();// 从HWC device获得合成类型的改变,这个根据hwc能力来选择device还是GPU合成if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(),&changes);result != NO_ERROR) {ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,strerror(-result));return;}//如果有变化则设置给对应的layerif (changes) {applyChangedTypesToLayers(changes->changedTypes);applyDisplayRequests(changes->displayRequests);applyLayerRequestsToLayers(changes->layerRequests);applyClientTargetRequests(changes->clientTargetProperty);}// Determine what type of composition we are doing from the final state// 决定最后的合成类型auto& state = editState();state.usesClientComposition = anyLayersRequireClientComposition();state.usesDeviceComposition = !allLayersRequireClientComposition();
}文件:frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cppstatus_t HWComposer::getDeviceCompositionChanges(DisplayId displayId, bool frameUsesClientComposition,std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {...if (!frameUsesClientComposition) {sp<Fence> outPresentFence;uint32_t state = UINT32_MAX;// 如果所有的layer都能走device合成,则在hwc里面直接present,若有不支持device合成的情况,则走GPU合成,会走validate逻辑error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);if (!hasChangesError(error)) {RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);}if (state == 1) { //Present Succeeded.// present成功,数据直接提交给了hwcstd::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;error = hwcDisplay->getReleaseFences(&releaseFences);displayData.releaseFences = std::move(releaseFences);displayData.lastPresentFence = outPresentFence;displayData.validateWasSkipped = true;displayData.presentError = error;return NO_ERROR;}// Present failed but Validate ran.} else {// 这个分支走不到error = hwcDisplay->validate(&numTypes, &numRequests);}// 接收hwc过来的change,对于device合成不走,GPU合成走的逻辑,这个后续GPU合成专门分析...

prepareFrame 的作用是根据hwc的能力选择合成方式,如果是device合成则直接走hwc present上屏,如果是GPU合成后面则走hwc validate,然后根据hwc过来的变化改变layer的合成方式。用另外一种方式表达就是首先将合成策略给HWC看是否接受,然后如果有变化就将变化应用于layer


3. 5 Output::finishFrame

文件: frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cppvoid Output::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) {ATRACE_CALL();ALOGV(__FUNCTION__);if (!getState().isEnabled) {return;}// Repaint the framebuffer (if needed), getting the optional fence for when// the composition completes.// 主要针对GPU合成的逻辑auto optReadyFence = composeSurfaces(Region::INVALID_REGION, refreshArgs);// device合成直接returnif (!optReadyFence) {return;}// swap buffers (presentation)mRenderSurface->queueBuffer(std::move(*optReadyFence));
}

composeSurface的主要核心逻辑是针对GPU合成,对于device合成,则直接return了。


3.6 Output::postFramebuffer

在这里插入图片描述

这块的核心逻辑主要是对于device合成设置release fence,GPU合成需要present给hwc。我们通过代码来看下:

文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cppvoid Output::postFramebuffer() {...// device合成获取release fenceauto frame = presentAndGetFrameFences();mRenderSurface->onPresentDisplayCompleted();for (auto* layer : getOutputLayersOrderedByZ()) {sp<Fence> releaseFence = Fence::NO_FENCE;if (auto hwcLayer = layer->getHwcLayer()) {if (auto f = frame.layerFences.find(hwcLayer); f != frame.layerFences.end()) {releaseFence = f->second;}}if (outputState.usesClientComposition) {releaseFence =Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);}// 将releasefence放到mslot里面,这个是dequeueBuffer等的fencelayer->getLayerFE().onLayerDisplayed(releaseFence);}
...
}文件:frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cppstatus_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {ATRACE_CALL();RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);auto& displayData = mDisplayData[displayId];auto& hwcDisplay = displayData.hwcDisplay;// device合成直接returnif (displayData.validateWasSkipped) {// explicitly flush all pending commandsauto error = static_cast<hal::Error>(mComposer->executeCommands()); RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR);return NO_ERROR;}//GPU 合成走presentauto error = hwcDisplay->present(&displayData.lastPresentFence);RETURN_IF_HWC_ERROR_FOR("present", error, displayId, UNKNOWN_ERROR);std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;error = hwcDisplay->getReleaseFences(&releaseFences);RETURN_IF_HWC_ERROR_FOR("getReleaseFences", error, displayId, UNKNOWN_ERROR);displayData.releaseFences = std::move(releaseFences);return NO_ERROR;
}

3.6 postComposition

文件: frameworks/native/services/surfaceflinger/SurfaceFlinger.cppvoid SurfaceFlinger::postComposition()
{ATRACE_CALL();ALOGV("postComposition");nsecs_t dequeueReadyTime = systemTime();// release Bufferqueuelayerfor (auto& layer : mLayersWithQueuedFrames) {layer->releasePendingBuffer(dequeueReadyTime);}...// mPreviousPresentFences[0]是这一帧的present fence,mPreviousPresentFences[1]是上一帧的present fencemPreviousPresentFences[1] = mPreviousPresentFences[0];mPreviousPresentFences[0] =display ? getHwComposer().getPresentFence(*display->getId()) : Fence::NO_FENCE;...// 这个有关vsync,是校验presentfence释放的时间戳if (display && display->isPrimary() && display->getPowerMode() == hal::PowerMode::ON &&presentFenceTime->isValid()) {mScheduler->addPresentFence(presentFenceTime);}....
}

postComposition的作用就是释放上一帧显示的Buffer,然后重新设置当前帧的present fence,以及根据presentfence时间戳判断是否重新打开HW Vsync和软件Vsync进行校准。至此,SurfaceFlinger刷新的主体函数分析完了,可以看出来,如果是走device合成,SurfaceFlinger是直接把layer信息提交给hwc,由hwc去合成.




写在最后

好了今天的博客Android下SF合成流程重学习之Refresh流程就到这里了。总之,青山不改绿水长流先到这里了。如果本博客对你有所帮助,麻烦关注或者点个赞,如果觉得很烂也可以踩一脚!谢谢各位了!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/688169.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QT串口通讯上位机_基础串口通讯

目录 1. 实现目标1.1 界面1.2 发送1.3 接收1.4 清除接收、发送 2. 新建工程3. 添加头文件4. 变量定义5. 完整代码6. 工程下载 1. 实现目标 1.1 界面 1.2 发送 1.3 接收 1.4 清除接收、发送 2. 新建工程 3. 添加头文件 QT serialport // #include <QDebug‘’> #incl…

数据库架构师之道:MySQL安装与系统整合指南

目录 MySQL数据库安装&#xff08;centos&#xff09; 版本选择 企业版 社区版 选哪个 MySQL特点 MySQL服务端-客户端 mysql下载选择 软件包解释 安装MySQL的方式 rpm包安装 yum方式安装 源码编译安装★ 具体的编译安装步骤★★ 环境准备 free -m命令 cat /pr…

OpenAI超级视频模型Sora登上央视,LeCun强推的「世界模型」雏形相继诞生,AGI如何能够以人类的理解方式看世界?

OpenAI超级视频模型Sora热度不减 Sora一经面世&#xff0c;瞬间成为顶流&#xff0c;话题热度只增不减&#xff0c;一度登上央视新闻报道。 强大的逼真视频生成能力&#xff0c;让许多人纷纷惊呼「现实不存在了」。 OpenAI官方技术报告 OpenAI官方Sora技术报告&#xff1a;V…

JMeter 配置元件之按条件读取CSV Data Set Config

实践环境 win10 JMeter 5.4.1 需求描述 需求是这样的&#xff0c;需要压测某个接口(取消分配接口)&#xff0c;请求这个接口之前&#xff0c;需要先登录系统(物流WMS系统)&#xff0c;并在登录后&#xff0c;选择并进入需要操作的仓库&#xff0c;然后请求接口&#xff0c;…

我的NPI项目之Android Camera (二) -- 核心部件之 Camera Sensor

说到Camera模组&#xff0c;我们比较关心的是用的什么样的sensor&#xff1f; sensor的分辨率多少&#xff0c;sensor的像素多大&#xff0c;sensor是哪家生产的等等一些问题。今天&#xff0c;我们就穿越时间&#xff0c;将sensor的历史扒一扒。 Wikipedia先看一下&#xff1…

MOSFET栅极应用电路分析汇总(驱动、加速、保护、自举等等)

概述 MOSFET是一种常见的电压型控制器件&#xff0c;具有开关速度快、高频性能、输入阻抗高、噪声小、驱动功率小、动态范围大、安全工作区域(SOA)宽等一系列的优点&#xff0c;因此被广泛的应用于开关电源、电机控制、电动工具等各行各业。栅极做为MOSFET本身较薄弱的环节&am…

【C++11新特性】详解智能指针 创建、使用、注意事项

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

小白如何学鸿蒙开发?

在互联网技术不断发展的现在&#xff0c;鸿蒙操作系统的出现标志着是能技术领域的一次重大突破&#xff0c;鸿蒙作为华为推出的一代操作系统&#xff0c;鸿蒙不仅达代表了自主创新的力量&#xff0c;还因为独特的分布式架构和全场景适配能力而备受关注。随着鸿蒙生态的不断完善…

测试架构师必备技能 —— Nginx安装部署实战

Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的免费开源Web和 反向代理服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。在高并发访问的情况下&#xff0c;Nginx是Apache服务器不错的替代品。官网数据显示每秒TPS高达50W左右。本文…

左旋字符串解析

题目 实现一个函数&#xff0c;可以左旋字符串中的k个字符。 例如&#xff1a; ABCD左旋一个字符得到BCDA ABCD左旋两个字符得到CDAB 法1&#xff1a;一个个移动 #include<stdio.h> #include<string.h>//把一个字符串s,左移time个字符 void leftRound(char* s…

leetcode日记(26)有效的数独

用暴力解法解出来的&#xff0c;判断3*3那要写的比较多&#xff0c;判断竖列那花了点功夫。 不知道有没有更好的解法。 class Solution { public:bool isValidSudoku(vector<vector<char>>& board) {for(int i0;i<9;i){vector<char>cboard[i];for(i…

[Docker实战] 旭日X3派上Docker Openwrt +Samba 实现局域网NAS 开启AP模式

​ &#x1f308; 博客个人主页&#xff1a;Chris在Coding &#x1f3a5; 本文所属专栏&#xff1a;[旭日X3派] [Docker实战] ❤️ 前置学习专栏&#xff1a;[Linux学习] ⏰ 我们仍在旅途 …

创建补丁文件.patch

前言 在linux中&#xff0c;在工程实践中。 自己基于文件1进行了修改&#xff0c;得到文件2&#xff0c;文件1有线上仓库。时常会遇到以下两种情景&#xff1a; 由于文件过大&#xff0c;直接向组员分享文件2很麻烦。由于修改地方过多&#xff0c;每次更换环境都需要重新修改文…

【JAVA语言-第18话】集合框架(五)——Map、HashMap、LinkedHashMap、TreeMap集合

目录 双列集合Map 1.1 概述 1.2 特点 1.3 Map接口中的常用方法 1.3.1 练习 1.4 HashMap集合 1.4.1 概述 1.4.2 特点 1.5 LinkedHashMap集合 1.5.1 概述 1.5.2 特点 1.6 TreeMap集合 1.6.1 概述 1.6.2 特点 1.7 Map集合练习 1.8 HashMap、LinkedHashMap、Tre…

【EI会议征稿通知】第五届城市工程与管理科学国际会议(ICUEMS 2024)

【Scopus稳定检索】第五届城市工程与管理科学国际会议&#xff08;ICUEMS 2024&#xff09; 2024 5th International Conference on Urban Engineering and Management Science 第五届城市工程与管理科学国际会议&#xff08;ICUEMS 2024&#xff09;将于2024年5月31日-6月2日…

GitKraken Create Repository and Clone不可点击

问题 GitKraken Create Repository and Clone不可点击 详细问题 笔者第一次使用GitKraken&#xff0c;在创建仓库时&#xff0c;填写完成仓库初始化后。发现Create Repository and Clone不可点击。 解决方案 选择Where to clone to位置 产生原因 在创建仓库时&#xff0…

洛谷: P1479 宿舍里的故事之五子棋

题目链接: https://www.luogu.com.cn/problem/P1479 思路: 这道题目可以打表或者搜索。每个位置有选择/不选择两种情况。搜索的时候我们一行一行的搜索&#xff0c;直到使用的棋子达到n为止。b[i]为五子连线的数量&#xff0c;b[i] 1表示五子连线的数量可以取i&#xff0c;在…

day6:继承与多态

思维导图 2.编程题&#xff1a; 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a;比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff…

如何引导llm为自己写prompt生成剧本

如何使用写prompt让你自己生一个狗血修仙穿越短剧&#xff0c;且短剧有趣生动让人流连忘返 好的&#xff0c;我会尝试编写一个狗血修仙穿越短剧的prompt&#xff0c;以激发你的想象力&#xff0c;让你创作出一个既有趣又生动的短剧。以下是我的prompt&#xff1a; 标题&#x…

简洁高效的短链接:优化互联网体验

title: 简洁高效的短链接&#xff1a;优化互联网体验 date: 2024/2/18 13:24:24 updated: 2024/2/18 13:24:24 tags: 短链接长网址缩短美观简洁分享优化点击率提升数据统计用户体验 在互联网时代&#xff0c;我们经常遇到需要分享长网址的情况。长网址不仅不美观&#xff0c;而…