如何在Qt OpenGL中使用PBO
我试图使用QOpenGLBuffer作为像素缓冲区对象。 目标是显示高分辨率视频流(4K,60FPS),所以我需要良好的性能。
由于我刚刚开始使用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:UsersAdrienDesktopopen3.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 coren"
"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 coren"
"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? 这只是全球性的想法,我不知道该怎么做。
谢谢。
目标是显示高分辨率视频流(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的效果是,如果绑定了一个,某些调用将改变语义以读取不是来自用户内存的数据,而是来自PUBO。 值得注意的是,上传纹理数据的调用。 所以,如果你有你的纹理(你应该使用QOpenGLTexture,它使用不可变的存储,而不是手动调用glTexImage
),你可以这样做:
pubo->bind();
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D,
level,
internalFormat,
width,
heigth,
border,
externalFormat,
type,
data);
由于有一个PUBO界限,最后一个参数( data
)会改变语义:它不再是一个指向客户端内存的指针,而是一个到当前绑定的像素解包缓冲区对象的字节偏移量 。 所以,如果你的纹理数据从偏移0开始到缓冲区,你需要在那里传递0(或者,实际上, (const GLvoid *)0
)。 否则,你需要相应地调整它。
现在你可以释放pubo:
pubo->release();
然后在着色器中像往常一样使用纹理,一切都会很好。
除了如果你马上使用纹理,你不会得到任何性能改进! 这一复杂设置的重点在于,当您渲染上一帧中上传的数据时,GL允许异步传输图像数据。 如果您立即使用图像数据,则GL需要同步整个管道以等待图像数据上传到GPU。
因此,这种情况下的典型设置是以循环方式使用多个PUBO。 例如,如果您有两个(在ping-poing中),则可以在一个PBO中上传数据,并使用前一个填充和绘制纹理。 这应该为GL购买“足够的时间”,以便实际上通过总线传输当前数据,因此在下一帧时,贴图上传和绘图将立即找到数据。
理想情况下,您还可以使用共享的OpenGL上下文从另一个线程执行PUBO中的数据上传,并在上传完成时使用屏蔽来指示渲染线程,以便填充纹理。 而且你可以通过使用孤立,固定地图,非同步写入甚至更多来进一步构建。
所有这些的深入解释可以在OpenGL Insights的第28/29章中找到,我不能在这里整体复制,并附带一些可用的代码。
您也可以在这里找到关于OpenGL维基缓冲区对象流的更多信息。
链接地址: http://www.djcxy.com/p/45743.html上一篇: How to use PBO with Qt OpenGL
下一篇: How to copy/render a Pixel Buffer to the back buffer in OpenGL 3.X