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的layer
    refreshArgs.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.cpp

void 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 layer
            output->prepare(args, latchedLayers);
        }
    }
    // 更新layerCompositionState
    updateLayerStateFromFE(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 coverage
    compositionengine::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 layer
        ensureOutputLayerIfVisible(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.cpp

void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE,
                                        compositionengine::Output::CoverageState& coverage) {

    ....
    // 创建OutputLayer
    auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE);
    ...
 }

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

区域

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

文件: frameworks/native/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h

OutputLayer* 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.cpp

std::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;
        // 创建hwclayer
        auto 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.hwc
        result->setHwcLayer(std::move(hwcLayer));
    }
    return result;
}

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


2.4 updateLayerStateFromFE

在这里插入图片描述

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


文件:frameworks/native/services/surfaceflinger/Layer.cpp

void 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.cpp

void 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.cpp

void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) {
    ATRACE_CALL();
    ALOGV(__FUNCTION__);
     //设置 Display的dataspace, colormode
    updateColorProfile(refreshArgs);
    // 设置outputlayer的dispFrame和sourceCrop等以及将outputlayer属性设给hwc
    updateAndWriteCompositionState(refreshArgs);
    // 设置display的颜色矩阵,对全屏有效
    setColorTransform(refreshArgs);
    // FrameBuffer里面没做啥操作
    beginFrame();
     // 选择合成策略,判断是device还是GPU合成,如果是device合成,直接present,如果要走GPU合成则需要validate
    prepareFrame();
    // 一般不走
    devOptRepaintFlash(refreshArgs);
    // GPU合成主要逻辑在此,device合成没做啥
    finishFrame(refreshArgs);
    // device合成设置release fence,GPU合成需要present给hwc
    postFramebuffer();
}

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


3.1 updateAndWriteCompositionState


文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Output.cpp

void 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属性设给hwc
        layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame);
    }

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

屏幕显示区域.png



3.2 setColorTransform

文件:frameworks/native/services/surfaceflinger/CompositionEngine/src/Display.cpp

