ARM Midgard GPU

|

最近突然对arm mali gpu的硬件结构蛮有兴趣,所以找了篇arm上的文章看了下。

看得就是这篇arm的文章,我来尝试简单复述一下这个文章里的内容。

The CPU-GPU rendering pipeline

API看起来是同步的,实则硬件大多是异步执行,包括gldraw和swapbuffer。 博客里解释这是因为性能的关系。

在mali上vertex和fragment是分开的模块,所以这两个也可以并行。

Throttling

就是说如何控制GPU的负载,因为在异步下,负载大意味着延时大。

“This throttling mechanism is normally provided by the host windowing system, rather than by the graphics driver itself.” 所以这是由操作系统决定的。

Tile-based rendering

传统的rendering方式: “In a traditional mains-powered desktop GPU architecture — commonly called an immediate mode architecture — the fragment shaders are executed on each primitive, in each draw call, in sequence. Each primitive is rendered to completion before starting the next one”

这样的缺点就是每次fragment shaded操作都要占用ddr带宽,

mali的Tile-based rendering方式: “ It first executes all of the geometry processing, and then executes all of the fragment processing. During the geometry processing stage, Mali GPUs break up the screen into small 16x16 pixel tiles and construct a list of which rendering primitives are present in each tile. When the GPU fragment shading step runs, each shader core processes one 16x16 pixel tile at a time, rendering it to completion before starting the next one.” 如何在如此小的一块tile缓存中渲染出高分辨率的图像?解决方案就是将OPENGL的帧缓存切割成16x16的小块(这就是tile-based渲染的命名由来),然后一次就渲染一块。对于每一块tile: 将有用的几何体提交进去,当渲染完成时,将tile的数据拷贝回主内存。这样,带宽的消耗就只来自于写回主内存了,那是一个较小的消耗:没有d/s,没有重绘的像素,没有多采样缓存。同时,消耗极高的深度/模板测试和颜色混合完全的在计算芯片上就完成了。

优点:It is clear from the list above that tile-based rendering carries a number of advantages, in particular giving very significant reductions in the bandwidth and power associated with framebuffer data, as well as being able to provide low-cost anti-aliasing

缺点:需要获取framebuffer结果的时候,会更慢。所以最好glBindFramebuffer只用一次。 另外这种渲染方式还会带来延迟。因为要整个场景的primitive都收到后才能开始Raster

取出framedata: Most content has a depth and stencil buffer, but doesn’t need to keep their contents once the frame rendering has finished. If developers tell the Mali drivers that depth and stencil buffers do not need to be preserved2 — ideally via a call to glDiscardFramebufferEXT (OpenGL ES 2.0) or glInvalidateFramebuffer (OpenGL ES 3.0), although it can be inferred by the drivers in some cases — then the depth and stencil content of tile is never written back to main memory at all. Another big bandwidth and power saving!

其他阅读

https://www.zhihu.com/question/49141824?from=profile_question_card https://www.cnblogs.com/gameknife/p/3515714.html

Shader core architecture

“unified shader core architecture, meaning that only a single class of shader core which is capable of executing all types of shader programs and compute kernels exists in the design”

“The graphics work for the GPU is queued in a pair of queues, one for vertex/tiling workloads and one for fragment workloads,so vertex processing and fragment processing for different render targets can be running in parallel.”
“The workload for a single render target is broken into smaller pieces and distributed across all of the shader cores in the GPU, or in the case of tiling workloads (see the second blog in this series for an overview of tiling) a fixed function tiling unit”
“The shader cores in the system share a level 2 cache to improve performance, and to reduce memory bandwidth caused by repeated data fetches”

“Unlike a traditional CPU architecture, where you will typically only have a single thread of execution at a time on a single core, the tripipe is a massively multi-threaded processing engine”

OpenCL

“Both of these types of work behave almost identically to vertex threads - you can view running a vertex shader over an array of vertices as a 1-dimensional compute problem.”

The Bifrost Shader Core

新的the Bifrost family GPU的shader core, 区别于midgard的shader core,没什么好看的。。。

文章链接

