2016-07-27 3 views
-1

QOpenGLBuffer를 픽셀 버퍼 객체로 사용하려고합니다. 목표는 고해상도 비디오 스트림 (4K, 60FPS)을 표시하여 성능이 좋도록하는 것입니다.Qt OpenGL에서 PBO를 사용하는 방법

저는 OpenGL부터 ​​시작 했으므로 가능한 한 가장 좋은 방법으로 간단한 2D 텍스처를 표시하려고합니다. VBO와 VAO를 이미 포함 시켰습니다. 다음 단계는 더 나은 성능을 위해 PBO를 사용하는 것입니다.

QOpenGLBuffer가 아닌 PBO의 경우 glGenBufferARB()가있는 튜토리얼이 있습니다. 그러니까 기본적으로,이 코드에 PBO를 사용하도록 할 것입니다 방법

glwidget.h

#ifndef GLWIDGET_H 
#define GLWIDGET_H 

#include <QOpenGLWidget> 
#include <QOpenGLFunctions> 
#include <QOpenGLBuffer> 
#include <QDebug> 
#include <QOpenGLTexture> 
#include <QOpenGLShader> 
#include <QOpenGLShaderProgram> 
#include <QOpenGLVertexArrayObject> 




class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions 
{ 
    Q_OBJECT 
public: 
    explicit GLWidget(QWidget *parent = 0); 

    ~GLWidget(); 

    void initializeGL(); 
    void paintGL(); 
    void resizeGL(int w, int h); 
    void LoadGLTextures(); 

private : 

    QOpenGLShaderProgram *program; 
    QOpenGLBuffer vbo; 
    QOpenGLVertexArrayObject vao; 
    GLuint tex; 

    GLint vertexLocation; 
    GLint texcoordLocation; 

    int tailleVerticesBytes; 
    int tailleCoordTexturesBytes; 

    float vertices[8]; 
    float coordTexture[8]; 


public slots: 



private slots: 



}; 

#endif // GLWIDGET_H 

glwidget.cpp

#ifndef BUFFER_OFFSET 
#define BUFFER_OFFSET(offset) ((char*)NULL + (offset)) 

#include "glwidget.h" 
#include <QElapsedTimer> 


GLWidget::GLWidget(QWidget *parent) : 
     QOpenGLWidget(parent) 
{ 
    tailleVerticesBytes = 8*sizeof(float); 
    tailleCoordTexturesBytes = 8*sizeof(float); 
} 

GLWidget::~GLWidget(){ 

    vao.destroy(); 
    vbo.destroy(); 
    delete program; 
    glDeleteTextures(1, &tex); 

} 

void GLWidget::LoadGLTextures(){ 

    QImage img; 

    if(!img.load("C:\\Users\\Adrien\\Desktop\\open3.bmp")){ 
     qDebug()<<"Image loading failed"; 
    } 

    QImage t = (img.convertToFormat(QImage::Format_RGBA8888)).mirrored(); 

    glGenTextures(1, &tex); 

    glBindTexture(GL_TEXTURE_2D, tex); 

     glTexImage2D(GL_TEXTURE_2D, 0, 3, t.width(), t.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, t.bits()); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 

    glBindTexture(GL_TEXTURE_2D, 0); 



} 

