2016-09-20 120 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

在發佈我的更新之後,我看到過了幾秒鐘,我改變了它,但我仍然不時陷入困境 –