https://community.arm.com/graphics/b/blog/
https://community.arm.com/graphics/b/blog/posts/the-mali-gpu-an-abstract-machine-part-1—frame-pipelining
https://community.arm.com/graphics/b/blog/posts/the-mali-gpu-an-abstract-machine-part-3—the-midgard-shader-core
https://community.arm.com/graphics/b/blog/posts/the-mali-gpu-an-abstract-machine-part-2—tile-based-rendering
https://community.arm.com/graphics/b/blog/posts/the-mali-gpu-an-abstract-machine-part-4—the-bifrost-shader-core

Perf

|

最近有一个客户报过来一个问题,说他们的Xserver变卡了。 通过回溯法,定位是到的commit是enable CLOCK_MONOTONIC

为了搞清楚原因,就得把perf请出来了。Perf record是以采样为原理的,他不能统计cpu的使用率, 但我们可以用他来推测一个程序运行时候的函数时间分布,从而推导出这个程序是不是在高效率的执行。

环境

perf是和你当前内核绑定的,因此需要从内核源码里编译。

交叉编译建议是准备一个chroot的arm环境,可以参考我写在rockchip wiki的cross compile。 进入内核下tools/perf目录编译,完后把perf拷进板子就可以了。

编完后把perf文件拷到板子下

sudo apt-get install gcc build-essential flex zlib1g-dev bison libunwind-dev libbfd-dev libdw-dev libelf-dev
make

记得还要装一下你要debug程序的debug符号。

dpkg -i xserver-xorg-core-dbgsym_1.19.3-1_armhf.deb
sudo apt-get install libc6-dbg libgcc1-dbg

实验

在做实验前,先做下面的命令,把cpu频率和gpu频率定频住,保证出来的图不被干扰

echo performance > /sys/devices/platform/ffa30000.gpu/devfreq/ffa30000.gpu/governor
echo performance > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor

用下面命令测试下perf是否正常工作,以及函数符号是不是能看见

./perf record  -F 99 --call-graph dwarf -a -p $(pidof Xorg) -- sleep 60 
./perf report

实验的设计步骤如下:

  • 进入桌面, 打开terminal和chromium,使用默认size
  • 快速拖动chromium,同时输入./perf record –call-graph dwarf -a -p $(pidof Xorg) – sleep 60

获取火焰图

git clone https://github.com/brendangregg/FlameGraph.git
./perf script | ./FlameGraph/stackcollapse-perf.pl > out.perf-folded
./FlameGraph/flamegraph.pl out.perf-folded > perf-kernel.svg

总结

结果如下, 右键用新tab打开可以zoom:

两次完全没有区别嘛!被耍了,根本就不是这个的影响。

后续其实是一些乱七八糟的编译问题, 确实CLOCK_MONOTONIC对性能有很大影响,因为这个调用频率很高,换CLOCK_MONOTONIC_COARSE后有好转

做为对比,这是待机状态下的:

这还只是其中一个小用法,perf太tm强了。(ง •̀_•́)ง

其实这几个图都不好,xorg里面的stack trace都不完整,其实这是因为程序编译的时候选项fno-omit-frame-pointer没开(用的debian package,设置一下“DEB_CPPFLAGS_MAINT_APPEND”就好了),只能打印当前的指令是在哪个函数内,但不能打印整个调用函数栈。重做出的图如这个,是不是效果要好多了:

See Also

http://www.brendangregg.com/perf.html
http://blog.csdn.net/21cnbao/article/details/78527777?from=singlemessage
http://www.brendangregg.com/flamegraphs.html

顺便研究了下debug symbol的东西,放这里记录下:
http://blog.chinaunix.net/uid-13746440-id-5578048.html

差分: https://linux.cn/article-4670-1.html

直接在图片右上角搜索可以搜函数,不过默认图片太小了,也很难用。。。

Graphics User Guide(Rockchip Linux)

|

