Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm working on a game where I use shaders with vertex attributes (so not immediate mode). I'm drawing lots of images and changing the width/height of the quads I use to draw them a lot. To optimize this it's probably a good idea to have one buffer but then one needs to update the complete buffer when one image changes (or only a part of the buffer using glBufferSubData...)

I was just wondering what kind of strategies you guys are using?

share|improve this question
add comment

2 Answers 2

One option may be to just use immediate mode with glVertexAttrib calls instead of the more traditional glTexCoord/glVertex/etc calls - you'll certainly avoid having to worry about how you manage VBOs here, and will still be able to get all the attribute goodness you need. Of course it is immediate mode, but if it works well enough then I say it works well enough.

Another way is to use a large VBO and glMapBufferRange - ensure that you specify the GL_MAP_UNSYNCHRONIZED_BIT option and append to the buffer each time you add a quad; when the buffer is full orphan it via GL_INVALIDATE_BUFFER_BIT or glBufferData (... NULL ...). That's probably the most efficient way of handling this use case with a VBO.

See here for further info: http://www.opengl.org/wiki/Buffer_Object_Streaming

It's worth making a comparison with D3D here. The glMapBufferRange approach is an almost direct match for D3D's NO_OVERWRITE/DISCARD pattern, which has been in use for well over a decade and is proven to work well for this kind of usage. That should assure you that it is going to be an optimized path and will be well supported.

share|improve this answer
add comment

I'm drawing lots of images and changing the width/height of the quads I use to draw them a lot.

My suggestion would be to make a function for a single quad you want to render and batching the calls in a for loop.

I made a vertex attribute object for rendering sprites using the Sprite shader as a source for the attributes:

    // sprite batches

    m_SpriteBatchShader = ResourceManager::Get().GetShader("Sprite");
    m_SpriteBatchArray = m_SpriteBatchShader->CreateVertexArray();

    m_SpriteBatchPosition = m_SpriteBatchArray->GetAttribute("attrPosition")->GetArray<tb::Vec2>();
    m_SpriteBatchPosition->SetData(AccessMode::Host | AccessMode::Write, 4, NULL);

    m_SpriteBatchTexCoord = m_SpriteBatchArray->GetAttribute("attrTexCoord0")->GetArray<tb::Vec2>();
    m_SpriteBatchTexCoord->SetData(AccessMode::Host | AccessMode::Write, 4, NULL);
    tb::Vec2* dst_tex = m_SpriteBatchTexCoord->Lock(LockMode::Write);
    dst_tex[0] = tb::Vec2(0.f, 1.f);
    dst_tex[1] = tb::Vec2(1.f, 1.f);
    dst_tex[2] = tb::Vec2(1.f, 0.f);
    dst_tex[3] = tb::Vec2(0.f, 0.f);
    m_SpriteBatchTexCoord->Unlock();

I never have to change the texture coordinates for a sprite, because they're always going to be the same, filling the entire quad.

And now you can loop over the Sprites in a batch call:

m_SpriteBatchShader->Enable();
m_SpriteBatchShader->BindVertexAttributeArray(m_SpriteBatchArray);
m_SpriteBatchShader->SetMatrix(MatrixLocation::FullTransform, m_MatOrthographic);

for (size_t i = 0; i < a_SpriteBatch.size(); i++)
{
    Sprite* curr = a_SpriteBatch[i];

    const tb::Vec2& pivot = curr->GetPivot();
    const tb::Vec2& dim = curr->GetDimensions();

    tb::Vec2 pos_ul(-pivot.x,         -pivot.y        );
    tb::Vec2 pos_ur(-pivot.x + dim.x, -pivot.y        );
    tb::Vec2 pos_ll(-pivot.x        , -pivot.y + dim.y);
    tb::Vec2 pos_lr(-pivot.x + dim.x, -pivot.y + dim.y);

    // this Lock is a wrapper around glMapBuffer, which returns a pointer
    tb::Vec2* dst_pos = m_SpriteBatchPosition->Lock(LockMode::Write);

    dst_pos[0] = curr->GetTransform() * pos_ul;
    dst_pos[1] = curr->GetTransform() * pos_ur;
    dst_pos[2] = curr->GetTransform() * pos_lr;
    dst_pos[3] = curr->GetTransform() * pos_ll;

    // Unlock uploads data to the GPU
    m_SpriteBatchPosition->Unlock();

    // bind the sprite's texture to GL_TEXTURE0 and texDiffuse in the shader
    m_SpriteBatchShader->BindTexture(TextureLocation::Diffuse, 0, curr->GetTextureHandle());

    glDrawArrays(GL_QUADS, 0, 4);
}

m_SpriteBatchShader->Disable();

How can you optimize this further? Well, if your sprites don't need to be rotated, you can remove that transform. If they don't need a pivot, you can remove that as well. But if you do need those things, it may be beneficial to rotate the quad in the shader.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.