void GLWidget::initializeGL(){ 


    float verticesTmp[] = {-1.0,-1.0, 1.0,-1.0, 1.0,1.0, -1.0,1.0}; 
    float coordTextureTmp[] = {0.0,0.0, 1.0,0.0, 1.0,1.0, 0.0,1.0}; 

    for(int i = 0; i<8; i++){ 
     vertices[i] = verticesTmp[i]; 
     coordTexture[i] = coordTextureTmp[i]; 
    } 

    initializeOpenGLFunctions(); 
    glClear(GL_COLOR_BUFFER_BIT); 
    glEnable(GL_TEXTURE_2D); 
    LoadGLTextures(); 

    //Shader setup 
    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this); 
    const char *vsrc = 
     "#version 150 core\n" 
     "in vec2 in_Vertex;\n" 
     "in vec2 vertTexCoord;\n" 
     "out vec2 fragTexCoord;\n" 
     "void main(void)\n" 
     "{\n" 
     " gl_Position = vec4(in_Vertex, 0.0, 1.0);\n" 
     " fragTexCoord = vertTexCoord;\n" 
     "}\n"; 
    vshader->compileSourceCode(vsrc); 

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this); 
    const char *fsrc = 
      "#version 150 core\n" 
      "uniform sampler2D tex;\n" 
      "in vec2 fragTexCoord;\n" 
      "void main(void)\n" 
      "{\n" 
      " gl_FragColor = texture2D(tex,fragTexCoord);\n" 
      "}\n"; 
    fshader->compileSourceCode(fsrc); 

    program = new QOpenGLShaderProgram; 
    program->addShader(vshader); 
    program->addShader(fshader); 
    program->link(); 
    program->bind(); 
    glActiveTexture(GL_TEXTURE0); 
    program->setUniformValue("tex", 0); 

    vertexLocation = glGetAttribLocation(program->programId(), "in_Vertex"); 
    texcoordLocation = glGetAttribLocation(program->programId(), "vertTexCoord"); 

    //VAO setup 
    vao.create(); 
    vao.bind(); 

    //VBO setup 
    vbo.create(); 
    vbo.setUsagePattern(QOpenGLBuffer::StaticDraw); 
    vbo.bind(); 
    vbo.allocate(tailleVerticesBytes + tailleCoordTexturesBytes); 
    vbo.write(0, vertices, tailleVerticesBytes); 
    vbo.write(tailleVerticesBytes, coordTexture, tailleCoordTexturesBytes); 

    program->enableAttributeArray(vertexLocation); 
    program->setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 2); 
    program->enableAttributeArray(texcoordLocation); 
    program->setAttributeBuffer(texcoordLocation, GL_FLOAT, tailleVerticesBytes, 2); 

    vbo.release(); 
    vao.release(); 
    program->release(); 



} 

void GLWidget::paintGL(){ 

    glClear(GL_COLOR_BUFFER_BIT); 

    program->bind(); 
    { 
     vao.bind(); 

      glBindTexture(GL_TEXTURE_2D, tex); 

       glDrawArrays(GL_QUADS, 0, 4); 

      glBindTexture(GL_TEXTURE_2D, 0); 

     vao.release(); 
    } 
    program->release(); 

} 

void GLWidget::resizeGL(int w, int h){ 

    glViewport(0, 0, (GLint)w, (GLint)h); 

} 



#endif 

: 여기

내 코드입니다 ?

먼저 QOpenGLBuffer 객체를 만들고 (QOpenglBuffer :: PixelUnpackBuffer) 형식을 지정하는 것이 좋습니다. 그런 다음 버퍼에 픽셀을 업로드하고 glTexImage2D 대신 사용해야합니다. 이것은 단지 세계적인 아이디어 일 뿐이며 어떻게 해야할지 잘 모릅니다.

감사합니다.

답변

0

목표는 고해상도 비디오 스트림 (4K, 60FPS)을 표시하여 성능이 좋도록하는 것입니다.

이렇게하는 유일한 방법은 일부 빠른 프레젠테이션 API (OpenGL과 관련 없음)를 사용하는 것입니다.

OpenGL을 계속 사용하려면 적어도 GPU에 비디오 디코딩을 수행하고 텍스처로 업로드하는 것이 좋습니다. 그렇게하는 방법은 OS와 GPU에 따라 다릅니다. 예를 들어 Linux 및 NVIDIA를 사용하는 경우 가속화 된 디코딩에는 VDPAU을, 디코딩 된 프레임으로 채워진 텍스처에는 NV_VDPAU_interop을 사용할 수 있습니다.