rockchip linux平台的graphic,和以往大家所习惯所不同的是,我们应该是最先全面应用上drmdmabuf的arm linux平台。优势是,通用的架构,在上面客制化会很容易,可以利用很多现有组件,现在有很多基础开源项目的开发,都开始基于rockchip平台来作为arm端的适配平台。但缺点是,确实大家都不是很理解这些东西,实际应用起来需要一个学习过程。

wiki资料点这里

hardware

vop

vop是display单元,用来显示图像 (比如输入nv12,rgb的buffer,显示到屏幕。输入1080p的图像,显示4k大小)。

rk3288有两个vop,vop有3个图层,前两个支持到4k分辨率,后一个鼠标层只有64×64。
rk3399同rk3288。
rk3036只有一个vop,2个图层,一个支持1080p,一个720p。

API

  • libdrm

gpu

gpu提供opengles api,适合做3d图形和2d图形的加速。

API

  • opengles
  • egl
  • opencl

software

先看这个结构图,有什么不理解可以留言。

重点理解libdrm,wayland和x11(compositor),mesa和libmali,qt和gtk(applcation)的关系。这张图理解了,写graphic就很轻松,不理解,就会一头雾水。

libdrm

libdrm是drm下沟通驱动和用户层的库。过去app可能是直接使用open(fb)这样的方式来和图形驱动沟通,但是在现在的硬件演化下,已经不合适了。比如硬件

  • 有多个图层怎么办?
  • 有多个屏幕怎么办?
  • 怎么处理vsync的问题,怎么同步不撕裂?
  • 怎么利用上dmabuf,做到memory zero-copy?

libdrm的存在就是用来方便用户层和驱动这些问题,提供API给X11, Wayland这样的display backend使用。如果你的程序比较简单,比如一个广告机循环播放视频,那是可以直接调用libdrm,但是不建议直接使用libdrm的api。原因很多:

  • 首先,你肯定不熟悉libdrm,因为libdrm本身是一个比较新的api,而且接触者局限在相关的drm driver developer和wayland/xserver developer这些人身上。
  • libdrm的演进比较快,比如说api还分atomic和legacy,你可能看到的资料和你现在用的完全不一样,只有熟悉演进记录的人才行。
  • 很多效果依赖于厂商的实现,甚至rockchip还有修改很多core API的表现效果来辅助我们内部产品项目,所以同个API在不同平台表现有可能完全不同的

当然,这里不是让你用x11和wayland,而是用我们封装好的东西。 比如,广告机循环播放视频,那最好是用gstreamer,然后选kmssink显示,而不是直接调用libdrm的api。

简单的理解

drm里有crtc,plane,connector这三个东西,可以理解

  • connector就是屏幕,比如一个hdmi一个connector num, 一个dsi一个connector num
  • crtc表示vop, 一个屏幕一般对应一个crtc
  • plane就是图层,比如视频层在plane2,UI在plane1, 视频在UI上面

drm里api分两套,legacy和atomic。

legacy看名字就是早期的api,我们现在大部分程序也都是用的legacy api。 里面有几个关键的funciton要注意下,drmModeSetCrtc包括了drmModeSetPlane包括了drmModePageFlip

drmModeSetCrtc一般是用来设置UI层,同时设置分辨率用,这里设下的size最后我改到屏幕的分辨率。在rockchip的bsp kernel上drmModeSetCrtc给出的buffer size不需要等于我们要设置的分辨率,这是为了实现在4k的屏幕上使用1080p的UI而做的,不过要注意,这是rockchip自己瞎改的。

drmModeSetPlane用来设置不同图层的显示,比如视频。参数上分别是要显示buffer fd,要操作的图层,要显示的大小,buffer的大小。他会缩放buffer显示到屏幕上。在rockchip平台上这个api是async的,两续调用两次,前面的就被覆盖了,可能需要drmwaitvblank一下,不过要注意,一般内核不会这样的。

为什么rockchip要改这么多地方呢。因为我们想在legacy的api上也完成多图层的显示。想下,我现在用两个图层,图层1和图层2,我现在图层1调用一次drmModeSetPlane,图层2调用一次drmModeSetPlane,然后他们都等一个vsync单位的时间,那假设屏幕刷新率60hz,我们的最大帧数是不是只有30fps了? 为了解决这个问题,上游的人又开发了atomic的api。

