2016-07-27 11 views
-1

Ich versuche, QOpenGLBuffer als Pixel Buffer-Objekt zu verwenden. Das Ziel wäre, einen hochauflösenden Videostream (4K, 60FPS) anzuzeigen, also brauche ich eine gute Leistung.Wie verwende ich PBO mit Qt OpenGL

Da ich gerade mit OpenGL beginne, versuche ich zunächst, eine einfache 2D-Textur so gut wie möglich darzustellen. Ich habe bereits VBO und VAO aufgenommen, der nächste Schritt (wie ich es gelesen habe) wäre, ein PBO für bessere Leistung zu verwenden.

Es gibt Tutorial für PBO aber mit glGenBufferARB(), nicht mit QOpenGLBuffer.

Hier ist mein Code:

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 

Also im Grunde, wie würde ich tun, in diesem Code PBO zu verwenden ?

Das erste, was zu tun wäre, ein QOpenGLBuffer-Objekt zu erstellen, während der Typ (QOpenglBuffer :: PixelUnpackBuffer) angeben, dann würde ich denke, ich müsste das Pixel auf den Puffer hochladen und schließlich anstelle von glTexImage2D verwenden? Dies ist nur die globale Idee und ich habe keine Ahnung, wie es geht.

Danke.

Antwort

0

Das Ziel wäre es, einen hochauflösenden Videostream (4K, 60FPS) anzuzeigen, also brauche ich gute Leistung.

Der einzige und richtige Weg, dies zu tun, ist die Verwendung einiger beschleunigter Präsentation API (die mit OpenGL nichts zu tun haben).

Wenn Sie bei OpenGL bleiben wollen, sollten Sie zumindest die Grafikdekodierung und das Hochladen der Grafik in eine Textur durchführen lassen. Wie das geht, hängt von Ihrem Betriebssystem und Ihrer GPU ab. Unter Linux und mit NVIDIA können Sie beispielsweise VDPAU für die beschleunigte Dekodierung und NV_VDPAU_interop verwenden, um Texturen mit decodierten Frames zu füllen.

Wenn Sie immer noch Pixel Packen Objekte dafür (PUBO; Sie laden in GL => es ist ein Auspacken) Packen Sie es ist sehr wenig Magie los ist. Erstellen Sie ein:

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

füllen Sie es dann mit Ihrem Rahmen Daten:

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

Nun ist die Wirkung eines pubo ist, dass, wenn man gebunden ist, bestimmte Anrufe Semantik ändern wird, Daten lesen nicht aus Benutzer Speicher aber von der PUBO. Bemerkenswerterweise die Aufrufe, die Texturdaten hochladen.Also, wenn Sie Ihre Textur um (und Sie sollten QOpenGLTexture verwenden, die unveränderlichen Speicher verwendet, nicht manuelle Anrufe glTexImage), können Sie tun:

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

Da es eine pubo ist verpflichtet, das allerletzte Argument (data) ändert Semantik: Es ist nicht mehr ein Zeiger auf Client-Speicher, aber es ist ein Byte-Offset in das aktuell gebundene Pixel entpacken Pufferobjekt. Wenn also Ihre Texturdaten bei Offset 0 im Puffer beginnen, müssen Sie dort 0 übergeben (oder tatsächlich (const GLvoid *)0). Andernfalls müssen Sie es entsprechend anpassen.

Jetzt können Sie die pubo Mitteilung:

pubo->release(); 

und dann die Textur wie gewohnt in Ihrem Shadern verwenden und alles wird gut.


Außer, wenn Sie die Textur sofort verwenden, erhalten Sie keine Leistungsverbesserung! Der springende Punkt dieser komplizierten Einrichtung besteht darin, dem GL zu erlauben, die Bilddaten asynchron zu übertragen, während Sie die Daten, die in den vorherigen Rahmen hochgeladen wurden, rendern. Wenn Sie die Bilddaten sofort verwenden, muss der GL die gesamte Pipeline synchronisieren, um darauf zu warten, dass die Bilddaten auf die GPU hochgeladen werden.

Das typische Setup in diesem Szenario ist also eine Reihe von PUBOs in einer Round-Robin-Mode verwendet. Zum Beispiel, wenn Sie zwei (in Ping-poing) haben, laden Sie Daten in einem PBO hoch, und benutzen das vorherige, um die Textur zu füllen und zu zeichnen. Dies sollte "genug Zeit" für den GL kaufen, um die aktuellen Daten tatsächlich über den Bus zu übertragen, so dass beim nächsten Frame der Textur-Upload und die Zeichnung die Daten sofort verfügbar finden. Im Idealfall können Sie auch Daten aus einem anderen Thread in PUBOs hochladen, die einen freigegebenen OpenGL-Kontext verwenden, und fences verwenden, um den Render-Thread zu signalisieren, wenn der Upload abgeschlossen ist, sodass die Textur gefüllt werden kann. Und Sie können weiter bauen, indem Sie verwaiste Maps, nicht synchronisierte Schreibvorgänge und noch mehr verwenden.

Eine ausführliche Erklärung all dessen ist in den Kapiteln 28/29 von OpenGL Insights verfügbar, die ich hier nicht vollständig reproduzieren kann, und mit einem Code zur Verfügung steht, der verfügbar ist here.

Sie können auch weitere Informationen über das Streaming von Pufferobjekten im OpenGL Wiki here finden.

Verwandte Themen