2016-09-20 117 views
0

我试图让使用PyQt5和PyOpenGL一个小应用程序。一切正常,但即使只有一个球体,渲染也需要很长时间。我尝试了不同的路线来尝试优化应用程序的速度,现在我正在使用带有OpenGLSurface的简单QWindow。PyQt5的OpenGL swapBuffers很慢

我设法弄清楚,它是context.swapBuffers调用需要很长的时间才能完成,大约之间变化。 0.01s(这很好)和0.05s(这是很长的一段时间),当显示一个带有一些阴影和240个顶点的球体时。

现在我的问题如下:这是正常的吗?如果是这样,有没有办法加快这个过程,或者这与pyqt的工作方式有关,因为它是一个围绕库的python包装?基本上:有没有办法让我继续开发这个程序,而不需要学习C++。这是一个相当简单的应用程序,只需要可视化一些原子结构并能够操纵它。

是否有其他GUI工具包从pyopengl OpenGL的工作时,我可能会使用较少的开销?

这是做渲染的定义:

fmt = QSurfaceFormat() 
fmt.setVersion(4, 2) 
fmt.setProfile(QSurfaceFormat.CoreProfile) 
fmt.setSamples(4) 
fmt.setSwapInterval(1) 
QSurfaceFormat.setDefaultFormat(fmt) 

EDIT1:

def renderNow(self): 
    if not self.isExposed(): 
     return 

    self.m_update_pending = False 

    needsInitialize = False 

    if self.m_context is None: 
     self.m_context = QOpenGLContext(self) 
     self.m_context.setFormat(self.requestedFormat()) 
     self.m_context.create() 

     needsInitialize = True 

    self.m_context.makeCurrent(self) 

    if needsInitialize: 
     self.m_gl = self.m_context.versionFunctions() 
     self.m_gl.initializeOpenGLFunctions() 

     self.initialize() 

    self.render() 

    self.m_context.swapBuffers(self) 

    if self.m_animating: 
     self.renderLater() 

我使用OpenGL的情况下直接使用Qt OpenGL的定义,表面格式为: 关于我的代码如何工作的更多说明:

def render(self): 
    t1 = time.time() 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 

    wtvMatrix = self.camera.get_wtv_mat() 
    transformMatrix = matrices.get_projection_matrix(60, self.width()/self.height(), 0.1, 30, matrix=wtvMatrix) 
    transformMatrixLocation = glGetUniformLocation(self.shader,"transformMatrix") 
    glUniformMatrix4fv(transformMatrixLocation,1,GL_FALSE,transformMatrix) 
    eye_pos_loc = glGetUniformLocation(self.shader, "eye_world_pos0") 
    glUniform3f(eye_pos_loc, self.camera.position[0], self.camera.position[1], self.camera.position[2]) 

    glDrawElementsInstanced(GL_TRIANGLES,self.num_vertices,GL_UNSIGNED_INT,None,self.num_objects) 
    print("drawing took:{}".format(time.time()-t1)) 
    self.frame+=1 
    t1=time.time() 
    self.m_context.swapBuffers(self) 
    print('swapping buffers took:{}'.format(time.time()-t1)) 

这是我调用的唯一drawElementsInstanced。着色器设置如下(遗憾的混乱):

VERTEX_SHADER = compileShader("""#version 410 
         layout(location = 0) in vec3 vertex_position; 
         layout(location = 1) in vec3 vertex_colour; 
         layout(location = 2) in vec3 vertex_normal; 
         layout(location = 3) in mat4 model_mat; 
         layout(location = 7) in float mat_specular_intensity; 
         layout(location = 8) in float mat_specular_power; 
         uniform mat4 transformMatrix; 
         uniform vec3 eye_world_pos0; 
         out vec3 normal0; 
         out vec3 colour; 
         out vec3 world_pos; 
         out float specular_intensity; 
         out float specular_power; 
         out vec3 eye_world_pos; 
         void main() { 

         colour = vertex_colour; 
         normal0 = (model_mat*vec4(vertex_normal,0.0)).xyz; 
         world_pos = (model_mat*vec4(vertex_position,1.0)).xyz; 
         eye_world_pos = eye_world_pos0; 
         specular_intensity = mat_specular_intensity; 
         specular_power = mat_specular_power; 
         gl_Position = transformMatrix*model_mat*vec4(vertex_position,1.0); 


         }""", GL_VERTEX_SHADER) 

     FRAGMENT_SHADER = compileShader("""#version 410 
         in vec3 colour; 
         in vec3 normal0; 
         in vec3 world_pos; 
         in float specular_intensity; 
         in float specular_power; 
         in vec3 eye_world_pos; 

         out vec4 frag_colour; 

         struct directional_light { 
          vec3 colour; 
          float amb_intensity; 
          float diff_intensity; 
          vec3 direction; 
         }; 

         uniform directional_light gdirectional_light; 

         void main() { 

         vec4 ambient_colour = vec4(gdirectional_light.colour * gdirectional_light.amb_intensity,1.0f); 
         vec3 light_direction = -gdirectional_light.direction; 
         vec3 normal = normalize(normal0); 

         float diffuse_factor = dot(normal,light_direction); 


         vec4 diffuse_colour = vec4(0,0,0,0); 
         vec4 specular_colour = vec4(0,0,0,0); 

         if (diffuse_factor>0){ 
          diffuse_colour = vec4(gdirectional_light.colour,1.0f) * gdirectional_light.diff_intensity*diffuse_factor; 
          vec3 vertex_to_eye = normalize(eye_world_pos-world_pos); 
          vec3 light_reflect = normalize(reflect(gdirectional_light.direction,normal)); 
          float specular_factor = dot(vertex_to_eye, light_reflect); 
          if(specular_factor>0) { 
           specular_factor = pow(specular_factor,specular_power); 
           specular_colour = vec4(gdirectional_light.colour*specular_intensity*specular_factor,1.0f); 
          } 
         } 


         frag_colour = vec4(colour,1.0)*(ambient_colour+diffuse_colour+specular_colour); 
         }""", GL_FRAGMENT_SHADER) 