atomic api的实质可以理解为一个提交包括了所有的图层的更新信息。这样就不用调用两次drmModeSetPlane了,而是一次的drmModeAtomicCommit,跟上所有的参数。 atomic还有一个很有用的地方在他可以设置plane的zpos,这样就可以自由交换overlay plane和primary plane的显示层级了。之前我们在legacy api(kmssink)的时候都在kernel里hard code层级,但用atomic(mpv)的时候就不需要这样做,设置zpos后,osd(primary layer)也能在video(overlay layer)上面显示了。

资料

leagacy的实例

atomic的实例

当然最好还是看libdrm的文档和test程序。 如果你是自己写小程序,可以把mpp+libdrm那demo里的rkdrm下的文件抽出来自己用,还是挺方便的。如果只是写给rockchip平台用,就legacy api,如果还有多个平台的需求,就研究下atomic了。

libmali

libmali单独拉出来讲一下,这个东西就是arm提供的userspace gpu驱动。

前面说了,gpu是提供opengles,egl,opencl api的,所以你要这几个工作,就需要把libmali加进rootfs里。

默认的binary我们都会传github上: https://github.com/rockchip-linux/libmali

命名规则: gpu型号-软件版本-硬件版本(如果有的话,比如说r1p0区分3288和3288w)-编译选项。

要注意编译选项

  • 不带后缀。是x11-gbm,注意gbm是配置drm使用的memory机制,如果不是3.10的kernel,不要用fbdev的。
  • gbm。 是给qteglfs这样的程序用的,不依赖x11,wayland。
  • wayland。 wayland-gbm, 给wayaland使用

需要替换的系统链接: x11 or none: https://github.com/rockchip-linux/libmali/blob/rockchip/debian/libmali-rk-midgard-t76x-r14p0-r0p0.links wayland: https://github.com/rockchip-linux/libmali/blob/rockchip/debian/libmali-rk-midgard-t76x-r14p0-r0p0-wayland.links

zero-copy

用mali显示dmabuf的数据,比如说摄像头,视频,其实是可以用dmabuf zero-copy机制优化的。 不然载入texture还要cpu去拷贝。

参考:

x11

就和一般桌面平台差不多,不过要x11要有个gpu性能缺陷的问题。

https://en.wikipedia.org/wiki/X.Org_Server http://www.comptechdoc.org/os/linux/howlinuxworks/linux_hlxwindows.html https://dri.freedesktop.org/wiki/DDX/ https://www.freedesktop.org/wiki/Software/Glamor/

wayland

建议使用yocto sdk做wayland的开发。 不然不回答怎么编译,怎么运行的问题。

效率上wayland要比x11好点,主要是兼容性问题。如果不需要桌面,又要多窗口,可以wayland试试看。

zero-copy

wayland也有zero-copy的配置,可以搜下相关的wayland dmabuf。

资料

https://en.wikipedia.org/wiki/Wayland

none

不使用x11和wayland就是none啦,这也是我们嵌入式上接触比较多的。 什么minigui啊,sdl啦都是这样的。

不过要支持到drm和opengl这种的,就只有qt了。

qteglfs

QT EGLFS是qt自己实现的一个gui系统,不支持多窗口,但也因此少了window compoiste。
QT EGLFS和dri2的方式也差不多,区别就在于,qt eglfs的font buffer在自己用gpu compoiste后,是直接送给drm去显示,而X里是送Window manager去做compoiste,所以EGLFS在效率上是有优势的。

FAQ

输出4k,但是UI只想1080p

目前只支持qteglfs实现这种需求。 http://blog.iotwrt.com/tips/2017/06/04/swap-layer/

推荐下怎么选display

At present, the recommended choices for fullscreen window are :

  • qt with eglfs plugin
  • EGL program + x11
  • wayland
  • x11

The recommended choices for multi window are :

  • wayland
  • x11

The recommended choices for desktop are :

  • x11

The recommended choices for 4K playback + fullscreen window are :

  • qt with eglfs plugin
  • x11

