​​

1 关键字

Acts兼容性测评;FPS;

2 问题描述

OpenHarmony版本:3.2 Release

问题描述:使用 Acts-Validator 套件进行帧率测试无结果,测试终端输出卡死

测试步骤:

  1. OpenHarmony 兼容性测试网站下载对应的 Acts-Validator 测试套件;

  2. 开发板安装 ActsValidatorTest.hap 应用;

  3. 推送下载的测试资源到开发板;

  4. 点击桌面 ActsValidatorTest 应用图标打开应用,Window系统电脑端点击.bat文件启动测试;

  5. 按照 ActsValidatorTest 应用内提示进行帧率性能测试;

3 问题原因

3.1 正常机制

帧率兼容性测试正常测试,并生成对应结果

3.2 异常机制

按照步骤进行帧率兼容性测试,无法获取FPS值,目录下的 DeskFPS.log 生成如下日志:

fps:0|315504090769

4 解决方案

由于统计不到帧率,需要在 display hid 中增加如下代码:

int32_t HdiDrmComposition::Apply(bool modeSet)
{
    ......
    if(mClientLayer->GetAcceleratorType() != ACCELERATOR_DPU)
    {
        mClientLayer->SetReleaseFence(dup(crtcOutFence));
    }
    return DISPLAY_SUCCESS;
}

代码路径:device/soc/产品名/hardware/display/src/display_device/hdi_drm_composition.cpp

5 定位过程

由于使用 ActsValidatorTest.hap 没有获取到测试 FPS 结果,于是使用 GP_daemon_fps 工具命令开始开始定位问题。

在终端输入以下命令:

GP_daemon_fps 10 | grep fps

运行指令后,也无法获取 fps

C:\Users\BF>hdc shell
# GP_daemon_fps 10 | grep fps
fps:0|10822095
fps:0|10822666
fps:0|10823668
fps:0|10824666
fps:0|10825669
fps:0|10826668
fps:0|10827667
fps:0|10828667
fps:0|10829670
fps:0|10830670

查看 GP_daemon_fps 工具源码,GP_daemon_fps 工具获取 fps 是通过 hidumper 指令获取的。

static FpsInfo GetSurfaceFrame(std::string name, FpsConfig &fpsConfig)
{
    ......
    std::string cmd = "hidumper -s 10 -a \"fps " + name + "\"";
    std::string cmdExc = cmd;
    FILE *fp = popen(cmdExc.c_str(), "r");
    ......
}
​

源码路径:developtools/profiler/host/smartperf/client/client_command_fps/main.cpp

运行 hidumper 命令获取当前 Node 节点,通过 Node 节点获取 fps

运行 hidumper 命令 ,获取到 settings0 Node 节点 (当前开发板的主界面显示 Settings 应用)

# hidumper -s 10 -a surface | grep surface
 surface [EntryView] NodeId[3418793967626] LayerId[19]:
 surface [settings0] NodeId[3418793967630] LayerId[20]:
 surface [SystemUi_StatusBar] NodeId[3418793967620] LayerId[21]:
 surface [SystemUi_PrivacyIndicator] NodeId[3418793967622] LayerId[22]:

通过 settings0 节点,执行 hidumper 命令获取当前的帧率时间戳的 buffer。在执行指令后,获取的 buffer 没有更新

# hidumper -s 10 -a "fps settings0"
-------------------------------[ability]-------------------------------
----------------------------------RenderService---------------------------------
-- The recently fps records info of screens:
 surface [settings0] Id[3418793967630]:
16806205865518
16806205865518
16806205865518
16806205865518
......

hdi_backend.cpp 文件中的 Repaint 方法调用 fence 相关的接口,获取 fence 刷新的时间戳,Repaint 方法则记录这个时间戳并更新对应的 dump 文件中,执行 hidumper 命令后,解析出对应的时间戳

在 Repaint 方法中获取时间戳后增加日志打印,再次执行 hidumper 命令后,过滤对应的日志,该时间戳没有变化

void HdiBackend::Repaint(std::vector<OutputPtr> &outputs)
{
    ScopedBytrace bytrace(__func__);
    HLOGD("%{public}s: start", __func__);
    ......
    int32_t ret = DISPLAY_SUCCESS;
    for (auto &output : outputs) {
        ......
        int64_t timestamp = lastPresentFence_->SyncFileReadTimestamp();
        HLOGI("timestamp %{public}s: ", std::to_string(timestamp).c_str()); // 增加日志打印log
        bool startSample = false;
        if (timestamp != SyncFence::FENCE_PENDING_TIMESTAMP) {
            startSample = sampler_->AddPresentFenceTime(timestamp);
            output->RecordCompositionTime(timestamp);
            for (auto iter = layersMap.begin(); iter != layersMap.end(); ++iter) {
                const LayerPtr &layer = iter->second;
                layer->RecordPresentTime(timestamp);
            }
        }
        ......
        HLOGD("%{public}s: end", __func__);
    }
}

文件路径:foundation/graphic/graphic_2d/rosen/modules/composer/hdi_backend/src/hdi_backend.cpp

GetFenceInfo 调用内核 ioctl 方法获取 fence 记录的信息

ns_sec_t SyncFence::SyncFileReadTimestamp()
{
    std::vector<SyncPointInfo> ptInfos = GetFenceInfo();
    if (ptInfos.empty()) {
        return FENCE_PENDING_TIMESTAMP;
    }
    ......
}

std::vector<SyncPointInfo> SyncFence::GetFenceInfo()
{
    struct sync_file_info arg;
    struct sync_file_info *infoPtr = nullptr;

    (void)memset_s(&arg, sizeof(struct sync_file_info), 0, sizeof(struct sync_file_info));
    int32_t ret = ioctl(fenceFd_, SYNC_IOC_FILE_INFO, &arg);
    ......
    size_t syncFileInfoMemSize = sizeof(struct sync_file_info) + sizeof(struct sync_fence_info) * arg.num_fences;
    infoPtr = (struct sync_file_info *)malloc(syncFileInfoMemSize);
    if (infoPtr == nullptr) {
        HiLog::Debug(LABEL, "GetFenceInfo malloc failed oom");
        return {};
    }
    (void)memset_s(infoPtr, syncFileInfoMemSize, 0, syncFileInfoMemSize);
    infoPtr->num_fences = arg.num_fences;
    infoPtr->sync_fence_info = static_cast<uint64_t>(uintptr_t(infoPtr + 1));
    ......
    std::vector<SyncPointInfo> infos;
    const auto fenceInfos = (struct sync_fence_info *)(uintptr_t)(infoPtr->sync_fence_info);
    for (uint32_t i = 0; i < infoPtr->num_fences; i++) {
        infos.push_back(SyncPointInfo { fenceInfos[i].timestamp_ns,
            static_cast<FenceStatus>(fenceInfos[i].status) } );
    }
    free(infoPtr);
    return infos;
}

文件路径:foundation/graphic/graphic_2d/utils/sync_gence/src/sync_fence.cpp

底层 hdi_display 会将 signal 信号量传递给 fence。具体查看 hdi_drm_composition.cpp 中的方法

int32_t HdiDrmComposition::Apply(bool modeSet)
{
    ......
    if(mClientLayer->GetAcceleratorType() != ACCELERATOR_DPU)
    {
        mClientLayer->SetReleaseFence(dup(crtcOutFence));
    }
    return DISPLAY_SUCCESS;
}

6 知识分享

HiDumper是OpenHarmony中为开发、测试人员,IDE工具提供统一的系统信息获取工具,帮助使用者分析,定位问题。 更多HiDumper信息参考

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