现在,当我想旋转的情景,我使用的代码如下(相机更新等作为正常AFAIK完成):

def mouseMoveEvent(self, event): 

    dx = event.x() - self.lastPos.x() 
    dy = event.y() - self.lastPos.y() 
    self.lastPos = event.pos() 
    if event.buttons() & QtCore.Qt.RightButton: 
     self.camera.mouse_update(dx,dy) 
    elif event.buttons()& QtCore.Qt.LeftButton: 
     pass 

    self.renderNow() 

最后的一些信息:在着色器所需的所有顶点信息通过我初始化和更早版本的初始化定义绑定一个给定的VAO,并不包含太多的对象(我只是测试它使用一个二十面体与2个细分来渲染一个球体,同样,我删除了重复的顶点,但是这并没有做任何事情,因为这实际上不应该是我认为的瓶颈)。

要回答一些问题:我做了只为gigglez杂色山雀不同版本的OpenGL的尝试,没有任何变化,尝试没有垂直同步,没有什么变化,试图用不同的样本大小,没有变化。

EDIT2: 可能是一个线索:swapBuffers大约需要0.015s的大部分时间,但是当我开始周围有很多动人,口吃以及跳起来0.05S一些渲染。这是为什么发生?据我所知,每个渲染都必须处理所有的数据呢?

+0

您是否尝试通过将交换间隔设置为'0'来关闭vsync?您是否尝试过单缓冲上下文比较? – thokra

+2

我怀疑'context.swapBuffers' *出现*是瓶颈,因为这是渲染管道必须刷新的地方 - 所有挂起的操作必须在缓冲区交换之前完成。我认为你需要显示你的渲染代码或最好是[mcve](http://stackoverflow.com/help/mcve)。 –

回答

2

通过OpenGL的工作方式,渲染命令你提交发送到GPU和执行异步(坦率地说,即使过程中向他们发送到GPU是异步的)。当您通过调用swapBuffers来请求显示后台缓冲区时,显示驱动程序必须等待后台缓冲区的内容完成渲染(即所有先前发出的命令完成执行),然后才能交换缓冲区。

如果您遇到低帧率,那么您应优化渲染代码,这是您提交给GPU的东西。切换到C++在这里不会帮助你(尽管它会是一个独立的好主意)。

编辑:你说,当你什么都不做时,你的swapBuffers执行0.015秒,这是可疑的约1/60秒。这意味着您的渲染代码足够高效,可以以60 FPS渲染,而且您没有理由对其进行优化。可能发生的情况是,您致电renderNow()mouseMoveEvent会导致场景每秒重绘超过60次,这是多余的。相反,您应该在mouseMoveEvent中拨打renderLater(),并相应地重构您的代码。

注:你后,立即打电话renderNow()swapBuffers两次,一次在render()和一次。

免责声明:我不熟悉PyOpenGL。


swapBuffer还可以异步执行,但即使如此,如果显示驱动程序互换缓冲速度比你可以使你将在swapBuffer电话最终阻止。

+0

我认为,您是否认为上面提供了额外的代码,这是因为着色器需要很长时间来处理?我在一个MBP 15上用“gt750M –

+0

@LouisPonet:显然你发出了比所需要更多的'swapBuffers'。查看我的更新。 – ybungalobill

+0

在发布我的更新之后,我看到过了几秒钟,我改变了它,但我仍然不时陷入困境 –