여전히 당신은 픽셀 의 포장을 풀고 버퍼가 이것에 대한 개체 사용하려는 경우 (치골, 당신은 그것이 압축을 풀고입니다> GL =로 업로드 할)에가는 거의 마법이있다. 다음

QOpenGLBuffer *pubo = new QOpenGLBuffer(QOpenGLBuffer::PixelUnpackBuffer); 
pubo->create(); 

당신의 프레임의 데이터로 채울 : 하나 만들기

pubo->bind(); 
pubo->allocate(...); // or, if already allocated, also write/map 

이제 치골의 효과는 하나가 바인딩 된 경우, 특정 전화 사용자의 데이터하지를 읽을 수있는 의미를 변경됩니다,이다 기억하지만 PUBO에서. 특히 텍스쳐 데이터를 업로드하는 호출.당신이 (을하고 불변 저장, glTexImage에없는 수동 호출을 사용 QOpenGLTexture를 사용한다) 텍스쳐 주위에있는 경우 그래서, 당신은 할 수 있습니다 :

pubo->bind(); 
glBindTexture(GL_TEXTURE_2D, textureId); 
glTexImage2D(GL_TEXTURE_2D, 
      level, 
      internalFormat, 
      width, 
      heigth, 
      border, 
      externalFormat, 
      type, 
      data); 

바운드 치골, 맨 마지막 인수 거기 때문에 (data)는 의미를 변경합니다. 더 이상 클라이언트 메모리에 대한 포인터가 아니지만, 현재 바인딩 된 픽셀 압축 해제 버퍼 객체에 대한 바이트 오프셋이 입니다. 따라서 텍스처 데이터가 오프셋 0에서 버퍼로 시작되면 0을 전달해야합니다 (실제로는 (const GLvoid *)0). 그렇지 않으면 적절하게 조정해야합니다.

pubo->release(); 

을 다음 쉐이더에서 평소와 같이 텍스처를 사용하고 모든 것이 잘 될 것입니다 :

지금 당신은 치골을 해제 할 수 있습니다.


텍스처를 곧바로 사용하면 성능이 향상되지 않습니다. 이 복잡한 설정의 요점은 이전 프레임에서 업로드 된 데이터를 렌더링하는 동안 GL이 이미지 데이터를 비동기 적으로 전송할 수 있도록하는 것입니다. 이미지 데이터를 즉시 사용하는 경우 GL은 이미지 데이터가 GPU에 업로드 될 때까지 기다릴 수 있도록 전체 파이프 라인을 동기화해야합니다.

따라서이 시나리오의 일반적인 설정에는 라운드 로빈 방식으로 사용되는 많은 PUBO가 있습니다. 예를 들어 핑 소리가 나는 두 개가 있으면 하나의 PBO에 데이터를 업로드하고 이전 PBO를 사용하여 텍스처를 채우고 그립니다. 이것은 GL이 버스를 통해 현재 데이터를 실제로 전송할 수있는 "충분한 시간"을 구매해야하므로 다음 프레임에서 텍스처 업로드 및 그리기를 통해 즉시 데이터를 찾을 수 있습니다.

이상적으로 공유 OpenGL 컨텍스트를 사용하는 다른 스레드의 PUBO에서 데이터 업로드를 수행하고 fences을 사용하여 업로드가 완료되면 텍스처를 채울 수있는 렌더링 스레드에 알릴 수 있습니다. 또한 고아 (orphaning), 고정 된 맵 (pinned maps), 비 동기화 된 쓰기 (unynchronized write) 등을 사용하여 계속 구축 할 수 있습니다.

이 모든 것에 대한 깊이있는 설명은 OpenGL Insights의 28/29 장에서 제공됩니다. 여기서는 완벽하게 재현 할 수 없으며 일부 코드는 here과 함께 제공됩니다.

OpenGL 위키 here에서 버퍼 객체 스트리밍에 대한 추가 정보를 찾을 수 있습니다.

관련 문제