質問

I have a 5 megapixel texture that I am having trouble updating. The texture is displayed on a rectangle not dissimilar to a video stream.

The OpenGL commands execute quickly, but the real texture update rate is sub optimal, perhaps only 3 actual frames per second. There is some change, but not much change when using a smaller texture (500x500).

The machine has a NVIDIA gtx 570

My initial efforts were to use glTexSubImage2D and glBufferSubData, but these performed slightly worse than the memory mapped scheme.

Is there any way to force the graphics card to update the texture? How is video streaming software written?

Render Loop

void glStream::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
    program1.bind();
    program1.setUniformValue("texture", 0);
    program1.enableAttributeArray(vertexAttr1);
    program1.enableAttributeArray(vertexTexr1);
    program1.setAttributeArray(vertexAttr1, vertices.constData());
    program1.setAttributeArray(vertexTexr1, texCoords.constData());
    //
    glECheck();
    glBindTexture(GL_TEXTURE_2D, textures[0]);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,pbos[0]);
    void* memory = glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,GL_WRITE_ONLY);
    device->fillBuffer((unsigned char *)memory,heightGL,widthGL); // takes 2ms (not long)
    glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB);
    glTexSubImage2D(GL_TEXTURE_2D,0,0,0,widthGL,heightGL,GL_LUMINANCE,GL_UNSIGNED_BYTE, NULL);
    glDrawArrays(GL_TRIANGLES, 0, vertices.size());
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB,0);
    glBindTexture(GL_TEXTURE_2D,0);
    //
    program1.disableAttributeArray(vertexTexr1);
    program1.disableAttributeArray(vertexAttr1);
    program1.release();
    glECheck();//no errors
}

Texture Reservation

void glStream::reserveTextures()
{
    assert(numGLFrames>0);
    assert(glGenBuffers);
    displayBuff = (GLubyte*) calloc(numGLFrames*widthGL*heightGL,sizeof(GLubyte));//GL_RGB8
    memset(displayBuff,100,numGLFrames*widthGL*heightGL*sizeof(GLubyte));
    glGenBuffers(1,&pbos[0]);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, pbos[0]);
    glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB,
                 numGLFrames*widthGL*heightGL*sizeof(GLubyte),
                 &displayBuff[0], GL_STREAM_DRAW);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
    glGenTextures(1,&textures[0]);
    glBindTexture(GL_TEXTURE_2D,textures[0]);
    glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,
                 widthGL,heightGL,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
    glBindTexture(GL_TEXTURE_2D,0);
}

Initialization

void glStream::initializeGL()
{
    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        const char * emssage = (const char*)glewGetErrorString(err);
        QMessageBox::information(0, "OpenGL 3.x Context Example",
                                 emssage);
        exit(20);
    }
    glDisable(GL_DEPTH_TEST);
    QGLShader *vshader1 = new QGLShader(QGLShader::Vertex, this);
    const char *vsrc1 =
        "attribute vec2 coord2d;   \n"
        "attribute mediump vec4 texCoord;\n"
        "varying mediump vec4 texc;\n"
        "void main()                  \n"
        "{                            \n"
        "   gl_Position = vec4(coord2d, 0.0, 1.0); \n"
        "   texc = texCoord;\n"
        "}                          \n";
    vshader1->compileSourceCode(vsrc1);
    QGLShader *fshader1 = new QGLShader(QGLShader::Fragment, this);
    const char *fsrc1 =
        "uniform sampler2D texture;\n"
        "varying mediump vec4 texc;\n"
        "void main(void)\n"
        "{\n"
        "    gl_FragColor = texture2D(texture, texc.st);\n"
        "}\n";
    fshader1->compileSourceCode(fsrc1);
    program1.addShader(vshader1);
    program1.addShader(fshader1);
    program1.link();
    vertexAttr1 = program1.attributeLocation( "coord2d");
    vertexTexr1 = program1.attributeLocation( "texCoord");
    // Create the vertex buffer.
    vertices.clear();
    float u=1;
#define AVEC -u,u
#define BVEC -u,-u
#define CVEC u,u
#define DVEC u,-u
    vertices << QVector2D(AVEC);vertices << QVector2D(BVEC);
    vertices << QVector2D(CVEC);vertices << QVector2D(BVEC);
    vertices << QVector2D(DVEC);vertices << QVector2D(CVEC);
    // Create the texture vertex buffer
#define TAVEC 0,1
#define TBVEC 0,0
#define TCVEC 1,1
#define TDVEC 1,0
    texCoords << QVector2D(TAVEC);texCoords << QVector2D(TBVEC);
    texCoords << QVector2D(TCVEC);texCoords << QVector2D(TBVEC);
    texCoords << QVector2D(TDVEC);texCoords << QVector2D(TCVEC);
    glECheck();
    reserveTextures();
}

Edit 1

I am confident that fill buffer comes up with a new texture because in some other part of the code I write this texture to a file and it is indeed different. Its a sad day when my File IO is faster then a OpenGL texture.

Edit 2 I tried out FRAPS and I verified that the render loop is going at ~18 FPS, but the visible updates are slower (maybe 3 FPS). What could account for such a discrepancy?

役に立ちましたか?

解決

The PBO is much larger than the texture, in the call to glBufferData:

numGLFrames*widthGL*heightGL*sizeof(GLubyte)

You're allocating a PBO large enough for multiple textures (frames), but only ever reading/writing one frame's worth of data.

If you make the PBO the same size as the texture and use glTexImage2D instead of glTexSubImage2D, is it much faster?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top