当前位置: 首页 > news >正文

三方相机问题分析七:【datespace导致GPU异常】facebook 黑块和Instagram花图问题

【关注我,后续持续新增专题博文,谢谢!!!】

上一篇我们讲了

        这一篇我们开始讲 

目录

一、问题背景

二、:问题分析过程

    2.1:基于原理分析

    2.2 :camx hal dump分析

    2.3 :三方APP分析

    2.4 :显示模块分析

    2.5 :高通分析

    2.6 :不同平台对比分析

    2.7 :dataspace差异分析

    2.8 :gpu 获取dataspace分析

    2.9 :dataspace设置分析

    2.10 :Dataspace二次修改分析

  2.11 :dump gfxinfo 对比差异

  2.12 :camx hal分析Dataspace二次修改

  2.10 :解决方案

【关注我,后续持续新增专题博文,谢谢!!!】


一、问题背景

【前提条件】【Prerequistes】下载安装Instagram
【操作步骤】【Operation steps】在首页点击加号开启直播,使用8K UItra HD 特效进行前后置切换
【实际结果】【Actual results】出现花图

【出现次数/测试次数】【Occurrence Times/Test Times】必现

二、:问题分析过程

    2.1:基于原理分析

由于是花图问题,日志一般很难分析具体根因,也无法知道具体哪个环节/算法出现的问题,也无法知道是哪个模块出现的问题,camera/display/app从上到下均有可能。因为涉及到相机app,一般都是优先camera hal来分析。

    2.2 :camx hal dump分析

由于是必现问题,我们首先复现并dump camx hal YUV。发现IPE YUV是正常的 ,并没有花图,因此并非camera HAL的出图问题。因此要么是系统层的问题,要么是三方app自身问题。

    2.3 :三方APP分析

由于问题转三方分析,一般需要内部对比机和外部对比机存在这个问题,才能确定是三方app的问题。由于此问题是必现的,而其他机型对比机无法复现此问题,因此无法确定是三方app自身问题,而且可能性不大。这时需要系统层来分析,特别是屏显组,也就是显示模块,包括GPU。

    2.4 :显示模块分析

显示组抓到问题渲染,提case和高通确认一下,怀疑和 gpu 浮点精度有关。

本地尝试使用 RenderDoc 抓取到问题关键帧,但无法抓取到。dcap 提供给高通,看是否有方法拿到问题帧的绘制流程。高通目前认为该问题为三方 shader 的问题。但有一些疑问需要确认。

    2.5 :高通分析

app 在暗色模式下做 gpu 渲染时,在对 yuv 数据采样之后,使用 texel.rgb = clamp(texel.rgb, 0.0, 1.0) 函数将输出结果 texel 限制到 [0,1] 范围内。

        

这里的重点在于输入的 yuv 数据为 full range, 但是预览的时候 dataspace 为 unknow.

为什么相机送过来的 yuv 数据不是 limit range, 而是 full range?

白色高亮区域的 Y 的值,全部都是 255。 符合 full range 的特征;但是 GPU 还是使用的默认的 BT605 + limit range 的转换矩阵,高亮部分像素值就会出现问题;

    2.6 :不同平台对比分析

对比情况如下:

  1. 只有 6450有这个问题
  2. dcap 在 8750 回看也有问题
  3. 高亮区域的值也是 full range? GraphicBuffer 中的 dataspace也是 unknow 状态?
    1. 在 SM8650 平台抓取的 ubwc_nv12 的原始流,高亮的区域纯白画面的像素值来看,为 full range. 
  4. 为什么 SM8650 平台没有出现黑块?为什么 SM8750 平台没有出现黑块?

    2.7 :dataspace差异分析

在 SM8750 平台上加trace, 看 graphicBuffer 的 dataspace:

预览的时候,graphicBuffer 的 dataspace 为 FullRange

Color Standard: STANDARD_BT601_625
Color Transfer: TRANSFER_SMPTE_170M
Color Range   : RANGE_FULL

在 SM8650 平台上加trace 的 dataspace

6450 平台上,gpu acqiure buffer 的时候,graphicBuffer 中的 dataspace 值为0。但是实际上 dump 出来的数据,是 full range 的数据;

通过trace分析,会发现Camera Server 进程送给 app 的预览流在 queueBuffer 阶段,从 graphicbuffer 的 metadata 中读取到的 dataspace 为0; 但是实际上的yuv raw 数据是 full range 类型,不可能 dataspace 为0;

而且sm8750 平台和 sm8650 平台可以拿到正确的 dataspace 信息;

    2.8 :gpu 获取dataspace分析

