rcapote
rcapote

Reputation: 1044

Buffers getting corrupted?

I'm trying to write a UI in OpenGL and have run into a problem when I resize a control.

Texture errors

As I shrink the panel, the text texture seems to shrink or get corrupted and eventually the buttons do the same. The buttons are not tied to the size of the window, so the problem isn't with calculating the sizes. I printed the size of the text texture and the button sizes and they stayed consistent during the test.

So every time I resize the window, here is what is happening:

onResize
    Delete TexturedRectangle object
        | Delete 9 sprites (including vertex data) used for the TexturedRectangle
        | Delete the RenderTexture
    New TexturedRectangle Object
        | Create 9 sprites (new vertex data) for the textured rectangle
        | Render the rectangle using the 9 sprites to a new RenderTexture the size of the window

I went through the code and made sure I am deleting the old data off the gpu before creating new buffers. Are my buffers getting corrupted or is the way I'm rendering to the RenderTextures incorrect? I checked glGetError() and get no errors during run time. Could the problem be with the OpenGL stack? I can't tell where the problem is because the buttons aren't changed at all when I resize the window.

Sprite::Sprite() 
    : ISprite(), mVbo(0) {
    mDiffuse = ResourceCache::getSingleton().getTexture("Texture_NOTFOUND");
    createVbo();
}

Sprite::Sprite(ITexture *diffuse, FloatRect textureBounds)
    : ISprite(), mVbo(0) {
    mDiffuse = diffuse;

    if(textureBounds.x == 0 && textureBounds.y == 0) {
        mTextureBounds = diffuse->getBounds();
    } else {
        mTextureBounds = textureBounds;
    }

    createVbo();
}

Sprite::~Sprite() {
    glDeleteBuffers(1, &mVbo);
}

void Sprite::draw(IRenderTarget *target, RenderState *state) const {
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    switch(state->getRenderMode()) {
    default:
    case RenderState::DIFFUSE:
        mDiffuse->bindTexture();
        break;
    case RenderState::NORMAL_MAP:
        mNormalMap->bindTexture();
        break;
    case RenderState::HEIGHT_MAP:
        mHeightMap->bindTexture();
        break;
    };

    glPushMatrix();
    glTranslatef(mPosition.x, mPosition.y, mPosition.z);
    glColor4f(mColor.r, mColor.g, mColor.b, mColor.a);

    glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, x));
    glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx));

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glPopMatrix();

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

void Sprite::createVbo() {
    if(mVbo != 0) {
        glDeleteBuffers(1, &mVbo);
    }

    // Generate the VBO
    glGenBuffers(1, &mVbo);
    Vector2f size = getSize();

    float texW = mDiffuse->getWidth();
    float texH = mDiffuse->getHeight();
    float srcW = size.x / texW;
    float srcH = size.y / texH;


    // Calculate the vertices
    Vertex verts[] = {{0.f, 0.f, 0.f, mTextureBounds.x / texW, mTextureBounds.y / texH},
                        {size.x, 0.f, 0.f, (mTextureBounds.x / texW) + srcW, mTextureBounds.y / texH},
                        {0.f, size.y, 0.f, mTextureBounds.x / texW, (mTextureBounds.y / texH ) + srcH},
                        {size.x, size.y, 0.f, (mTextureBounds.x / texW) + srcW, (mTextureBounds.y  / texH) + srcH}};

    int vertSize = sizeof(verts);

    // Bind the VBO
    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    // Submit the vertex data to the GPU
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 4, &verts[0].x, GL_STATIC_DRAW_ARB);

    // Unbind the VBO
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

Repeating Sprite

RepeatingSprite::RepeatingSprite(Texture *diffuseTexture, FloatRect spriteBounds, int xRepeat, int yRepeat) 
    : ISprite(), mXRepeat(xRepeat), mYRepeat(yRepeat) {
    mVbo = 0;
    mDiffuse = diffuseTexture;
    mTextureBounds = spriteBounds;
    createVbo();
}

RepeatingSprite::~RepeatingSprite() {
    glDeleteBuffers(1, &mVbo);
}



void RepeatingSprite::draw(IRenderTarget *target, RenderState *state) const {
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    switch(state->getRenderMode()) {
    default:
    case RenderState::DIFFUSE:
        mDiffuse->bindTexture();
        break;
    case RenderState::NORMAL_MAP:
        mNormalMap->bindTexture();
        break;
    case RenderState::HEIGHT_MAP:
        mHeightMap->bindTexture();
        break;
    };

    glPushMatrix();
    glTranslatef(mPosition.x, mPosition.y, mPosition.z);
    glColor4f(mColor.r, mColor.g, mColor.b, mColor.a);

    glVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, x));
    glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, tx));

    glDrawArrays(GL_QUADS, 0, (mXRepeat * mYRepeat) * 4);

    glPopMatrix();

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

