FPS的意思是每秒屏幕刷新的画面数,每秒的画面数越多显示就越流畅。在游戏画面、视频中,作为评估流畅度的重要指标。通常情况下,每秒低于25个画面,人眼将能感知到不流畅。
为了更好的描述安卓系统的画面显示的过程,步骤如下:
分步描述如下:
1.VBO/VAO(顶点缓冲区对象或顶点数组对象):
VBO/VAO(到底是啥,下回讲解)是cpu提供给GPU的顶点信息,包括了顶点的位置、颜色(只是顶点的颜色,和纹理的颜色无关)、纹理坐标(用于纹理贴图)等顶点信息。
2.VertexShader(顶点着色器):
顶点着色器是处理VBO/VAO提供的顶点信息的程序。 VBO/VAO提供的每个顶点都执行一遍顶点着色器 。Uniforms(一种变量类型)在每个顶点保持一致,Attribute每个顶点都不同(可以理解为输入顶点属性)。 执行一次VertexShader输出一个Varying和gl_positon。
3.PrimitiveAssembly(图元装配):
顶点着色器下一个阶段是图元装配,图元(prmitive)是三角形、直线或者点精灵等几何对象。这个阶段,把顶点着色器输出的顶点组合成图元。
4.rasterization(光栅化):
光栅化是将图元转化为一组二维片段的过程,然后,这些片段由片段着色器处理(片段着色器的输入)。这些二维片段代表着可在屏幕上绘制的像素。 用于从分配给每个图元顶点的顶点着色器输出生成每个片段值的机制称作插值(Interpolation) 。这句不是人话的话解释了一个问题,就是从cpu提供的分散的顶点信息是如何变成屏幕上密集的像素的,图元装配后顶点可以理解成变为图形,光栅化时可以根据图形的形状,插值出那个图形区域的像素(纹理坐标v_texCoord、颜色等信息)。注意,此时的像素并不是屏幕上的像素,是不带有颜色的。接下来的片段着色器完成上色的工作。
(1)pixelOwnershipTest(像素归属测试):
这个用来确定帧缓冲区中位置(x,y)的像素是不是归当前上下文所有。例如,如果一个显示帧缓冲区窗口被另一个窗口所遮蔽,则窗口系统可以确定被遮蔽的像素不属于此opengl的上下文,从而不显示这些像素。
(2)ScissorTest(剪裁测试):
如果该片段位于剪裁区域外,则被抛弃
(3)StencilTest and DepthTest(模板和深度测试):
深度测试比较好理解,若片段着色器返回的深度小于缓冲区中的深度,则舍弃。模板测试没有用过,不清楚具体功能,猜测功能应该和名字一样,模板形状内可通过。
(4)Blending(混合):
将新生成的片段颜色值与保存在帧缓冲区的颜色值组合起来,产生新的RGBA。
最后将所有数据从GPU传送帧缓冲区,屏幕根据缓冲区数据显示像素。这个过程为一个画面。
FPS统计一秒的画面数,所以要获得该数值,就需要在交换缓冲区的时候进行监控。OpenGL进行这个过程的API接口为:eglswapbuffers;代码如下:
需要在OpenGL装载的时候,对该函数进行hook,代码如下:
到这里已经可以获得FPS数值了。
struct timeval lastrecordtime;
int
(
*
orig_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf);
int
my_eglSwapBuffers(EGLDisplay dpy, EGLSurface surf)
{
timeval nowtime;
gettimeofday(&nowtime,NULL);
deltime
=
nowtime.tv_sec
*
1000
+
nowtime.tv_usec
/
1000
-
( lastrecordtime.tv_sec
*
1000
+
lastrecordtime.tv_usec
/
1000
);
swaptimes
+
+
;
if
(deltime >
=
1000
)
{
float
fps
=
swaptimes
*
1000
/
time;
unsigned
int
realfps
=
(
int
)fps;
lastrecordtime
=
nowtime;
swaptimes
=
0
;
}
return
orig_eglSwapBuffers(dpy,surf);
}
struct timeval lastrecordtime;
int
(
*
orig_eglSwapBuffers)(EGLDisplay dpy, EGLSurface surf);
int
my_eglSwapBuffers(EGLDisplay dpy, EGLSurface surf)
{
timeval nowtime;
gettimeofday(&nowtime,NULL);
deltime
=
nowtime.tv_sec
*
1000
+
nowtime.tv_usec
/
1000
-
( lastrecordtime.tv_sec
*
1000
+
lastrecordtime.tv_usec
/
1000
);
swaptimes
+
+
;
if
(deltime >
=
1000
)
{
float
fps
=
swaptimes
*
1000
/
time;
unsigned
int
realfps
=
(
int
)fps;
lastrecordtime
=
nowtime;
swaptimes
=
0
;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)