2016-07-27 84 views
-1

我想使用QOpenGLBuffer作爲像素緩衝區對象。目標是顯示高分辨率視頻流(4K,60FPS),所以我需要良好的性能。如何使用PBO與Qt OpenGL

由於我剛剛開始使用OpenGL,我首先嚐試以最佳方式顯示簡單的2D紋理。我已經包含了VBO和VAO,下一步(當我閱讀它時)將使用PBO來獲得更好的性能。

有PBO的教程,但與glGenBufferARB(),而不是與QOpenGLBuffer。

這裏是我的代碼:

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 

所以基本上,我會怎麼做在此代碼使用PBO ?

首先要做的是創建一個QOpenGLBuffer對象,同時指定類型(QOpenglBuffer :: PixelUnpackBuffer),那麼我想我需要上傳緩衝區中的像素,並最終使用它來代替glTexImage2D?這只是全球性的想法,我不知道該怎麼做。

謝謝。

回答

0

目標是顯示高分辨率視頻流(4K,60FPS),所以我需要良好的性能。

唯一正確的方法是使用一些加速的演示API(與OpenGL無關)。

如果你想堅持使用OpenGL,你至少希望讓GPU進行視頻解碼並上傳到紋理中。怎麼做取決於你的操作系統和GPU。例如,在Linux和使用NVIDIA的情況下,您可以使用VDPAU進行加速解碼,使用NV_VDPAU_interop進行填充解碼幀的紋理。

如果仍然想要使用像素解壓縮緩衝區對象爲此(PUBO;您正在上傳到GL =>這是一個解壓縮),但是有很少的魔法正在進行。創建一個:

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

然後用幀的填充數據:

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

現在普博的效果是,如果一個人的約束,某些呼叫將改變語義來讀取用戶沒有數據記憶,但來自PUBO。值得注意的是,上傳紋理數據的調用。所以,如果你有你的紋理左右(,你應該使用QOpenGLTexture,它採用一成不變的存儲,而不是手動調用glTexImage),你可以這樣做:

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。例如,如果您有兩個(在ping-poing中),則可以在一個PBO中上傳數據,並使用前一個填充和繪製紋理。這應該爲GL購買「足夠的時間」,以便實際上通過總線傳輸當前數據,因此在下一幀時,貼圖上傳和繪圖將立即找到數據。

理想情況下,您還可以使用共享的OpenGL上下文從另一個線程執行PUBO中的數據上傳,並在上傳完成時使用fences來指示渲染線程,以便可以填充紋理。而且你可以通過使用孤立,固定地圖,非同步寫入甚至更多來進一步構建。

所有這些的深入解釋可以在OpenGL Insights的第28/29章中找到,我不能在這裏整體地複製,並附帶一些可用的代碼here

您還可以在OpenGL wiki here上找到關於緩衝區對象流的更多信息。