void RepeatingSprite::createVbo() {
    int totalRepeats = mXRepeat * mYRepeat;
    float textureWidth = mDiffuse->getWidth();
    float textureHeight = mDiffuse->getHeight();
    Vertex *vertices = new Vertex[totalRepeats*4];

    int counter = 0;
    // For each sprite count, create a quad
    for(float y = 0; y < mYRepeat; y++) {
        for(float x = 0; x < mXRepeat; x++) {   

            Vertex v1 = {x * mTextureBounds.w, 
                         y * mTextureBounds.h, 0.f, 
                         mTextureBounds.x / textureWidth, 
                         mTextureBounds.y / textureHeight};

            Vertex v2 = {x * mTextureBounds.w + mTextureBounds.w, 
                         y * mTextureBounds.h, 0.f, 
                         (mTextureBounds.x / textureWidth) + (mTextureBounds.w / textureWidth), 
                         mTextureBounds.y / textureHeight};

            Vertex v3 = {x * mTextureBounds.w, 
                         y * mTextureBounds.h + mTextureBounds.h, 0.f, 
                         mTextureBounds.x / textureWidth, 
                         (mTextureBounds.y / textureHeight) + (mTextureBounds.h / textureHeight)};

            Vertex v4 = {x * mTextureBounds.w + mTextureBounds.w, 
                         y * mTextureBounds.h + mTextureBounds.h, 0.f, 
                         (mTextureBounds.x / textureWidth) + (mTextureBounds.w / textureWidth), 
                         (mTextureBounds.y / textureHeight) + (mTextureBounds.h / textureHeight)};


            vertices[counter] = v1;
            counter++;
            vertices[counter] = v2;
            counter++;
            vertices[counter] = v4;
            counter++;
            vertices[counter] = v3;
            counter++;
        }
    }

    glGenBuffers(1, &mVbo);

    glBindBuffer(GL_ARRAY_BUFFER, mVbo);

    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * (totalRepeats*4), &vertices[0].x, GL_STATIC_DRAW_ARB);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    delete[] vertices;
}

Render Texture

RenderTexture::RenderTexture(float width, float height) {
    mWidth = width;
    mHeight = height;

    // Create the color buffer
    glGenTextures(1, &mId);
    glBindTexture(GL_TEXTURE_2D, mId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)mWidth, (int)mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);

    // Create the framebuffer
    glGenFramebuffers(1, &mFbo);
    glBindFramebuffer(GL_FRAMEBUFFER, mFbo);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mId, 0);

    GLenum err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    assert(err == GL_FRAMEBUFFER_COMPLETE); // Make sure texture is valid

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

RenderTexture::~RenderTexture() {
    glDeleteBuffers(1, &mFbo);
    glDeleteTextures(1, &mId);
    mFbo = 0;
}


void RenderTexture::preDraw() {
    // store the glViewport and glEnable states
    glPushAttrib(GL_VIEWPORT_BIT);

    // Bind the frame buffer
    //glBindTexture(GL_TEXTURE_2D, 0);
    glBindFramebuffer(GL_FRAMEBUFFER, mFbo);

    // Save the current matrix
    glPushMatrix();
    glLoadIdentity();

    // Setup the projection matrix for the render target
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glViewport(0, 0, (int)mWidth, (int)mHeight);
    glOrtho(0, mWidth, 0.f, mHeight, 0.f, 100.f);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glDrawBuffer(GL_COLOR_ATTACHMENT0);
}

void RenderTexture::postDraw() {
    // Pop the render target's projection matrix off the stack
    glPopMatrix();
    // Restore previouse projection matrix
    glPopMatrix();
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    // Restore the previous viewport settings
    glPopAttrib();
}

Upvotes: 2

Views: 466

Answers (1)

Michael Slade
Michael Slade

Reputation: 13877

In OpenGL, when you apply a transform of some sort to an object and see other objects being affected, a good place to start looking is your transform and stack manipulation logic.

So in RenderTexture::preDraw() you have:

glPushMatrix();
// ...
glMatrixMode(GL_PROJECTION);
glPushMatrix();

and in RenderTexture::postDraw():

glPopMatrix();
// Restore previouse projection matrix
glPopMatrix();

without any call to glMatrixMode() between them.

It won't work properly like this. Each matrix mode has its own stack, so that second glPopMatrix() is popping off the wrong stack.

You will need to go something like:

glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

Upvotes: 2

Related Questions