2016-09-20 2 views
0

저는 PyQt5와 PyOpenGL을 사용하여 작은 응용 프로그램을 만들려고합니다. 모든 것은 잘 작동하지만, 단 하나의 구로 렌더링이 너무 오래 걸립니다. 나는 시도하고 응용 프로그램의 속도를 최적화하는 다른 경로를 시도하고 지금은 OpenGLSurface와 간단한 QWindow를 사용하고 있습니다.PyQt5 OpenGL swapBuffers 매우 느림

나는 그것이 완료하는 데 오랜 시간이 걸리는 context.swapBuffers 호출이라는 것을 알아 냈다. 1 개의 구면에 약간의 음영과 240 개의 정점을 표시 할 때 0.01 초 (괜찮습니다)와 0.05 초 (길다)입니다.

내 질문은 다음과 같습니다. 정상입니까? 그렇다면이 프로세스의 속도를 높이는 방법이 있습니까? 아니면 pyqt가 작동하는 것과 관련이 있습니까? 파이썬이 라이브러리를 감싸고 있기 때문입니다. 기본적으로 : C++을 배울 필요없이이 프로그램을 계속 개발할 수있는 방법이 있습니까? 그것은 단지 원자 구조를 시각화하고 그것을 조작 할 수 있어야하는 아주 간단한 어플리케이션입니다.

pyopengl에서 OpenGL로 작업 할 때 오버 헤드가 적을 수있는 다른 gui 툴킷이 있습니까?

가 렌더링을 수행하는 정의입니다 :

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

EDIT1 : 직접 Qt는 OpenGL을 정의를 사용하지 않고

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을 사용하고를, 표면의 형식이 주어진다 : 내 코드 작동 방식에 대한 몇 가지 설명 :

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() 

최종 정보 : 쉐이더에 필요한 모든 버텍스 정보는 내가 초기화하고 초기화 정의에서 이전에 바인딩 한 바우트를 통해 제공되며 너무 많은 객체를 포함하지 않습니다. (저는 단지 테스트 중이며 다음과 같은 20 면체를 사용합니다. 구체를 렌더링하는 2 개의 구획, 또한 복제 된 버텍스를 제거했지만 실제로는 병목 현상이 없어야하므로 아무 것도하지 않았습니다.)

몇 가지 질문에 대답하려면 : 나는 gigglez, 변경 사항 없음, vsync없이 시도, 아무것도 변경하지 않고 다른 샘플 크기로 시도했지만 변경되지 않은 다른 버전의 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이 의심 ~ 1/60 초입니다 0.015 초에 실행 말한다. 렌더링 코드는 60FPS로 렌더링 할만큼 효율적이므로 아직 최적화 할 이유가 없습니다. 아마도 renderNow()mouseMoveEvent에서 호출하면 초당 60 회 이상 장면이 다시 렌더링되어 중복됩니다. 대신 renderLater()mouseMoveEvent에 넣고 이에 따라 코드를 재구성해야합니다.

참고 : 즉시 후 renderNow() 한 번 render()에 한 번, 두 번 swapBuffers 호출합니다.

면책 조항 : 저는 PyOpenGL에 익숙하지 않습니다. swapBuffer


는 비동기 적으로 실행할 수 있지만, 그렇다하더라도 디스플레이 드라이버 스왑 버퍼 빨리 당신이 결국 swapBuffer 통화 차단 렌더링 할 수있는 것보다 경우.

+0

쉐이더가 처리하는 데 너무 오래 걸리기 때문에 위의 추가 코드를 제공한다고 생각하십니까? 나는 MBP 15 "에 a gt750M을 가지고있다 –

+0

@ 루이스 게시 : 분명히 필요한 것보다 더 많은'swapBuffers'를 발행한다. 내 업데이트보기 – ybungalobill

+0

내 업데이트를 게시 한 후 너무 오래 걸렸다. 다시 변경 했으므로, 그래도 때때로 수렁에 빠지네. –