app 在拿到 graphicBuffer (yuv 纹理之后)需要创建 EGLImage 对象绑定 buffer, 才能给 gpu 使用。在调用 eglCreateImageKHR() 函数的时候, 驱动会去读取 metadata 信息拿 dataspace 信息;但是驱动不通过 GraphicBuffer 的 getDataspace 或者 GraphicMapper 的 getmetadata() 而是直接通过 gralloc 的 QtiMapper5::GetMetadataPrivate 函数去拿到 metadata 信息;

// gpu 驱动直接通过 native handle 通过 gralloc 拿到 metadata 信息
int32_t QtiMapper5::GetMetadataPrivate(buffer_handle_t _Nonnull bufferHandle, int64_t metadataType,void *_Nonnull outData, size_t outDataSize,bool isStandard) {char trace_tag[64] = {0, };snprintf(trace_tag, 64, "GetMetadataPrivate[%d] %p", metadataType, bufferHandle);ALOGE("[yjh] getMetadataPrivate[%d] from handle %p", metadataType, bufferHandle);ATRACE_BEGIN(trace_tag);if (!(bufferHandle)) {ALOGW("Failed to %s. Null buffer_handle_t.", __func__);return -AIMAPPER_ERROR_BAD_BUFFER;}if (!snap_helper_ || !snap_alloc_enable_) {return -AIMAPPER_ERROR_NO_RESOURCES;}int32_t size_required = outDataSize;auto snap_error =snap_helper_->GetMetadata(const_cast<native_handle_t *>(bufferHandle), metadataType, outData,false, false, isStandard, &size_required);if (snap_error == SnapError::NONE) {ATRACE_END();return size_required;} else {ATRACE_END();return -(static_cast<int32_t>(snap_error));}
}

所以 GraphicBuffer 的 metadata 信息必须设置正确;并且必须在创建 EGLImage 之前就设置好;因为GraphicBuffer 和 EGLImage 是绑定的,一般 app也只需要创建一次, 所以 dataspace 信息只会读取一次;中途改变 dataspace, 必须要重新创建 EGLImage;

    2.9 :dataspace设置分析

所有针对buffer 的 dataspace 修改,最终都会调用到QtiMapper 的 setMetadata() 接口或者setStandardMetadata() 接口;

1. camera hal 写 dataspace 是直接拿 buffer handle 直接调用 setMetadata() 接口去设置;

dataspace.colorPrimaries = static_cast<SnapColorPrimaries>(pGralocMetadata->colorPrimaries);
dataspace.range          = static_cast<SnapColorRange>(pGralocMetadata->range);
dataspace.transfer       = static_cast<SnapGammaTransfer>(pGralocMetadata->transfer);// 在这里写入 dataspace
auto mapper_err = STABLEMAPPER(pGrallocIntf->qtiAidlMapper).setMetadata(reinterpret_cast<buffer_handle_t>(phNativeHandle), VENDOR_QTI_METADATA(SnapMetadataType::DATASPACE),static_cast<VOID *>(&dataspace), sizeof(dataspace));

2.surface 和 GraphicBuffer 则是通过 GraphicMapper 的 setDataspace 接口去进行设置;通过getDataspace 接口去进行读取:

status_t GraphicBufferMapper::setDataspace(buffer_handle_t bufferHandle, ui::Dataspace dataspace) {return mMapper->setDataspace(bufferHandle, dataspace);
}status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle,ui::Dataspace* outDataspace) {ATRACE_NAME("GBM::getDataspace");return mMapper->getDataspace(bufferHandle, outDataspace);
}

3. 那么如何确定底层camera hal 写入的 buffer 和上层 surface 读取的 buffer 是同一个 buffer?

buffer 的 native_handle_t 指针在不同的进程中地址是不一样的,native_handle_t 中的 fd 在不同的进程中也是不一样的;但是对于同一个buffer, 其 fd 的 inode 号是一致的。 所以可以在QtiMapper 的 setMetadata 和 getMetadata 函数内打印对应 native handle 的 inode。

Error QtiMapper5::setStandardMetadata(buffer_handle_t _Nonnull bufferHandle,int64_t standardTypeRaw, const void *_Nonnull metadata,size_t metadataSize) {char trace_tag[64] = {0, };metadataSize = (metadataSize == 0 && metadata == nullptr) ? 1 : metadataSize;struct stat statA;if (bufferHandle->numFds >= 1) {int fdA = bufferHandle->data[0];fstat(fdA, &statA);}// 拿到 fd 的 inode 号snprintf(trace_tag, 64, "QtiMapper5::setStandardMetadata[%d] - [%lu,%lu]", standardTypeRaw, statA.st_ino, statA.st_dev);ALOGE("QtiMapper5::setStandardMetadata[%d] - [%lu,%lu]", standardTypeRaw, statA.st_ino, statA.st_dev);ATRACE_BEGIN(trace_tag);if (standardTypeRaw == 17) {   // metadata 对应 dataspace// dataspaceDataspace *dataspace = (Dataspace*)(metadata);int colorPrimaries = dataspace->colorPrimaries;int range = dataspace->range;int transfer = dataspace->transfer;// 解析和获取 dataspacesnprintf(trace_tag, 64, "color:%d, range:%d, transfer:%d", colorPrimaries, range, transfer);ATRACE_BEGIN(trace_tag);ATRACE_END();}Error err =  (SetMetadataPrivate(bufferHandle, standardTypeRaw, metadata, metadataSize, true));snprintf(trace_tag, 64, "err: %d", err);ATRACE_BEGIN(trace_tag);ATRACE_END();ATRACE_END();return err;
}

