Jump to content
  • Advertisement
Sign in to follow this  
csisy

OpenGL Moving to OpenGL 3.1

This topic is 794 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, it's me again :)

 

So I've decided to drop the 100 years old compatiblity and finally move on - RIP OpenGL 2.0.

 

As a short to-do list:

- change context creation

- change GLSL shaders and the bindings to the client

- drop the old uniform handling and use UBO

- introduce VAOs

 

And my biggest problem is with VAO. I think I know how it works: in short, it stores the required bindings for drawing (vertexAttribPointers, enabled vertex attrib arrays). This can be a good thing, however this way I lose the connection with the currently bound shader.

 

How my current (2.0) rendering works:

bind a program
check the used attributes and enable/disable vertex attrib arrays
for each geometry
    bind vbo
    call vertex attrib pointers
    bind ibo (element array buffer)
    draw

And this way a vertex attrib pointer is only called for an active attribute. So if the VBO contains eg. positions, normals and uvs but the shader only uses the positions, a single vertex attrib pointer is called (and note that the other attributes are disabled)

 

But if I use VAO, the enabled state and the attrib pointers are associated with the VAO and decoupled from the shader.

// initialization
bind vao
bind vbo
call vertex attrib pointer for every attribute
call enable vertex attrib array for every attribute
bind ibo
//unbind vao to avoid mistakes

// drawing
bind program
for each geometry
    bind vao
    draw

So the question: Logically it looks better to call only two functions to draw a geometry instead of many, but does the bindings and enables affect the performance? Of course I could call the enable/disable and attribPointer functions every frame but I think that would have a bigger performancy penalty.

 

Edit:

I thought the same that this guy wrote here.

Edited by csisy

Share this post


Link to post
Share on other sites
Advertisement

I handle my VAOs in a way that I have only 2 calls per model by enabling attributes once as much as needed

Graphics::BufferData(BufferTarget::ArrayBuffer, sizeof(vertices) * sizeof(float), vertices, BufferUsage::StaticDraw);
Graphics::VertexAttribPointer(0, 3, VertexAttribPointerType::Float, false, 20, 0);
Graphics::VertexAttribPointer(1, 2, VertexAttribPointerType::Float, false, 20, (void*)12);

Graphics::EnableVertexAttribArray(0);
Graphics::EnableVertexAttribArray(1);
Graphics::BindVertexArray(0);

And when rendering call just

Graphics::BindVertexArray(model);
Graphics::DrawArrays(PrimitiveType::TriangleStrip, 0, 5);

For my Shader I always bind the attributes staticaly to certain locations with

//vertex
layout (location = 0) in vec4 Vertex;
layout (location = 1) in vec2 TexCoord;

Anything else about binding Textures/Shaders does my render pipeline in a way that it sorts models by Shader and then by Texture to keep Shader/Texture swaps as low as possible (and safes performance this way). It would save even more performance when you also skip the call to glBindVertexArray and do that once for each cluster of same models

Edited by Shaarigan

Share this post


Link to post
Share on other sites

In practice what you're going to find is that you only have a handful of vertex format combinations (i.e enabled arrays, strides, offsets) but the buffers they're sourced from change with greater frequency.

 

What you need is GL_ARB_vertex_attrib_binding - it's getting into GL 4.x territory but support for it should be uniquitous nowadays, and it's designed to solve exactly this problem.  Using this model your vertex format specification moves from runtime to load time, and at runtime you just bind a VAO and issue some glBindVertexBuffer calls.

Share this post


Link to post
Share on other sites

Thanks for your replies!

 

I'm not sure I'd like to use any GL higher than 3.1. A decent amount of Intel cards support only <= 3.1.

 

Well, I'm done with porting to 3.1. I've lost about 10 FPS (from 462 -> 452) but I can accept this because much more functions are available now (eg. geometry shaders). For now I followed the way that UE4 chosen: create a single VAO for every rendering context. This way, the rest of the code is untouched. :)

 

Edit:

Nevermind, it runs at the same speed than before and the UBOs are not used yet. :)

Edited by csisy

Share this post


Link to post
Share on other sites

Okay, so let's talk about the UBOs.

 

I think I have two different options and can't decide which one I should choose.

 

1) layout(std140)

This is probably the easier because the aligments and sizes are predefined. This way I can create a UBO everywhere (eg. before any shader program linked) and share across programs. I just have to follow the standards and align the data manually.

 

2) layout(shared)

This is more tricky, because first I need a program to query the parameters of the uniform block. However when two blocks are the same in the shader codes, then the layouts match as well. So at engine startup, I can compile and link a dummy shader where every available uniform block is defined. Actually I can imagine only three: one for camera and other global properties (view, projection, camera parameters, and so on) and one for the common object properties (world matrix and bones matrix array) and the last for common lighting parameters (color, intensity)

 

What do you think?

Share this post


Link to post
Share on other sites

Just std140 all the things. Its basically what D3D does, and look how popular it is.

 

Well, I'm done with porting to 3.1. I've lost about 10 FPS (from 462 -> 452)
 You're looking at this from a bad perspective. Don't measure FPS, measure milliseconds per frame.

 

462 fps is a grand total of 2.164 milliseconds per frame.

452 fps is a grand total of 2.212 milliseconds per frame.

 

Less than half a millisecond of difference (0.47ms). Thats nothing. You can probably chalk those margins up to measuring error.

 

Now, say that you're at 30 fps, and you loose 10 fps.

 

30 fps is 33.333 ms.

20 fps is 50.000 ms.

 

Difference is a whooping 16.666 milliseconds per frame. Thats a lot. And the difference from a playable 30 fps to a horribly unplayable 20 fps. Whereas from 460 fps to 450 fps, you probably cant physically notice a difference.

Share this post


Link to post
Share on other sites

Just std140 all the things. Its basically what D3D does, and look how popular it is.

I thought the same but it consumes more memory. But probably that's not a big deal.

 

The FPS "drop" (which actually does not happen, just forgot to comment an extra "test" clear :)) was just a sidenote.

 

I guess I'll go for the std140-way.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!