The recommended choices for 4K playback + multi window are :

  • x11

Gstreamer学习

|

接触Gstreamer有一段时间了,但一直没有系统的了解过Gstreamer的结构,只是停留在”打补丁“这样的一种开发思维上。 虽然不是很喜欢gobject的风格,但毕竟开发不能总是没有头绪,还是需要系统的了解一下。

gobject

gstreamer是基于gobject开发的一个库,所以学习gstreamer前,需要先了解下gobject相关的基础。

中文 : https://www.ibm.com/developerworks/cn/linux/l-gobject/

一开始,可以先简单的看看这篇中文文章,对gobject有一个基础的认识。

英文 : https://developer.gnome.org/gobject/stable/

在后面的学习过程中,如果有遇到不理解,可以再回来check官方的manual

What is Gstreamer

https://gstreamer.freedesktop.org/documentation/application-development/introduction/index.html

这里的链接介绍了什么是gstreamer,他的结构是什么样。一般大家印象里,一个提供视频编解码的库,应该是ffmpeg那样,而gstreamer有点反这种印象。其实gstreamer不能说是一个库,而应该算是一种嵌入小程序。

关于gstreamer对使用者最主要的用处,见这段话:

The application programmer who wants to build a media pipeline. The programmer can use an extensive set of powerful tools to create media pipelines without writing a single line of code. Performing complex media manipulations becomes very easy.

Foundations里解释了一些在使用gstreamer的时候会遇到的名词,比如什么是elements,什么是pads。

Application

https://gstreamer.freedesktop.org/documentation/application-development/basics/index.html

过一遍即可,不过如果还没用过gstreamer的话,最好先用gst-launch多输几条命令玩玩先。 这里主要是进一步了解什么是elements, bins, pads, buffer以及你该如何在应用中使用。

Player

如果你是要做一个player,同时又是要直接使用gstreamer的话,看这里:
https://gstreamer.freedesktop.org/documentation/application-development/highlevel/index.html

使用playbin和decodebin来简化程序。

Advance

https://gstreamer.freedesktop.org/documentation/application-development/advanced/index.html

Plugin

https://gstreamer.freedesktop.org/documentation/plugin-development/index.html

Memory

https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/allocation.html 关于memory的部分可以多看看,有助于编写程序。

Others

如果你的App不满足于media pipeline,你不会也不想写gstreamer plugin,那可以看看博客里的另一篇gstreamer+opencv,可以了解下如果使用appsink和appsrc,来达到类似ffmpeg输入输出的效果。

tips

看playbin选了什么plugin

export GST_DEBUG=decodebin:6

会报出选中的plugin。

Camera(Rockchip Linux)

|

rockchip-isp1

先看这里,了解下驱动和硬件。 http://opensource.rock-chips.com/wiki_Rockchip-isp1

Linux使用

这套驱动把很多原来被kernel包装住的细节暴露给了用户层,这样一来,应用层怎么使用就是个问题。

我们最先开发的是Android CamHal3。对CamHal3来说,这种按照规范暴露出的细节其实很方便开发者(看看我们CamHal3到处都是intel的header就知道了…)。 对使用者来说,使用方式也不会有差异,细节都在xml里被隐藏了。

然后回到Linux,用的是rkcamsrc(类似的nvidia有nvcamerasrc,intel有xcamsrc)。
一开始我是打算写成类似CamHal的做法—-就是使用者不用关心硬件细节,不用操作media controller,不用手动设置各个模块的format—-只需要通过下request,确定拍照还是录像还是预览,分辨率要多大—但后面发现这样的话,如果要做的完整,不损失功能,需要的工作量太大了。另外有很多奇奇怪怪的sensor,比如说HDMI IN,这么写的话,要怎么满足他的需求? 抽象这些太麻烦了。所以在Linux使用的话,还是得User自己去根据实际硬件和基础软件,操作media controller,设置pad format,配置sensor,来满足自己想要的需求。

怎么把3A用起来?

首先最好是用gstreamer,按照readme,设置3a-mode和xml path既可。tuning xml需要找rockchip获取。

