标准系统xts认证HatszHdfDisplayComposerUtTest用例测试失败分析

关键字

xts;hats; display

问题描述

系统版本:OpenHarmony 4.1 Release
代码版本:OpenHarmony v4.1 Release

问题现象:xts 在执行HatszHdfDisplayComposerUtTest测试套件中有2个用例不过 SUB_Driver_Display_HDI_6600 SUB_Driver_Display_HDI_6500

问题原因

用例函数调用关系如图:

 

正常机制

 


用例的核心流程如上图,当执行commit 提交操作前会先判断是否有图层刷新,当有图层刷新时会将clientLayer置为黑色,同时将clientLayer设置为合成后数据保存的layer

异常机制

部分合成方式比如DPU直接在drm驱动中合成送显示,不会产生中间合成buffer。

解决方案

用例前添加注释,测试前注意Display HDI 适配图层的合成方式,例如展锐DPU合成无clientLayer 接受数据,可忽略此用例

定位过程

如下代码示例这两个用例失败地方都是在送显Buffer与合成后的Buffer进行像素点颜色异常比较异常导致

int32_t HdiCompositionCheck::Check(const std::vector<LayerSettings> &layers,
    const BufferHandle& clientBuffer, uint32_t checkType) const
{
    int ret = DISPLAY_SUCCESS;
    const int MID_POS = 2;
    // get the all check point
    std::vector<Point> points;
    for (auto layer : layers) {
        const IRect& RECT = layer.displayRect;
        if (checkType == CHECK_VERTEX) {
            GetCheckPoints({RECT.x, RECT.y}, points);
            GetCheckPoints({RECT.x, RECT.y + RECT.h}, points);
            GetCheckPoints({RECT.x + RECT.w, RECT.y}, points);
            GetCheckPoints({RECT.x + RECT.w, RECT.y + RECT.h}, points);
        } else {
            GetCheckPoints({RECT.x + RECT.w / MID_POS, RECT.y + RECT.h / MID_POS}, points); // center point
        }
    }

    // get all the check color
    std::vector<uint32_t> colors = GetCheckColors(layers, points);
    DISPLAY_TEST_CHK_RETURN((colors.size() != points.size()), DISPLAY_FAILURE,
        DISPLAY_TEST_LOGE("Points and colors don't match"));
    for (uint32_t i = 0; i < points.size(); i++) {
        if ((points[i].x >= clientBuffer.width) || (points[i].x < 0) || (points[i].y < 0) ||
            (points[i].y >= clientBuffer.height)) {
            continue;
        }
        ret = CheckPixel(clientBuffer, points[i].x, points[i].y, colors[i]);
        if (ret != DISPLAY_SUCCESS) {
            DISPLAY_TEST_LOGE("check failed");
            break;
        }
    }
    return ret;
}

猜测可能出现的情况:

  1. 绘制异常导致
  2. 比较异常导致
  3. 合成异常导致

下面我们来逐一分析

  1. 绘制异常导致: 分析代码可以看出在生成Layer图层时进行了颜色的填充,没有进行GPU绘制动作,可以通过保存layer 数据来检查绘制是否正常。
static std::shared_ptr<HdiTestLayer> CreateTestLayer(LayerSettings setting, uint32_t zorder)
{
    int ret;
    HdiTestDevice::GetInstance();
    DISPLAY_TEST_LOGD("color 0x%x", setting.color);
    std::shared_ptr<HdiTestDisplay> display = HdiTestDevice::GetInstance().GetFirstDisplay();
    DISPLAY_TEST_CHK_RETURN((display == nullptr), nullptr, DISPLAY_TEST_LOGE("can not get display"));

    std::shared_ptr<HdiTestLayer> layer = display->CreateHdiTestLayer(setting.bufferSize.w, setting.bufferSize.h);
    DISPLAY_TEST_CHK_RETURN((layer == nullptr), nullptr, DISPLAY_TEST_LOGE("can not create hdi test layer"));

    ............
    ............
    HdiGrallocBuffer* handle = layer->GetFrontBuffer();
    DISPLAY_TEST_CHK_RETURN((handle == nullptr), nullptr, DISPLAY_TEST_LOGE("can not get front buffer"));
    ClearColor(*(handle->Get()), setting.color);
    ret = layer->SwapFrontToBackQ();
    DISPLAY_TEST_CHK_RETURN((ret != DISPLAY_SUCCESS), nullptr, DISPLAY_TEST_LOGE("SwapFrontToBackQ failed"));
    layer->SetZorder(zorder);
    layer->SetBlendType(setting.blendType);
    layer->SetTransform(setting.rotate);
    return layer;
}

送显前需要刷新layer列表可以在此处遍历layers 保存合成前layer 的数据信息

