复习知识

|

在Rockchip这两年,感觉Embedded的东西都被我搞的差不多了,特没意思。。。(逃

所以最近跳槽换了个工作,主要为自动驾驶平台写应用程序,之所以转到这个方向,是因为觉得之前在底层(Kernel和硬件驱动),GNU/Linux系统的相关经验能在这里起到很大帮助。

新的工作是开发类似ROS一类的应用。考虑到自己自从大学毕业后,很久没有系统的写过C++,甚至OOP编程也没怎么写,所以决定在这个离职到入职的一个月里,再复习下上学时候的软工知识。

《深入理解C++11》

右值

所有的值属于左值,将亡值(返回&&引用还有move),纯右值(1,true)。 std::move 强制左值转右值。const会使右值变左值,要谨慎使用。

注意完美转发。

拷贝构造
HasPtrMem(HasPtrMem &p);
移动语义
HasPtrMem(HasPtrMem &&p);

移动构造在临时变量的拷贝时候触发。

others

constexpr

相比const,constexpr可以让赋值在编译时被确定,从而能把该变量保存的ROM里。以前上学的时候用C++写OS的时候,有用到这个,不过写应用软件应该不会接触这个了。

noexcept

noexcept修饰的函数不会抛出异常,可以阻止异常的传播与扩散。

final/override

final可以使派生类不可覆盖他修饰的虚函数。
override则是使派生类必须虚函数,防止基类的虚函数发生了变化。

explicit

可以阻止隐式的类型转换。

initializer_list

这个还可以防止类型收窄

多继承

多继承是指一个子类继承多个父类。多继承对父类的个数没有限制,继承方式可以是公共继承(用public继承时,派生类内部可以访问基类中public和protected成员)、保护继承(类外也不能通过派生类的对象访问基类的成员)和私有继承(类外也不能通过派生类的对象访问基类的成员, public,protected变成private)。

由输出结果可以看出,在多继承中,任何父类的指针都可以指向子类的对象,在实例化子类时,先根据继承的顺序依次调用父类的构造函数,然后再调用该子类自己的构造函数;

虚函数表

《More Effective+C++》

指针与引用的区别

《深入理解linux内核》

和工作无关。。。只是以后Kernel Level的工作估计很少做了,再看看,巩固下知识。

进程

task_struct 进程描述符, thread_info指向task_struct,放在栈的最下面。

parent的pid是其他的tgid,同样一个tgid的算一个进程组。

线程一般是进程允许共享页表,打开文件表,信号处理。

Linux内核在创建进程的时候,是写时复制,只有写新物理页的时候,才会拷贝一个新的。(内核只为新生成的子进程创建虚拟空间结构,它们来复制于父进程的虚拟究竟结构,但是不为这些段分配物理内存,它们共享父进程的物理空间,当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间)

vfork不会复制页表项,因此父进程必须等子退出才行,vfork的程序一般会另外调用exec,创建自己的页表,不需要复制主的。

进程僵死,是因为需要通知父进程,不能马上就删除进程描述符这些资源

中断

软中断和tasklet是中断上下文,但是是中断下半部。tasklet是一种软中断,一般用在IO上。

fault可以修复,trap没有影响,一般只是为了debug,abort会终止进程。中断描述符存放入口。

在中断禁止过程中丢失的中断(比如说do_IRQ, 在do_IRQ被调用时,处理器已经屏蔽了对外部中断的响应)(为了支持中断的嵌套执行,Linux内核在进入中断服务程序之前会将硬中断开启,运行完中断服务程序之后再将硬中断关闭,在这期间硬件中断是可以被抢占的),在irq_enable的时候,会通过内核代码检查一次,手动触发。

linux支持中断嵌套,一个中断可以抢占其他中断和异常,不过异常不能抢占中断,一般来说内核里的异常就缺页,中断处理函数不会触发缺页。

IRQF_ONESHOT保护,只有中断下半部分threaded_handler执行完才行。(这是因为在旧的中断处理机制中,top half是不可重入的,强制线程化之后,强制设定IRQF_ONESHOT可以保证threaded handler是不会重入的)(在那些被强制线程化的中断线程中,disable bottom half的处理。这是因为在旧的中断处理机制中,botton half是不可能抢占top half的执行,强制线程化之后,应该保持这一点)

内核同步

一个tasklet,softirq不会在不同CPU上执行,可以减少同步问题。

每CPU变量都需要禁止抢占才能访问,避免其他CPU访问到了。

内存屏障既用于多处理器系统(MP),也用于单处理器系统(UP),api前面带smp,都是用于多处理器的。
http://www.wowotech.net/kernel_synchronization/memory-barrier.html

spin_lock会禁止抢占,但是只有在lock住后才禁止抢占,如果是在忙等,还是会被抢占的。 rw_lock读写自旋锁。 seqlock顺序锁,类似读写自旋锁,但是允许读的时候写。 这几个锁的实现都是基于原子变量的加减实现的。

RCU是为了保护被多个CPU读的数据而设计的结构,允许多个读者和写者(rwlock只允许一个写),另外rcu不使用ALL CPU的变量,效率高。
读者不需要获得任何锁就可以访问它,但写者在访问它时首先拷贝一个副本,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据,当然也可以直接用rcu_assign_pointer和synchronize_rcu赋值。RCU允许多个读者同时访问被保护的数据,也允许多个读者在有写者时访问被保护的数据。 rcu_read_lock会关抢占,不能在临界区睡眠,因为写者执行完毕后需要调用回调函数,这样就被阻塞住了。
http://www.wowotech.net/kernel_synchronization/rcu_fundamentals.html

注意rcu的指针替代是原子语句,reclaimer不管同步

rcu和spin lock的对比:由此可见,RCU的并发性能要好于rwlock,而且rcu比基于counter(需要访问存储器件)的锁(例如spin lock,rwlock)的机制开销会更小。

定时

clocksource(clocksource不能被编程,没有产生事件的能力,它主要被用于timekeeper来实现对真实时间进行精确的统计):http://www.wowotech.net/timer_subsystem/clocksource.html
clockevent(触发中断的) : http://www.wowotech.net/timer_subsystem/clock-event.html
timekeeper: http://blog.csdn.net/DroidPhone/article/details/7989566
arm-timer(注意armtimer一般依赖一个system counter做源,arm-timer注册是clocksource设备,rk-timer是clockevent设备): http://www.wowotech.net/timer_subsystem/armgeneraltimer.html

jiffies记录系统节拍。

动态定时器通过软件实现,没有限制。

调度

linux调度基于分时,cpu时间被分成slice。

《Head First设计模式》

面向对象的核心特性包括封装、继承和多态
封装从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类
所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。

面向对象编程一定程度上是借鉴现实世界中物体与物体之间的联系,将有特定功能或者说有内在联系的代码抽象成一个类,使代码更容易被理解,更容易被重用。

设计模式的目的:抵御变化,达成复用。

观察者模式

一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。他定义了一对多的关系。

例子:weather和气象站。

工厂模式

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以(不同口味pizza)。 3、屏蔽产品的具体实现,调用者只关心产品的接口(pizza -> new prepare back put -> create)。

缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

抽象工厂:用于一个产品族

策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。

装饰者模式

装饰着模式的含义:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

适配器模式

适配器模式定义:将一个类的接口,转化成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间

状态模式

允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类

其他

命令模式(command和订单),模板方法模式(骨架,比如说sort对应qsort),迭代器与组合模式(指针-》不同的集合类型所造成的遍历),代理模式(通过代理对象访问目标对象,可以增加实现),复合模式

《算法导论》

这个和工作无关,只是为了以后再跳槽的可能要用。

面Embedded的工作的时候,基本上其实也不会涉及算法,最多写个简单的软件过程而已。

但是面一般软开的时候,因为缺少相关经验,因为都会让我做题。。。
因此,我也错过了Nvidia和百度的机会。

面试常见问题

mutex怎么多进程

Posix信号量就可以,创建命名锁,就可以在不同进程里实现。匿名锁的主要问题是怎么找到。 pthread_mutexattr_setpshared可以使pthread的锁共享。

likely性能提升

likely实质是把容易执行的条件放到CPU流水线会预处理的地方,这样如果条件满足,就直接使用预处理的结果,不然处理器会放弃整个流水线。

delete实现原理,删错地址了怎么办

见此图片

面向对象五个基本原则

  • 单一职责原则(SRP): 一个类应该仅有一个引起它变化的原因(最简单,最容易理解却最不容易做到的一个设计原则)
  • 开放封闭原则(OCP): 既开放又封闭,对扩展是开放的,对更改是封闭的!; 扩展即扩展现行的模块,当我们软件的实际应用发生改变时,出现新的需求,就需要我们对模块进行扩展,使其能够满足新的需求!
  • 里氏替换原则(LSP): 子类可以替换父类并且出现在父类能够出现的任何地方!; 这个原则也是在贯彻GOF倡导的面向接口编程!在这个原则中父类应尽可能使用接口或者抽象类来实现!
  • 依赖倒置原则(DIP): 传统的结构化编程中,最上层的模块通常都要依赖下面的子模块来实现,也称为高层依赖低层!让高层模块不要依赖低层模块,所以称之为依赖倒置原则!高层应该依赖的是抽象。
  • 接口隔离原则(ISP): 这个原则的意思是:使用多个专门的接口比使用单个接口要好的多!这是接口隔离原则的核心定义,接口要尽量小,不要出现臃肿的接口,但是小也是有限度的,不能违背单一职责原则。具体到接口隔离原则就是要求在接口中尽量减少公布public方法

无锁化

当今比较流行的 Non-blocking Synchronization 实现方案有三种:

  • Wait-free 是指任意线程的任何操作都可以在有限步之内结束,而不用关心其它线程的执行速度。
  • Lock-Free 是指能够确保执行它的所有线程中至少有一个能够继续往下执行。
  • Obstruction-free 是指在任何时间点,一个孤立运行线程的每一个操作可以在有限步之内结束。只要没有竞争,线程就可以持续运行。一旦共享数据被修改,Obstruction-free 要求中止已经完成的部分操作,并进行回滚。 所有 Lock-Free 的算法都是 Obstruction-free 的。

二叉树中两个节点的最近公共祖先节点

从树的根节点开始和两个节点作比较,如果当前节点的值比两个节点的值都大,则这两个节点的最近公共祖先节点一定在该节点的左子树中,则下一步遍历当前节点的左子树;

如果当前节点的值比两个节点的值都小,则这两个节点的最近公共祖先节点一定在该节点的右子树中,下一步遍历当前节点的右子树;这样直到找到第一个值是两个输入节点之间的值的节点,该节点就是两个节点的最近公共祖先节点。

伙伴算法

Buddy(伙伴的定义): 这里给出伙伴的概念,满足以下三个条件的称为伙伴:
1)两个块大小相同; 2)两个块地址连续; 3)两个块必须是同一个大块中分离出来的;