void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) {
    Output::setColorTransform(args);

    if (!mId || CC_LIKELY(!args.colorTransformMatrix)) {
        return;
    }

    auto& hwc = getCompositionEngine().getHwComposer();
    // 将上层设的colorTransformMatrix 设给hwc,这个效果作用于全屏,比如护眼模式下,改变的就是这个colorTransformMatrix
    status_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.cpp

void 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.cpp

void 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;
    }
    //如果有变化则设置给对应的layer
    if (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.cpp

status_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成功,数据直接提交给了hwc
            std::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.cpp

void 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合成直接return
    if (!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.cpp

void Output::postFramebuffer() {
   ...
    // device合成获取release fence
    auto 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等的fence
        layer->getLayerFE().onLayerDisplayed(releaseFence);
    }
...
}

文件:frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp

status_t HWComposer::presentAndGetReleaseFences(DisplayId displayId) {
    ATRACE_CALL();
    
    RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
    
    auto& displayData = mDisplayData[displayId];
    auto& hwcDisplay = displayData.hwcDisplay;
     // device合成直接return
    if (displayData.validateWasSkipped) {
        // explicitly flush all pending commands
        auto 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 合成走present
    auto 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.cpp

void SurfaceFlinger::postComposition()
{
    ATRACE_CALL();
    ALOGV("postComposition");

    nsecs_t dequeueReadyTime = systemTime();
     // release Bufferqueuelayer
    for (auto& layer : mLayersWithQueuedFrames) {
        layer->releasePendingBuffer(dequeueReadyTime);
    }
    ...
     // mPreviousPresentFences[0]是这一帧的present fence,mPreviousPresentFences[1]是上一帧的present fence
    mPreviousPresentFences[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流程就到这里了。总之,青山不改绿水长流先到这里了。如果本博客对你有所帮助,麻烦关注或者点个赞,如果觉得很烂也可以踩一脚!谢谢各位了!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue24 收集表单数据 实例

实例 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>收集表单数据</title><script type"text/javascript" src"../js/vue.js"></script></head><body><!-- 收集…

线性回归-使用ClickHouse机器学习函数

本文字数&#xff1a;5923&#xff1b;估计阅读时间&#xff1a;15 分钟 作者&#xff1a;Ensemble 审校&#xff1a;庄晓东&#xff08;魏庄&#xff09; 本文在公众号【ClickHouseInc】首发 这原本是转发的ensemble analytics的文章。 【https://ensembleanalytics.io/blog/l…

校园流浪猫信息记录和分享的小程序源码/微信云开发中大猫谱小程序源码

这是一套用于校园流浪猫信息记录和分享的小程序源码&#xff0c;微信云开发中大猫谱小程序源码。主要功能是创建校园猫猫档案&#xff0c;为猫猫上传照片&#xff0c;以及照片审核&#xff0c;人员管理等&#xff0c;并且可以拍照记录校园内猫猫的成长轨迹&#xff0c;该程序由…

【Java EE初阶十三】网络初识

1. 网络发展史 网络发展的几个主要时期&#xff1a; 单机时代->局域网时代->广域网时代->移动互联网时代 随着时代的发展&#xff0c;越来越需要计算机之间互相通信&#xff0c;共享软件和数据&#xff0c;即以多个计算机协同工作来完成 业务&#xff0c;就有了网络互…

代码随想录算法训练营第43天(动态规划05 ● 1049. 最后一块石头的重量 II ● 494. 目标和 ● 474.一和零

动态规划 part05 1049. 最后一块石头的重量 II解题思路 494. 目标和解题思路 474.一和零解题思路总结 详细布置 1049. 最后一块石头的重量 II 本题就和 昨天的 416. 分割等和子集 很像了&#xff0c;可以尝试先自己思考做一做。 题目链接&#xff1a; 1049. 最后一块石头的重量…

十五、环境变量和代理跨域及api的定义

环境变量的定义 在根目录下新建三个环境变量配置文件 .env.development&#xff08;开发环境&#xff09;.env.test&#xff08;测试环境&#xff09;.evn.production&#xff08;生产环境&#xff09;分别定义开发环境、线上环境和测试环境的变量 webpack VUE_APP_TITLE 学…

第二篇【传奇开心果系列】Python的文本和语音相互转换库技术点案例示例:深度解读pyttsx3支持多种语音引擎

传奇开心果短博文系列 系列短博文目录Python的文本和语音相互转换库技术点案例示例系列 短博文目录前言一、三种语音引擎支持介绍和示例代码二、SAPI5引擎适用场景介绍和示例代码三、nsss引擎适用场景介绍和示例代码四、eSpeak适用场景介绍和示例代码五、归纳总结 系列短博文目…

PPT怎么输出PDF(不留白)

1、首先选中所有元素&#xff0c;右键点击“组合”形成一个对象。然后查看该对象的高度和宽度。 2、在设计->自定义->幻灯片大小中-->选择“自定义”&#xff0c;然后修改高度和宽度稍稍大于选中对象的值。点击“最大化”。 3、输出为PDF即可

【Java EE初阶十七】网络原理(二)

2. 传输层 2.2 TCP协议 2.2.2 关于可靠传输 4.滑动窗口 前面的三个机制&#xff0c;都是在保证 tcp 的可靠性&#xff1b; TCP 的可靠传输,是会影响传输的效率的.(多出了一些等待 ack 的时间,单位时间内能传输的数据就少了)&#xff1b; 滑动窗口,就让可靠传输对性能的影响,更…

sora的理解

1、背景 近期, openai紧跟Runway、 Google、Meta等公司, 发布了视频生成模型Sora, 全面进军视频领域。官网的视频效果炸裂&#xff0c;连贯性优秀&#xff0c;生成视频时长可达60秒&#xff0c;但模拟复杂物理场景仍有瑕疵。相对Pika、Runway的效果还是有进一步提升。考虑到这…

哪种台灯的灯光适合学生用?明基/书客/松下等护眼台灯推荐

目前近视人群越来越多&#xff0c;并且有低龄化的倾向。针对护眼这一卖点&#xff0c;市面上出现了很多护眼台灯品牌&#xff0c;但是很多不知名的网红品牌生产出来的产品质量没有办法得到保障。在挑选护眼台灯时&#xff0c;还是要先做好攻略才不会踩雷。 一、使用护眼台灯更…

基于Java SSM框架实现留学生交流互动论坛网站项目【项目源码+论文说明】计算机毕业设计

摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

量化交易开发之循环、多股策略语法(六)

量化交易开发之循环、多股策略语法&#xff08;六&#xff09; 一、用list数据类型存储多个股票 以如下这个简单的策略为例&#xff0c;学习在策略中操作多个股票&#xff1a; def initialize(context):run_daily(period, timeevery_bar)g.security 000001.XSHEdef period(c…

java 课程签到管理系统Myeclipse开发mysql数据库web结构jsp编程servlet计算机网页项目

一、源码特点 java 课程签到管理系统是一套完善的java web信息管理系统 采用serlvetdaobean&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0…

聚道云软件连接器助力生产制作行业实现数字化升级

在数字经济时代&#xff0c;生产制造行业迫切需要进行数字化转型&#xff0c;通过数字化技术手段打通各系统之间的数据壁垒&#xff0c;实现生产全流程数字化管理&#xff0c;提高企业的整体运营效率&#xff0c;进一步增强企业竞争力。聚道云为此推出了生产制造行业的集成管理…

哎呀,当时怎么没有想到 | 京东云技术团队

在我们的测试工作中&#xff0c;是不是经常遇到这样的情形&#xff0c;发生了线上问题&#xff0c;产品、研发或者测试同学一拍脑袋&#xff1a;当时怎么没有想到&#xff0c;怎么给漏掉了呢&#xff1f;明明是一个非常简单的事情&#xff0c;用大拇指都能想到的验证场景&#…

Linux-ls命令

目录 ls&#xff1a;查看目录下文件/文件夹 ls -l&#xff1a;列表显示文件 ls -a&#xff1a;显示所有文件正常情况下‘ . ’开头的文件是隐藏的 ls -la&#xff1a;以列表形式显示所有文件包括隐藏文件 ls -lt&#xff1a;按时间倒序查看文件 ls -R&#xff1a;递归方式…

c++中浮点类型比较的理解

为什么浮点类型存在误差 带有小数的表示&#xff1a; 25.3 整数通过除2取余法表示&#xff1a; 25/2…1 12/2…0 6/2…0 3/2…1 1/2…1 倒过来&#xff1a;25&#xff08;十进制&#xff09; 11001&#xff08;二进制&#xff09; 小数部分通过乘2取整法&#xff1a; 0.3 * 2 …

OpenCV DNN 活体检测项目环境配置等各阶段tips

date: 2020-09-22 14:53 资料来源《OpenCV深度学习应用与性能优化实践》第八章。 在复现这个项目的时候发现一些可以调整的小tips。 环境配置阶段 使用conda 创建python 工作环境时&#xff0c;注释掉requirems.txt 里的opencv-python-inference-engine4.1.2.1&#xff0c;安…

【JavaEE】_线程与多线程的创建

目录 1. 线程的概念 2. 创建与使用多线程 2.1 方式1&#xff1a;继承Thread类 2.2 方式2&#xff1a; 实现Runnable接口 2.3 以上两种创建线程方式的对比 3. 多线程的优势-增加运行速度 1. 线程的概念 进程的存在是由于系统的多任务执行需求&#xff0c;这也要求程序员进…