具体实施方式
为详细说明本发明的技术内容、构造特征、所实现目的及效果,以下结合实施方式并配合附图详予说明。
请参考图1,为本发明实施方式中的图像显示系统的功能模块示意图,该图像显示系统10包括处理单元11、服务端20以及客户端30。该图像显示系统10运行于一个或两个主机装置中,该一个或两个主机装置可以为便携式电子装置,例如便携式计算机(例如膝上型计算机、笔记本计算机或平板计算机),还可以包括可视媒体播放器、个人数字助理等,还可以是个人计算机。在本实施方式中,该服务端20与客户端30设置在同一主机装置中,在其他实施方式中,该服务端20设置在一主机装置中,该客户端30设置在另一主机装置中。
该处理单元11用于提供处理能力以执行操作系统、程序、用户和应用界面、以及图像显示系统10的任何其他功能,一应用程序110由处理单元11在管理程序中或无管理程序的操作系统中执行(操作系统和管理程序在图中未显示)。例如,操作系统包含Windows XP、UNIX、Android等。在本实施方式中,该应用程序为OpenGL ES1.x,在其他实施方式中,该应用程序还可以是OpenGL ES2.x或OpenGL ES3.x。
该服务端20包括API重定向模块21、API记录模块22、打包模块23、压缩模块24、第一GPU(Graphic Processing Unit,图形处理单元)25以及第一显示单元26。在本实施方式中,该API重定向模块21为OpenGL ES1.x API重定向模块,在其他实施方式中,该API重定向模块21还可以是OpenGL ES2.x API重定向模块或OpenGL ES3.x API重定向模块。该API重定向模块21设置于系统封装层,用于根据处理单元11运行应用程序110时发出的第一API重新定向到封装层中对应的第二API,以及根据系统设置判断是否记录该第一API信息。具体地,以glDrawArrays这个API为例,应用程序调用glDrawArrays后,封装层并不是直接调用到GPU驱动的glDrawArrays函数,而是由API重定向模块23调用到封装层中与glDrawArrays对应的glDrawArraysWrap函数。该API重定向模块21在glDrawArraysWrap中判断是否需要记录下对应的API信息参数,如果不需要记录,则直接调用GPU驱动的glDrawArrays函数;如果需要记录,则除了调用GPU驱动的glDrawArrays函数外,还要记录下glDrawArrays的参数。
在本实施方式中,根据OpenGL ES1.x API的功能,将API分成5类,包括:设置状态API、查询状态API、缓冲区数据与纹理数据API、顶点属性数组API以及绘制图元API。其中,设置状态API用于对OpenGL ES1.x的状态变量进行渲染,该状态变量包括颜色、顶点坐标、深度、视点和投影变换、纹理映射、光照和材质等。查询状态API用于对OpenGL ES1.x的当前状态进行查询,以及将当前帧缓冲中的像素数据反馈至GPU以供应用程序使用,其包括glGet*和glIs*。缓冲区数据与纹理数据API的输入参数中包括指向大量数据的指针,其包括glBufferData、glBufferSubData、glCompressedTexImage2D、glCompressedTexSubImage2D、glTexImage2D、以及glTexSubImage2D。顶点属性数组API用于标识顶点属性,顶点属性包括颜色、法线、纹理坐标和顶点坐标,这类API包括glColorPointer、glNormalPointer、glTexCoordPointer和glVertexPointer。绘制图元API包括glDrawArrays和glDrawElements,用于指定将要绘制的图元的类型和参与绘制的顶点,并向GPU发出绘制的命令,GPU根据此时的OpenGL ES1.x的状态进行渲染。
API信息包括API名称、上下文(context)、输入参数、还可以包括API返回值。当该API重定向模块21根据重新定向的API确定进行记录API信息时产生相应的控制信号,该API记录模块22用于响应该控制信号对该第一API信息进行记录,具体包括:该API记录模块22依次记录该第一API信息中的函数名称、上下文以及输入参数,以及判断该第一API信息中是否具有数据指针。当确定该第一API信息中具有数据指针时该API记录模块22复制指针所指向的数据并调用第一GPU25执行该第一API,当确定该API信息中不具有数据指针时直接调用第一GPU25执行该第一API。当第一GPU25执行该第一API完成时,该API记录模块22还判断第一GPU25执行该第一API后是否产生返回值,并当确定产生返回值时该API记录模块22将该返回值记录下来。该API记录模块22还根据该第一API信息判断是否需要进行特殊处理,以及将经过特殊处理或未经过特殊处理的记录的API信息以API数据包的形式存储到缓冲存储区220中。在本实施方式中,该API记录模块22以添加相应的glMsg到缓冲存储区220中的方式进行API的记录。
以在运行应用程序为OpenGL ES1.x的情况下记录API信息为例进行具体说明:该API记录模块22用连续的正整数表示记录的API名称,且每个API与一个数字对应。每个上下文代表一个独立的可视的绘图区域,且存储了一个OpenGL ES1.x实例的所有状态,该API记录模块22用正整数标识上下文,且每个上下文与一个数字对应。该API记录模块22根据OpenGL ES1.x标准中的API原型依次记录API的输入参数,具体地,对于类型为向量形式的API记录向量的所有分量,对于类型为数据指针的输入参数首先记录数据长度,然后记录指针所指向的数据内容。记录数据的长度是便于在接下来的流程中复制数据,统计glMsg的总大小,以及当数据长度为0时可以表示数据指针是空指针或是在缓冲区数据中的偏移量。当调用GPU驱动执行完API后,该API记录模块22记录下具有返回值的API对应的返回值。
本发明根据API信息可以将API划分为三类,包括只需要依次记录下完整信息的API、不需要记录信息的API以及需要特殊处理的API。
在本实施方式中,不需要记录的API包括查询状态的API、glGenBuffers以及glGenTextures。具体地,查询记录的API的作用在于反馈OpenGL ES1.x的当前状态以便执行的应用程序110选择下一步操作,并没有直接参与绘图操作,客户端30只需要显示出与服务端20相同的画面,并不需要去选择应用程序的运行路径。因此,本发明中服务端20不记录查询状态的API。glGenBuffers以及glGenTextures用于生成一个或多个对象(缓冲区对象或纹理对象)名称,但是在服务端20和客户端30执行同样API时生成的对象名称并不一定相同,其取决于GPU驱动对OpenGL ES1.x的实现。OpenGL ES1.x允许不通过预先生成对象名称能够在执行绑定操作(glBindBuffer、glBindTexture)和删除操作(glDeleteBuffers、glDeleteTextures)中使用这个对象名称。如果服务端20的部分对象名称是调用API生成的,另一部分对象名称是直接绑定的,就会造成客户端30管理对象名称的混乱,使得API无法得到正确地执行。因此,本发明的服务端20负责管理全部的对象名称,客户端30不生成对象名称,只是执行与服务端20相同的绑定操作和删除操作,保证服务端20与客户端30的对象名称是一一对应的,服务端20可以确保对象名称的正确使用。因此,服务端20不记录glGenBuffers和glGenTextures这两个API。
在本实施方式中,需要进行特殊处理的API包括顶点属性数组API、绘制图元API、glBufferData以及glBufferSubData。具体地,glColorPointer、glNormalPointer、glTexCoordPointer和glVertexPointer这四个API的输入参数中都有一个指向属性数据的指针。在没有绑定顶点属性缓冲区的情况下,参与绘制的顶点直到glDrawArrays或glDrawElements时才能确定,而在调用顶点属性数组API时无法记录下绘图所需要的顶点属性数据。因此,对于没有绑定顶点属性缓冲区的顶点属性数组API进行的特殊处理包括:在调用未绑定顶点属性缓冲区的顶点属性数组API时,将除了顶点属性数据之外的参数记录在glMsg中;对于glTexCoordPointer函数,在glMsg中额外增加一个参数记录当前绑定的纹理单元;不完整的glMsg被添加到队列中而不是记录缓冲区中。
绘制图元API包括glDrawArrays和glDrawElements,其函数原型如下:glDrawArrays(GLenum mode、GLint first、GLsizei count),glDrawElements(GLenum mode、GLsizei count、GLenum type、const GLvoid*indices),用于设置图元的类型(输入参数mode)、参与绘制的顶点索引的数目(输入参数count)以及索引。其中,glDrawArrays的顶点起始索引是输入参数first,随后的连续count-1个顶点参与绘制,glDrawElements的输入参数indices指向存放顶点索引的数组。由于顶点属性数组API在没有绑定顶点属性缓冲区对象的情况下被“缓存”,执行glDrawArrays和glDrawElements时需要在记录API时的特殊处理包括:从队列中依次取出缓存的顶点属性数组API的glMsg,此时参与绘图的顶点已经确定,在原先的glMsg的基础上将参与绘图的顶点属性数据记录下来,构成一个完整的glMsg,添加到记录缓冲区中;执行glDrawArrays或glDrawElements结束后,清空队列。应用程序110可能一次在一块内存中传入了所有顶点的属性数据,但是每次参与绘制的顶点只是其中的一部分,客户端30只需要参与绘制的那部分顶点的属性数据就可以正确绘图。在本实施方式中,该服务端20只对必须的顶点属性数据进行记录。具体地,glDrawArrays其参与绘制的顶点索引是连续的,只需要将索引从first开始的连续count个顶点的属性数据记录下来,glDrawElements的输入参数indices指向的索引数组中的索引是没有规律的,数组中也可能有重复的索引,因此找出参与绘制的索引的最小和最大值,并将最小索引和最大索引之间的那部分顶点的属性数据记录下来。
glBufferData和glBufferSubData这两个API的函数原型为void glBufferData(GLenum target、GLsizeiptr size、const GLvoid*data、GLenum usage);voidglBufferSubData(GLenum target、GLintptr offset、GLsizeiptr size、const GLvoid*data)。当顶点属性数组API没有绑定顶点属性缓冲区对象时需要在执行glDrawElements时找出参与绘图的顶点索引的最大值和最小值,当应用程序为glDrawElements的索引数组绑定索引缓冲区,由于索引缓冲区中的内容存储在GPU的内存中,此时服务端无法访问到这些索引数据。因此,对glBufferData和glBufferSubData执行的特殊处理包括:在上下文中为每个索引缓冲区对象分配一块相同大小的备份索引缓冲区,该备份索引缓冲区的内容是服务端可以访问到的。在glBufferData/glBufferSubData更新数据到索引缓冲区的同时将数据更新到对应的备份索引缓冲区,使得备份索引缓冲区中的数据与索引缓冲区保持一致。在记录glDrawElements的过程中就可以在备份索引缓冲区中查找索引的最大值和最小值,并且备份索引缓冲区随着索引缓冲区销毁而销毁。
该API记录模块22将记录API信息生成相应的API数据包,并将API数据包存储到缓冲存储区220中。该打包模块23用于将缓冲存储区220中存储的多个API数据包进行组合打包并发送至压缩模块24。在本实施方式中,该打包模块23将多个API数据包组合成一个帧数据包,具体地,该打包模块23将API记录模块22输出的API数据包不断地累加到帧数据包中,在一帧结束后再将帧数据包发送给压缩模块24。进一步地,该打包模块23还判断当前帧数据包的大小是否超过一个阈值,若确定当前帧数据包的大小超过该阈值时则无论这一帧是否结束,都将当前的帧数据包发送至压缩模块24,并将该帧剩余的API数据包再组成一个或多个新的帧数据包。
该压缩模块24用于使用一压缩算法对接收到的数据包进行压缩,以及添加相应的压缩协议信息。在本实施方式中,该压缩模块24以打包模块23输出的一个数据包作为单位进行数据包压缩,上述压缩算法包括通用压缩算法(GeneralCompression)和差量压缩算法(Delta Compression),若当前API数据包和上一个API数据包相似程度较高,该压缩模块24使用差量压缩算法对当前API数据包进行压缩以获得较高压缩比,若当前API数据包和上一个API数据包的相关性较弱,则该压缩模块24使用通用压缩算法对当前API数据包进行压缩。在对API数据包进行压缩后,该压缩模块24还用于在获得的压缩包的头部加入压缩协议信息,其中,该压缩协议信息包括压缩后数据包的校验值、压缩类型、压缩前的数据量以及压缩后的数据量。
该客户端30包括解压缩模块31、解包模块32、API重放模块33、第二GPU34以及第二显示单元35。当服务端20完成API数据包的压缩并将压缩包传送至客户端30时,该客户端30的解压缩模块31用于分析接收到的压缩包的协议信息头以得到原始的帧数据包,具体地,该解压缩模块31通过校验值校验数据以保证数据的正确性,根据压缩后的数据量将数据流以压缩包为单位分割出来,根据压缩前的数据量分配内存给解压后的帧数据包,以及根据压缩类型进行相应地解压,得到原始的帧数据包。
该解包模块32用于获取解压缩模块31处理得到的原始的帧数据包,取出一个API数据包的头部,获得该API的glMsg的数据长度信息,以及将该API的参数信息送至API重放模块33,该解包模块32依照上述处理过程直至处理完所有API数据包。
该API重放模块33按照解包模块32发送的API参数调用第二GPU34执行该API,并由第二显示单元35显示相应的图像信息。在本实施方式中,需要特殊处理的API包括输入参数包含数据指针的API、顶点属性数组API以及glScissor与glViewport。
具体地,服务端20对于输入参数包含数据指针的API并没有记录应用程序设定的数据指针,而是记录了数据的长度和数据的内容。因此,在客户端30重放对应的API时,该API重放模块33将输入参数中的数据指针设置为指向服务端20传送过来的数据内容。对于绑定了顶点属性缓冲区的顶点属性数组API,API重放模块33回放服务端20传送过来的参数。对于未绑定顶点属性缓冲区的顶点属性数组API,服务端20在缓存这些API后记录相应的API信息,该API重放模块33在回放未绑定顶点属性缓冲区的顶点属性数组API之前执行glBindBuffer(GL_ARRAY_BUFFER,0)以解除绑定顶点属性缓冲区,在回放API完成之后执行glBindBuffer(GL_ARRAY_BUFFER,curbuf)以重新绑定此时上下文的顶点属性缓冲区。glTexcoordPointer与纹理单元相关,在服务端20,未绑定定点属性缓冲区的glTexCoordPointer被延后记录,以及增加额外的参数记录当时绑定的纹理单元(记为X)。在客户端30回放未绑定顶点属性缓冲区的glTexCoordPointer之前执行glClientActiveTexture(X)以设置当时的纹理单元,并在回放后执行glClientActiveTexture(Y)以重新绑定此时上下文的纹理单元(Y)。glScissor与glViewport的原型分别为void glScissor(GLint x,GLint y,GLsizei width,GLsizei height)和void glViewport(GLint x,GLint y,GLsizeiwidth,GLsizei height)。由于服务端20与客户端30的屏幕分辨率可能不同,客户端30在重放glScissor与glViewport时要做适应屏幕分辨率的调整,具体包括:设置服务端的分辨率为(Ws,Hs),客户端的分辨率为(Wc,Hc),计算宽比例因子为Fw=Wc/Ws,高比例因子Fh=Hc/Hs。对API中的输入x,y,width,height参数分别做如下处理:
x′=(GLint)(x*Fw+0.5f);
y′=(GLint)(y*Fh+0.5f);
width′=(GLsizei)(width*Fw+0.5f);
height′=(GLsizei)(height*Fh+0.5f);
将调整后的x′,y′,width′,height'作为新的输入参数传给第二GPU34以驱动第二显示单元35进行相应图像的显示。
请参阅图2,为本发明实施方式中的图像显示方法的流程图,该方法包括:
步骤S40,该API重定向模块21根据处理单元11运行应用程序110时发出的第一API重新定向到封装层中对应的第二API,以及根据系统设置判断是否记录该第一API信息。若是,则进入步骤S41,否则,继续执行步骤S40。
其中,该API重定向模块21设置于系统封装层中。
步骤S41,该API记录模块22响应API重定向模块21的调用对该第一API信息进行记录。其中,API信息包括API名称、上下文(context)、输入参数、还可以包括API返回值。
请同时参阅图3,为本发明实施方式中的API记录方法的流程图,该方法包括:
步骤S410,该API记录模块22依次记录该第一API信息中的函数名称、上下文以及输入参数。
以在运行应用程序为OpenGL ES1.x的情况下记录API信息为例进行具体说明:该API记录模块22用连续的正整数表示记录的API名称,且每个API与一个数字对应。每个上下文代表一个独立的可视的绘图区域,且存储了一个OpenGL ES1.x实例的所有状态,该API记录模块22用正整数标识上下文,且每个上下文与一个数字对应。该API记录模块22根据OpenGL ES1.x标准中的API原型依次记录API的输入参数,具体地,对于类型为向量形式的API记录向量的所有分量,对于类型为数据指针的输入参数首先记录数据长度,然后记录指针所指向的数据内容。
步骤S411,该API记录模块22判断该第一API信息中是否具有数据指针,若是,则进入步骤S412,否则,进入步骤S413。
步骤S412,该API记录模块22复制指针所指向的数据。
步骤S413,该第一GPU25获取并执行该第一API以驱动第一显示单元26显示相应的图像。
步骤S414,该API记录模块22判断第一GPU25执行该第一API完成后是否产生返回值,若是,则进入步骤S415,否则,进入步骤S416。
步骤S415,该API记录模块22记录该第一API执行完成后产生的返回值。
步骤S416,该API记录模块22根据该第一API信息判断是否对该第一API进行特殊处理,若是,则进入步骤S417,否则,本流程结束。
步骤S417,该API记录模块22根据第一API信息对该第一API进行特殊处理。然后,本流程结束。
本发明根据API信息可以将API划分为三类,包括只需要依次记录下完整信息的API、不需要记录信息的API以及需要特殊处理的API。在本实施方式中,不需要记录的API包括查询状态的API、glGenBuffers以及glGenTextures,需要进行特殊处理的API包括顶点属性数组API、绘制图元API、glBufferData以及glBufferSubData。
对于没有绑定顶点属性缓冲区的顶点属性数组API进行的特殊处理包括:在调用未绑定顶点属性缓冲区的顶点属性数组API时,将除了顶点属性数据之外的参数记录在glMsg中;对于glTexCoordPointer函数,在glMsg中额外增加一个参数记录当前绑定的纹理单元;不完整的glMsg被添加到队列中而不是记录缓冲区中。
当顶点属性数组API没有绑定顶点属性缓冲区时,执行绘制图元API中的glDrawArrays和glDrawElements需要进行的特殊处理包括:从队列中依次取出缓存的顶点属性数组API的glMsg,此时参与绘图的顶点已经确定,在原先的glMsg的基础上将参与绘图的顶点属性数据记录下来,构成一个完整的glMsg,添加到记录缓冲区中;执行glDrawArrays或glDrawElement结束后,清空队列。
当顶点属性数组API没有绑定顶点属性缓冲区时需要在执行glDrawElements时找出参与绘图的顶点索引的最大值和最小值,当应用程序为glDrawElements的索引数组绑定了索引缓冲区时,对glBufferData和glBufferSubData执行的特殊处理包括:在上下文中为每个索引缓冲区对象分配一块相同大小的备份索引缓冲区;在glBufferData/glBufferSubData更新数据到索引缓冲区的同时将数据更新到对应的备份索引缓冲区,使得备份索引缓冲区中的数据与索引缓冲区保持一致。在记录glDrawElements的过程中在备份索引缓冲区中查找索引的最大值和最小值,并且备份索引缓冲区随着索引缓冲区销毁而销毁。
步骤S42,该API记录模块22将记录API信息生成相应的API数据包,并将API数据包存储到缓冲存储区220中。
在本实施方式中,该API记录模块22以添加相应的glMsg到缓冲存储区220中的方式进行API的记录。
步骤S43,该打包模块23将缓冲存储区220中存储的多个API数据包组合成相应的帧数据包并发送至压缩模块24。
在本实施方式中,该打包模块23还判断当前的帧数据包的大小是否超过一个阈值,若确定当前帧数据包的大小超过该阈值时则无论这一帧是否结束,都将当前的帧数据包发送至压缩模块24,并将该帧剩余的API数据包再组成一个或多个新的帧数据包。
步骤S44,该压缩模块24使用一压缩算法对接收到的帧数据包进行压缩,添加相应的压缩协议信息,以及将压缩包发送至客户端30。
在本实施方式中,该压缩模块24以打包模块23输出的一个数据包作为单位进行数据包压缩,上述压缩算法包括通用压缩算法(General Compression)和差量压缩算法(Delta Compression),若当前API数据包和上一个API数据包相似程度较高,该压缩模块24使用差量压缩算法对当前API数据包进行压缩以获得较高压缩比,若当前API数据包和上一个API数据包的相关性较弱,则该压缩模块24使用通用压缩算法对当前API数据包进行压缩。
在对API数据包进行压缩后,该压缩模块24在获得的压缩包的头部加入压缩协议信息,其中,该压缩协议信息包括压缩后数据包的校验值、压缩类型、压缩前的数据量以及压缩后的数据量。
步骤S45,该解压缩模块31分析接收到的压缩包的协议信息头以得到原始的帧数据包,具体地,该解压缩模块31通过校验值校验数据以保证数据的正确性,根据压缩后的数据量将数据流以压缩包为单位分割出来,根据压缩前的数据量分配内存给解压后的帧数据包,以及根据压缩类型进行相应地解压,得到原始的帧数据包。
步骤S46,该解包模块32用于获取解压缩模块31处理得到的原始的帧数据包,取出一个API数据包的头部以获得该API的glMsg的数据长度信息,以及将该API的参数信息发送至API重放模块33,该解包模块32依照上述处理过程直至处理完所有的API数据包。
步骤S47,该API重放模块33按照解包模块32发送的API参数调用第二GPU34执行该API,以驱动第二显示单元35显示相应的图像信息。其中,该第二显示单元35显示的图像与第一显示单元26显示的图像相同,即客户端30显示的图像与服务端20显示的图像相同。
在本实施方式中,需要特殊处理的API包括输入参数包含数据指针的API、顶点属性数组API以及glScissor与glViewport。
具体地,服务端20对于输入参数包含数据指针的API并没有记录应用程序设定的数据指针,而是记录了数据的长度和数据的内容。因此,在客户端30重放对应的API时,该API重放模块33将输入参数中的数据指针设置为指向服务端20传送过来的数据内容。对于绑定了顶点属性缓冲区的顶点属性数组API,API重放模块33回放服务端20传送过来的参数。对于未绑定顶点属性缓冲区的顶点属性数组API,服务端20在缓存该些API后记录相应的API信息,该API重放模块33在回放未绑定顶点属性缓冲区的顶点属性数组API之前执行glBindBuffer(GL_ARRAY_BUFFER,0)以解除绑定顶点属性缓冲区,在回放API完成之后执行glBindBuffer(GL_ARRAY_BUFFER,curbuf)以重新绑定此时上下文的顶点属性缓冲区。glTexcoordPointer与纹理单元相关,在服务端20,未绑定定点属性缓冲区的glTexCoordPointer被延后记录,以及增加额外的参数记录当时绑定的纹理单元(记为X)。在客户端30回放未绑定顶点属性缓冲区的glTexCoordPointer之前执行glClientActiveTexture(X)以设置当时的纹理单元,并在回放后执行glClientActiveTexture(Y)以重新绑定此时上下文的纹理单元(Y)。glScissor与glViewport的原型分别为void glScissor(GLint x,GLint y,GLsizei width,GLsizei height)和void glViewport(GLint x,GLint y,GLsizeiwidth,GLsizei height)。由于服务端20与客户端30的屏幕分辨率可能不同,客户端30在重放glScissor与glViewport时要做适应屏幕分辨率的调整,具体包括:设置服务端的分辨率为(Ws,Hs),客户端的分辨率为(Wc,Hc),计算宽比例因子为Fw=Wc/Ws,高比例因子Fh=Hc/Hs。对API中的输入x,y,width,height参数分别做如下处理:
x′=(GLint)(x*Fw+0.5f);
y′=(GLint)(y*Fh+0.5f);
width′=(GLsizei)(width*Fw+0.5f);
height′=(GLsizei)(height*Fh+0.5f);
将调整后的x′,y′,width′,height′作为新的输入参数传给第二GPU34以驱动第二显示单元35进行相应图像的显示。
本发明提供的一种图像显示系统和图像显示方法,通过服务端中设置在封装层内的API重定向单元重定向了系统应用的API调用,并控制记录API功能的打开和关闭,并且服务端将记录的API信息经过打包压缩后传送至客户端进行解包、解压缩,以此进行API的重放以在客户端上显示相应的图像,并且与服务端显示的画面相同。从而,解决现有技术中进行记录与重放API受到特定GPU及应用平台的局限而不具有通用性,以及无法支持实时播放需求的技术问题。
以上所述仅为本发明的实施例,并非因此限制本发明的专利范围,凡是利用本发明说明书及附图内容所作的等效结构或等效流程变换,或直接或间接运用在其他相关的技术领域,均同理包括在本发明的专利保护范围内。