通过上面的trace 打印,可以很快定位到所有修改过我们需要查询的 native_handle 的 metadata 在哪些地方被修改。

    2.10 :Dataspace二次修改分析

从抓取的 systrace 中来看,buffer 在被 app 消费之前,metadata 一共被修改过两次:

  • camera hal 通过 Qtimapper 将 metadata 数据写入到 buffer 的 metadata 中;这次写入的值是正常的。
  • cameraserver preview 线程在做 queueBuffer(Surface.cpp) 时,会将 mDataspace 重新再写入一次;这次写入的是0,是有问题的;
  • 这里写入的值来自哪里:

  • Surface::queueBuffer -> applyGrallocMetadataLocked(buffer, input)
    void Surface::applyGrallocMetadataLocked(android_native_buffer_t* buffer,const IGraphicBufferProducer::QueueBufferInput& queueBufferInput) {ATRACE_CALL();char trace_tag[64] = {0, };auto& mapper = GraphicBufferMapper::get();snprintf(trace_tag, 64, "Surface[%p] setDataspace [%d]", this, (uint32_t)(queueBufferInput.dataSpace));ATRACE_NAME(trace_tag);// 在这里会覆盖一次 dataspace 信息;这里写入的是 0(错误值)mapper.setDataspace(buffer->handle, static_cast<ui::Dataspace>(queueBufferInput.dataSpace));

    这里的 dataspace 的值来自于 queueBufferInput,可以追踪代码,其最终来自 Surface 的 mDataspace 成员变量

    唯一修改 mDataspace 的位置:

  • int Surface::setBuffersDataSpace(Dataspace dataSpace)
    {char trace_tag[64];snprintf(trace_tag, 64, "Surface[%p]::setBuffersDataSpace[%d]", this, (uint32_t)(dataSpace));ATRACE_NAME(trace_tag);ALOGV("Surface::setBuffersDataSpace");Mutex::Autolock lock(mMutex);mDataSpace = dataSpace; // 在这里被修改;return NO_ERROR;
    }

    根据 trace 打印,定位到是 cameraserver 的 preview 线程在 configStreams() 的时候,会给 Surface 设置 dataspace;cameraserver 在 configure streams 时,会写入 dataspace, 但写入的是一个错误的值;调用 Surface::perform()。

  2.11 :dump gfxinfo 对比差异

cameraserver 查看流配置信息,dataspace 设置情况是0x102。

0x102 我们可以看到其定义为:HAL_DATASPACE_BT601_625 = 258;这里的 dataspace 只给出了 Standard, 没有给出 Transfer 和 Range 的信息;

相机完整的 yuv 数据的 dataspace 为:

HAL_DATASPACE_V0_JFIF = 146931712,  // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)

cameraserver 在 setDataspace 的时候,直接传入了 HAL_DATASPACE_BT601_625 这个数值,是一个不完整的 dataspace; 实际上 metadata 并没有实际写入;此时,buffer 中的 dataspace 将会保持 camera hal 写入的原始的 HAL_DATASPACE_V0_JFIF 正确值;

但是写入 0x00(UNKNOW) 这个默认值,QtiMapper 的写入将会成功;所有 buffer 中的 dataspace 写入的正确的 dataspace 将会被抹除掉。

通过dump gfxinfo 对比差异

  1. 这里可以看到送 gpu 渲染的 buffer 的 dataspace 信息,为0x8c20000, 即 bt601 full range;
  2. 从 app 的 gfxinfo 对比可以看出,surfacetexture 中 buffer 的 dataspace 在6450 平台和在 8750 平台上的差异是不一样的。

通过显示模块分析,最终确定是相机模块二次修改dataspace导致的问题,camx hal接力分析。

  2.12 :camx hal分析Dataspace二次修改

通过trace分析,第二次在cameraserver GUI模块set的dataspace参数值,GUI模块的说是从camera configstream的时候set到GUI的,

目前大概可以确定dataspace的问题,且出现在cameraserver configstream的时候,下面需要排查下camera configstream的时候,为啥set dataspace的有问题,尽快流转或解决这个参数的问题。

通过加日志,最终定位到:

在平台逻辑PipelineDescriptor* ChiContext::CreatePipelineDescriptor(

的时候设置dataspace的时候错误,具体是由于某种场景下少set了一路steam的dataspace值,因为之前的时候三方并不会使用这个,SM8550等平台,由于高通的代码及时的更新修复这块的逻辑,所以没有问题。在SM6450上属于三方新功能使问题暴露出来了。

  2.10 :解决方案

分析:{GPU:白色高亮区域的 Y 的值,全部都是 255。 符合  full range 的特征;
但是 GPU 还是使用的默认的 BT605 + limit range 的转换矩阵,高亮部分像素值就会出现问题;
输入的 yuv 数据为 full range, 但是预览的时候 dataspace 为 unknow,这时候的dataSpace应该为DataspaceBT601_625:0x102}
方案:{平台在configstream会根据条件对stream set dataSpace,而问题场景没有set。
三方新功能,平台逻辑老,添加部分逻辑在该场景set stream dataSpace为0x102。
提Qcom Case和qcom确认可以如此修改。也和GPU模块确认过.目前三方的预览流dataspace都是0x102才是正常的

diff --git a/src/core/chi/camxchicontext.cpp b/src/core/chi/camxchicontext.cpp
index baa8130..06a024f 100755
--- a/src/core/chi/camxchicontext.cpp
+++ b/src/core/chi/camxchicontext.cpp
@@ -4874,6 +4874,24 @@}}+#if (defined(CAMX_ANDROID_API) && (CAMX_ANDROID_API >= 32)) // Android-T or better
+            // For Android T new API, we should check preview based on its own per stream hdr profile
+            if (((GrallocUsageHwComposer == (GetGrallocUsage(pChiStream) & GrallocUsageHwComposer)) ||
+                 (GrallocUsageHwTexture  == (GetGrallocUsage(pChiStream) & GrallocUsageHwTexture )))   &&
+                (0 == (GetGrallocUsage(pChiStream) & GrallocUsageHwVideoEncoder)))
+            {
+                if ((FALSE                      == overrideImpDefinedFormat.isHDR) &&
+                    (StreamHDRMode::HDRModeNone == HDRModeValue))
+                {
+                    // Set default preview stream dataspace
+                    pChiStream->dataspace = DataspaceBT601_625;
+                }
+
+                CAMX_LOG_INFO(CamxLogGroupCore, "pChiStream:%p,override dataspace:%x",
+                    pChiStream, pChiStream->dataspace);
+            }
+#endif // Android-T or better
+// HEIC streams are snapshot streams, data space should not be updated for HEIC Streamif ((DataspaceHEIF              != pChiStream->dataspace) &&(GrallocUsageHwImageEncoder != (GetGrallocUsage(pChiStream) & GrallocUsageHwImageEncoder)) &&

【关注我,后续持续新增专题博文,谢谢!!!】

下一篇讲解

http://www.dtcms.com/a/320522.html

相关文章:

  • Linux Shell:Nano 编辑器备忘
  • 以下是使用这款ePub编辑器将指定章节转换为TXT文本文档的操作方法
  • (数据结构)链表
  • Android 安全编程:Kotlin 如何从语言层保障安全性
  • Kotlin反射
  • HarmonyOS SDK助力讯飞听见App能力建设
  • 高德地图地理编码 逆地理编码全解析:地址和坐标的双向转换实战
  • HarmonyOS 设备自动发现与连接全攻略:从原理到可运行 Demo
  • 深入理解 robots.txt:网站与搜索引擎的 “沟通协议”
  • DataFun联合开源AllData社区和开源Gravitino社区将在8月9日相聚数据治理峰会论坛
  • 控制建模matlab练习12:线性状态反馈控制器-①系统建模
  • Ideogram:优秀的在线AI绘画平台
  • 人工智能基础知识笔记十五:文本分块(Chunk)
  • 芯伯乐XBL6019 60V/5A DC-DC升压芯片的优质选择
  • 新手向:Python实现图片转ASCII艺术
  • Custom SRP - Directional Shadows
  • 【0基础3ds Max】主工具栏介绍(上)
  • 论文reading学习记录7 - daily - ViP3D
  • 3D TOF 视觉相机:工业视觉的破局者,重塑视觉感知的未来
  • Cesium 模型3dtiles压平,任意多面压平,无闪烁
  • ETL流程详解:从概念到实战案例一次讲透
  • 宝龙地产债务化解解决方案二:基于资产代币化与轻资产转型的战略重构
  • OpenAI 最新开源模型 gpt-oss (Windows + Ollama/ubuntu)本地部署详细教程
  • 适用于在线3D测量和检测的3D激光轮廓仪
  • DMETL简单介绍、安装部署和入门尝试
  • MySQL definer does not exist 问题分析
  • 【Qt开发】常用控件(二) -> enabled
  • Prometheus监控平台部署
  • java分布式定时任务
  • 使用 Setup Project 打包