int32_t HdiTestLayer::PreparePresent()
{
    ............
    ............
    HdiGrallocBuffer* buffer = AcquireBackBuffer();
    DISPLAY_TEST_CHK_RETURN((buffer == nullptr), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("can not get back buffer"));

    BufferHandle* handle = buffer->Get();
    DISPLAY_TEST_CHK_RETURN((handle == nullptr), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("BufferHandle is null"));
    // 将要合成的layer 数据保存到文件中
    SaveFile("/data/PreparePresent", static_cast<uint8_t *>(handle.virAddr), handle.size);
    IRect tmp {0, 0, handle->width, handle->height};
    std::vector<IRect> vRects;
    vRects.push_back(tmp);
    ret = HdiTestDevice::GetInstance().GetDeviceInterface()->SetLayerVisibleRegion(displayID_, id_, vRects);
    DISPLAY_TEST_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE,
        DISPLAY_TEST_LOGE("SetLayerVisibleRegion failed"));
    
    ............
    ............
    return DISPLAY_SUCCESS;
}

使用yuvplayer.exe 工具查看 /data/PreparePresent 文件,发现图片与预想的图层相同,绘制图层没有问题

  1. 比较异常导致

分析像素点颜色比较代码,打印合成后颜色与预设颜色

uint32_t CheckPixel(const BufferHandle &handle, int x, int y, uint32_t color)
{
    const int32_t PIXEL_BYTES = 4;
    ............
    ............
    uint32_t *pixel = reinterpret_cast<uint32_t *>(handle.virAddr) + position;
    DISPLAY_TEST_CHK_RETURN((pixel == nullptr), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("get pixel failed"));

    uint32_t checkColor = ConverToRGBA(static_cast<Composer::V1_0::PixelFormat>(handle.format), GetUint32(*pixel));
    if (checkColor != color) {
        // 添加打印 打印预设颜色和合成后的颜色
        DISPLAY_TEST_LOGD("x:%{public}d y:%{public}d width:%{public}d checkColor:%{public}d  color:%{public}d", x, y, handle.width,checkColor,color);
        SaveFile("/data/display_test_bitmap_", static_cast<uint8_t *>(handle.virAddr), handle.size);
        return DISPLAY_FAILURE;
    }
    ............
    ............
    return DISPLAY_SUCCESS;
}

通过日志分析打印获取的合成后的颜色都为黑色,用yuvplayer.exe 查看 /data/display_test_bitmap_ 为黑色,由此可以说明大概率时合成问题

  1. 合成异常导致 分析合成送显代码,保存显示后的合成数据,打印送显后handle 地址
int32_t HdiTestDisplay::Commit()
{
    int32_t fenceFd;
    int ret;
    HdiGrallocBuffer* buffer = nullptr;
    if (needFlushFb_) {
        ret = clientLayer_->SwapFrontToBackQ();
        DISPLAY_TEST_CHK_RETURN((ret != DISPLAY_SUCCESS), DISPLAY_FAILURE,
            DISPLAY_TEST_LOGE("has no front buffer display id %{public}u", id_));

        buffer = clientLayer_->GetBackBuffer();
        DISPLAY_TEST_CHK_RETURN((buffer == nullptr), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("can not get back buffer"));
        BufferHandle* handle = buffer->Get();
        DISPLAY_TEST_CHK_RETURN((handle == nullptr), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("BufferHandle is null"));
        ClearColor(*handle, 0); // need clear the fb first

        ret = buffer->SetGraphicBuffer([&](const BufferHandle* buffer, uint32_t seqNo) -> int32_t {
            int32_t result = device_->SetDisplayClientBuffer(id_, buffer, seqNo, -1);
            DISPLAY_TEST_CHK_RETURN(
                (result != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("set client buffer handle failed"));
            return DISPLAY_SUCCESS;
        });
        currentFb_ = handle;
        DISPLAY_TEST_CHK_RETURN(
            (ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("set client buffer handle failed"));
    }

    ret = device_->Commit(id_, fenceFd);
    DISPLAY_TEST_CHK_RETURN(
        (ret != DISPLAY_SUCCESS), DISPLAY_FAILURE, DISPLAY_TEST_LOGE("commit failed display id %{public}u", id_));
    // 保存送显后buffer 的内容
    SaveFile("/data/Commit", static_cast<uint8_t *>(handle->virAddr), handle->size);   
    ............
    ............
    return DISPLAY_SUCCESS;
}

通过日志分析,送显前后handle 地址没有发生变化,送显后的 buffer 数据依然为黑色,通过观察手机界面发现界面颜色为红色说明送显成功了,此时需要调查为什么buffer 数据依然为黑色,调查适配层代码

 

RK3568 合成策略如下:

 如上流程图,RK3568 默认采用RGA的合成方式,图层合成后会将合成结果保存在mClientLayer 中送显。

本产品合成策略如下:

 

 

默认优先使用DPU合成,DPU只支持4个图层,如果总图层有6个,那前面3个将使用GSP合成结果到clientLayer,再与后面的3个图层一起,共4个layer给DPU合成送显。 DPU 的合成方式没有使用ClientBuffer 保存合成数据,调查一下DPU的合成,只需要将layer 与Plane 绑定,厂商对DPU合成集成在DRM 驱动中,所以不会使用clientLayer 接收合成数据

分析用例可以发现用例图层不超过4层,参照上述流程图,本产品和RK3568 在合成方式和合成结果上存在差异,用例失败的根本原因时本产品DPU合成方式不会将结果保存到clientLayer,会导致display xts 没办法获取到合成后的信息与预想图层比较,从而导致用例失败

知识分享

查看图像数据软件 yuvplayer.exe

Logo

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

更多推荐