Buddy算法的分配原理: 假如系统需要4(22)个页面大小的内存块,该算法就到free_area[2]中查找,如果链表中有空闲块,就直接从中摘下并分配出去。如果没有,算法将顺着数组向上查找free_area[3],如果free_area[3]中有空闲块,则将其从链表中摘下,分成等大小的两部分,前四个页面作为一个块插入free_area[2],后4个页面分配出去,free_area[3]中也没有,就再向上查找,如果free_area[4]中有,就将这16(2222)个页面等分成两份,前一半挂如free_area[3]的链表头部,后一半的8个页等分成两等分,前一半挂free_area[2] 的链表中,后一半分配出去。假如free_area[4]也没有,则重复上面的过程,知道到达free_area数组的最后,如果还没有则放弃分配。

Buddy算法的释放原理: 内存的释放是分配的逆过程,也可以看作是伙伴的合并过程。当释放一个块时,先在其对应的链表中考查是否有伙伴存在,如果没有伙伴块,就直接把要释放的块挂入链表头;如果有,则从链表中摘下伙伴,合并成一个大块,然后继续考察合并后的块在更大一级链表中是否有伙伴存在,直到不能合并或者已经合并到了最大的块(222222222个页面)。

可以解决内存碎片问题。

3sum

然后对于3 Sum问题,解决方法就是最外层遍历一遍,等于选出一个数, 之后的数组中转化为找和为target-nums[i]的2SUM问题。 因为此时排序复杂度在3SUM问题中已经不占据主要复杂度了,所以直接排序,然后设置两个指针,一个指向数组开头,一个指向数组结尾,从两边往中间走。直到扫到满足题意的为止或者两个指针相遇为止。

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。

Comments