但是如果你想把3A加到普通的v4l2程序里,也可以。因为我在写给rkcamsrc加3a支持的时候,用了trick的办法,让这部分的代码和rkcamsrc独立出来。 但没有例程,需要自行理解。
https://github.com/rockchip-linux/gstreamer-rockchip-extra/tree/master/gst/rkv4l2/rkcamsrc/rkisp1

拍照程序怎么写

拍照的话理论上取一个buffer就行,但实际上还有等3a收敛,不然第一个buffer的图像质量比较差。所以一般都是一边预览,跑3a,然后再拍照的。 这里要注意拍照的话需要获取3a结果,来确定是不是效果已经收敛。

拍照的分辨率:
假设拍照要求1080p,那么sensor设置一个接近的最大的分辨率。以ov5695来说,他没有1080p,最近的是2592x1944。 如果直接sensor 2592x1944输出,mainpath 1920x1080输出,那么比例会不对,因为sensor输出是4:3, 而后者是是16:9。 所以还需要设置crop,来裁剪掉sensor的输出。

预览+拍照怎么做

预览的输出一般由selfpath给出,因为selfpath支持旋转,镜像,但输出分辨率较低。 拍照一般由mainpath给出,因为mainpath的输出分辨率大,而且支持输出raw格式。

其实对于一般Linux的应用来说,不必太计较两者同时工作,可以直接mainpath工作,拍照直接从stream里取一帧数据即可。

但是如果你比较计较速度,还有资源占用(比如显示的分辨率太大,占用了GPU的资源),那么就需要两个stream同时工作。 gstreamer先开一个selfpath的pipeline用做预览,当需要拍照的时候,再create一个mainpath的pipline,输出raw到文件或者jpeg压缩,用完销毁。

双path的pipeline example可以看usr/local/bin下的test_camera-dual测试脚本。

录像怎么做

gstreamer上source接mpph264enc即可。

如果要和预览不同的分辨率(其实没必要),也是要使用到双path。
如果是单path,也要注意3a收敛的问题,前几个buffer可以跳过不要。

复杂的sensor(HDMI-IN)

HDMI-IN的subdev驱动有很多他自己的事件,比如说插入检测,分辨率改变,EDID,AUDIO等等。这种场景,需要另外在应用加代码去获取这些事件,同时做出 必要的反应。

Tips

sensor

调试sensor的技巧:

第一步是在rkisp1_mipi_isr里加中断, 确认mipi phy的信号有出来. mipi phy有信号, 可以验证以下事情: mipi phy/controler可以接收输入, sensor信号有了输出. 反之如果没有, 原因也就是这个两个, 可以用下面的命令来验证.

io -4 -l 0x100 0xff911c00(rk3288)

出来的数据, 第2个部分代表mipi status, 多输入几次, 看看有没有变化, bit 8:11代表data lane0-3的stop state, bit12代表clock lane stop state. 如果有所的stop bit没变, 需要检查sensor是不是正确输出了, 如果只是clock lane, 需要看下sensor的clock lane是不是在s_stream前没在lp11模式.

第二步, 如果mipi_isr有中断了, 但是有sync_error的错误, 表示mipi接收的图像和isp配置的信息不一致, 可以有以下原因: mipi接收的图形大小和isp配置的不一致, mipi接收的信号有问题.
第一个原因,检查下是不是sensor配置了1080p输出, 但是设置的软件size确是720p.
第二个原因,检查下是不是bitrate设置的有问题, lane num设置的有问题, 硬件信号有问题.

预览出现条纹

Horizontal and Vertical Banding Noise Fixed-pattern noise
请检查AVDD,根据实际情况并联电容

about 3A

对没有商业支持的客户来说,用内置的ISP跑3A是用不起来的。因为每一种批次的模组,都需要经过tuning,得到参数才能用,而这个tuning是需要排队的。 所以你只能把他当成一个bypass的mipi-csi host,使用自带isp的sensor模组。

See also

http://www.360doc.com/content/16/1019/17/496343_599664458.shtml

Comments