当前位置: 首页 > 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.lryc.cn/news/613980.html

相关文章:

  • 【性能测试】-2- JMeter工具的使用
  • 网吧在线选座系统|基于java和小程序的网吧在线选座小程序系统设计与实现(源码+数据库+文档)
  • 【Jmeter】设置线程组运行顺序的方法
  • Baumer相机如何通过YoloV8深度学习模型实现危险区域人员的实时检测识别(C#代码UI界面版)
  • 利用千眼狼sCMOS相机开展冷离子云成像与测量实验
  • 平板探测器的主要技术指标
  • Spring Boot 优雅配置InfluxDB3客户端指南:@Configuration + @Bean + yml实战
  • C# 异步编程(GUI程序中的异步操作)
  • 从浅拷贝到深拷贝:C++赋值运算符重载的核心技术
  • 【设计模式】抽象工厂模式 (工具(Kit)模式)
  • 【接口自动化】-2- request模块及通过变量实现接口关联
  • 瑞利杂波背景下不同环境的虚警概率与目标检测概率仿真
  • 项目历程—右键菜单(问题,解决,拓展(非教学向,因为乱))
  • django uwsgi启动报错failed to get the Python codec of the filesystem encoding
  • 17.14 CogVLM-17B多模态模型爆肝部署:4-bit量化+1120px高清输入,A100实战避坑指南
  • 流形折叠与条件机制
  • 【ee类保研面试】其他类---计算机网络
  • STM32HAL 快速入门(二):用 CubeMX 配置点灯程序 —— 从工程生成到 LED 闪烁
  • 如何在Vue中使用拓扑图功能
  • 相机坐标系与世界坐标系的点相互转换:原理、可视化与实践
  • HTML 与 CSS:从 “认识标签” 到 “美化页面” 的入门指南
  • Numpy科学计算与数据分析:Numpy数据分析与图像处理入门
  • 使用Python提取PDF大纲(书签)完整指南
  • Date、Calendar、LocalDateTime:Java 处理时间的类该怎么选?
  • 【网络自动化】利用Python脚本与计划任务,实现H3C/HPE设备配置无人值守备份
  • 安装向量数据库chromadb
  • Java+uniapp+websocket实现实时聊天,并保存聊天记录
  • mac笔记本如何重新设置ssh key
  • React Hooks 完全指南:从概念到内置 Hooks 全解析
  • 五种IO模型与⾮阻塞IO