Sign in to follow this  
issch

OpenGL Double-buffered VBO vs updating parts of a VBO

Recommended Posts

Hi,

I am currently working on my rendering code (using OpenGL 3.2, in case it matters) and I am wondering which of these approaches would be best (or if any better alternatives exist).

In both methods I store data about each object (position, colour etc) in a VBO, lets call it objectsVBO.

Method A, double-buffered VBO:

[code]
objectsVBO[0] and objectsVBO[1] are the two VBO buffers.
unsigned int buf = 0;
updatedObjects is an array of all objects, after having been updated by gameplay & physics code

every frame:
sort updatedObjects back to front, remove objects not in view
glBufferData to put updatedObjects into objectsVBO[buf]
glDrawArrays(objectsVBO[buf], 0, num_objects_in_view);
buf = 1 - buf;
[/code]
EDIT: Looks like [url="http://www.opengl.org/wiki/Buffer_Object_Streaming#Buffer_re-specification"]this method[/url] may be a better way to accomplish the above.

Method B, updating part of VBO:

[code]
objectsVBO is a VBO storing all objects
updatedObjects is an array of objects that have changed, after having been updated by gameplay & physics code

every frame:
glBufferSubData to put updatedObjects into objectsVBO (possibly only objects that are on screen)
indices = index of each object in objectsVBO that is on screen, sorted back to front
glDrawElements(objectsVBO, indices);
[/code]

Method A means only objects to be drawn ever need to be pushed to the gpu and since its double buffered, hopefully the next frame can be prepared before the first frame is fully rendered.

Method B means only objects which have actually changed need to be pushed to the gpu while unchanged objects are still there from the previous frame but I do not have double buffering.

Both methods only render whats on screen and render back to front as required. Static geometry will be kept in a separate VBO in both cases.

Any help or ideas appreciated as I don't have an awful lot of experience with 3D rendering yet.

Share this post


Link to post
Share on other sites
Or, store exactly 1 copy of each model in your vbo and just loop through your models setting up matrices. The point of vbos is to stop talking to the gpu so much. You can also store everything in 1 bigger vbo so that you dont have to bind separate vbos for different models.

Share this post


Link to post
Share on other sites
Well, that works fine for any static or almost static geometry. I was planning to do that in these cases as, like you said, the geometry then needs to only be uploaded once.
I am looking at these two options for dynamic stuff that will change almost every frame, like sprites and particles where the geometry is being modified from frame to frame instead of just the matrices. In this case I was thinking of tightly packed VBOs (eg positions of point sprites/billboards) which then get batch rendered by a single call to glDrawArrays or glDrawElements.

Share this post


Link to post
Share on other sites
If doing it for particles, then keep a vbo for each instance. So like 50 particles max for your fire effect. The buffer would be 50 positions and 50 sizes (in world coordinates) for each fire in your game. Any particles that are dead, mark their size as 0 and just draw them anyway. Just upload to 1 buffer and then use it to draw. I dont see a benefit to double buffering unless someone says otherwise, but I highly doubt it.

Share this post


Link to post
Share on other sites
Double buffering isn't needed, u just have to make sure to glBufferData with null first so the Driver can allocate new memory and don't have to stall.

With OpenGL 3 you have access to the Transform Feedback extension, which let u "render" from a Vertex Shader vertices into a Buffer Object.
This let u calculate all the particle movement on the GPU and several million particles are no problem (Sorting on the GPU is also possible but a bit more complicated).
PS: When using OpenGL 3 u can use Buffer objects for your Shader uniforms, take a look at [url="http://www.opengl.org/wiki/Uniform_Buffer_Object"]http://www.opengl.org/wiki/Uniform_Buffer_Object[/url].

Share this post


Link to post
Share on other sites
Have a look at some examples of Hardware Instancing on the gpu via gsl shaders..
Basically, you can pass a page worth of matrices to the gpu in one call, representing a bunch of unique object instances based on a single static vbo.
Try to use transforms to manipulate your geometry instead of manipulating the VB.

Share this post


Link to post
Share on other sites
[quote name='Danny02' timestamp='1304986343' post='4808780']
Double buffering isn't needed, u just have to make sure to glBufferData with null first so the Driver can allocate new memory and don't have to stall.

With OpenGL 3 you have access to the Transform Feedback extension, which let u "render" from a Vertex Shader vertices into a Buffer Object.
This let u calculate all the particle movement on the GPU and several million particles are no problem (Sorting on the GPU is also possible but a bit more complicated).
PS: When using OpenGL 3 u can use Buffer objects for your Shader uniforms, take a look at [url="http://www.opengl.org/wiki/Uniform_Buffer_Object"]http://www.opengl.or...m_Buffer_Object[/url].
[/quote]
Thanks! This is the kind of information I was hoping to find.

[quote name='__Homer__' timestamp='1304987532' post='4808787']
Have a look at some examples of Hardware Instancing on the gpu via gsl shaders..
Basically, you can pass a page worth of matrices to the gpu in one call, representing a bunch of unique object instances based on a single static vbo.
Try to use transforms to manipulate your geometry instead of manipulating the VB.
[/quote]
Yes, this seems like a good idea for complex dynamic objects. Thanks. So, basically I would store the matrices in a buffer object (like what Danny02 suggests) and then render the VBO with glDrawElementsInstanced however many times I need, suing the instance id to index the buffer object of matrices? Is that correct?

Finally, what about simple objects like point sprites (ie the VBO only contains a small amount of information (eg, position and colour) per vertex (one object) but stores many objects, instead of a lot of geometry for a single model)? For particles, Danny02's suggestion of handling everything on the GPU sounds appealing, but what about things that cannot be handled entirely on the GPU, eg, because they are physics enabled? I take it I can use Transform Feedback here?

I'm thinking something like the following:
[code]
Each vertex in a VBO is one object, containing whatever parameters are required.
Store events (generated from user interaction, collisions etc) into BO
BeginTransformFeedback()
DrawArrays() - the vertex shader now calculates positions, animation and so on for each object. Use events BO to add external stimulus where needed.
EndTransformFeedback()
DrawArrays the written buffer to render
MapBuffer to read the written values (for input to collision detection code or user interaction or whatever)
[/code]

Does that sound like a reasonable procedure? I'm especially wondering how best to handle the external stimulus. Perhaps a good way would be to glDrawElementsInstanced so I can use the instance id to look up stimulus/events for a single object. My first thought was to use the instance ID as a y coordinate in a 2d sampler (where the X coordinates contain the event information for that object) and then make sure that unused event texels are set in a way that applying them does nothing (identity function). Is that a reasonable way to do this or is that crazy (and potentially slow)?

Thanks for all the help everyone, I'm learning lots of new things, which is what I was hoping for.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Announcements

  • Forum Statistics

    • Total Topics
      628312
    • Total Posts
      2981999
  • Similar Content

    • By mellinoe
      Hi all,
      First time poster here, although I've been reading posts here for quite a while. This place has been invaluable for learning graphics programming -- thanks for a great resource!
      Right now, I'm working on a graphics abstraction layer for .NET which supports D3D11, Vulkan, and OpenGL at the moment. I have implemented most of my planned features already, and things are working well. Some remaining features that I am planning are Compute Shaders, and some flavor of read-write shader resources. At the moment, my shaders can just get simple read-only access to a uniform (or constant) buffer, a texture, or a sampler. Unfortunately, I'm having a tough time grasping the distinctions between all of the different kinds of read-write resources that are available. In D3D alone, there seem to be 5 or 6 different kinds of resources with similar but different characteristics. On top of that, I get the impression that some of them are more or less "obsoleted" by the newer kinds, and don't have much of a place in modern code. There seem to be a few pivots:
      The data source/destination (buffer or texture) Read-write or read-only Structured or unstructured (?) Ordered vs unordered (?) These are just my observations based on a lot of MSDN and OpenGL doc reading. For my library, I'm not interested in exposing every possibility to the user -- just trying to find a good "middle-ground" that can be represented cleanly across API's which is good enough for common scenarios.
      Can anyone give a sort of "overview" of the different options, and perhaps compare/contrast the concepts between Direct3D, OpenGL, and Vulkan? I'd also be very interested in hearing how other folks have abstracted these concepts in their libraries.
    • By aejt
      I recently started getting into graphics programming (2nd try, first try was many years ago) and I'm working on a 3d rendering engine which I hope to be able to make a 3D game with sooner or later. I have plenty of C++ experience, but not a lot when it comes to graphics, and while it's definitely going much better this time, I'm having trouble figuring out how assets are usually handled by engines.
      I'm not having trouble with handling the GPU resources, but more so with how the resources should be defined and used in the system (materials, models, etc).
      This is my plan now, I've implemented most of it except for the XML parts and factories and those are the ones I'm not sure of at all:
      I have these classes:
      For GPU resources:
      Geometry: holds and manages everything needed to render a geometry: VAO, VBO, EBO. Texture: holds and manages a texture which is loaded into the GPU. Shader: holds and manages a shader which is loaded into the GPU. For assets relying on GPU resources:
      Material: holds a shader resource, multiple texture resources, as well as uniform settings. Mesh: holds a geometry and a material. Model: holds multiple meshes, possibly in a tree structure to more easily support skinning later on? For handling GPU resources:
      ResourceCache<T>: T can be any resource loaded into the GPU. It owns these resources and only hands out handles to them on request (currently string identifiers are used when requesting handles, but all resources are stored in a vector and each handle only contains resource's index in that vector) Resource<T>: The handles given out from ResourceCache. The handles are reference counted and to get the underlying resource you simply deference like with pointers (*handle).  
      And my plan is to define everything into these XML documents to abstract away files:
      Resources.xml for ref-counted GPU resources (geometry, shaders, textures) Resources are assigned names/ids and resource files, and possibly some attributes (what vertex attributes does this geometry have? what vertex attributes does this shader expect? what uniforms does this shader use? and so on) Are reference counted using ResourceCache<T> Assets.xml for assets using the GPU resources (materials, meshes, models) Assets are not reference counted, but they hold handles to ref-counted resources. References the resources defined in Resources.xml by names/ids. The XMLs are loaded into some structure in memory which is then used for loading the resources/assets using factory classes:
      Factory classes for resources:
      For example, a texture factory could contain the texture definitions from the XML containing data about textures in the game, as well as a cache containing all loaded textures. This means it has mappings from each name/id to a file and when asked to load a texture with a name/id, it can look up its path and use a "BinaryLoader" to either load the file and create the resource directly, or asynchronously load the file's data into a queue which then can be read from later to create the resources synchronously in the GL context. These factories only return handles.
      Factory classes for assets:
      Much like for resources, these classes contain the definitions for the assets they can load. For example, with the definition the MaterialFactory will know which shader, textures and possibly uniform a certain material has, and with the help of TextureFactory and ShaderFactory, it can retrieve handles to the resources it needs (Shader + Textures), setup itself from XML data (uniform values), and return a created instance of requested material. These factories return actual instances, not handles (but the instances contain handles).
       
       
      Is this a good or commonly used approach? Is this going to bite me in the ass later on? Are there other more preferable approaches? Is this outside of the scope of a 3d renderer and should be on the engine side? I'd love to receive and kind of advice or suggestions!
      Thanks!
    • By nedondev
      I 'm learning how to create game by using opengl with c/c++ coding, so here is my fist game. In video description also have game contain in Dropbox. May be I will make it better in future.
      Thanks.
    • By Abecederia
      So I've recently started learning some GLSL and now I'm toying with a POM shader. I'm trying to optimize it and notice that it starts having issues at high texture sizes, especially with self-shadowing.
      Now I know POM is expensive either way, but would pulling the heightmap out of the normalmap alpha channel and in it's own 8bit texture make doing all those dozens of texture fetches more cheap? Or is everything in the cache aligned to 32bit anyway? I haven't implemented texture compression yet, I think that would help? But regardless, should there be a performance boost from decoupling the heightmap? I could also keep it in a lower resolution than the normalmap if that would improve performance.
      Any help is much appreciated, please keep in mind I'm somewhat of a newbie. Thanks!
    • By test opty
      Hi,
      I'm trying to learn OpenGL through a website and have proceeded until this page of it. The output is a simple triangle. The problem is the complexity.
      I have read that page several times and tried to analyse the code but I haven't understood the code properly and completely yet. This is the code:
       
      #include <glad/glad.h> #include <GLFW/glfw3.h> #include <C:\Users\Abbasi\Desktop\std_lib_facilities_4.h> using namespace std; //****************************************************************************** void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow *window); // settings const unsigned int SCR_WIDTH = 800; const unsigned int SCR_HEIGHT = 600; const char *vertexShaderSource = "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" "}\0"; const char *fragmentShaderSource = "#version 330 core\n" "out vec4 FragColor;\n" "void main()\n" "{\n" " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" "}\n\0"; //******************************* int main() { // glfw: initialize and configure // ------------------------------ glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // glfw window creation GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "My First Triangle", nullptr, nullptr); if (window == nullptr) { cout << "Failed to create GLFW window" << endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // glad: load all OpenGL function pointers if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { cout << "Failed to initialize GLAD" << endl; return -1; } // build and compile our shader program // vertex shader int vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); glCompileShader(vertexShader); // check for shader compile errors int success; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl; } // fragment shader int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); glCompileShader(fragmentShader); // check for shader compile errors glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl; } // link shaders int shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); // check for linking errors glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog); cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl; } glDeleteShader(vertexShader); glDeleteShader(fragmentShader); // set up vertex data (and buffer(s)) and configure vertex attributes float vertices[] = { -0.5f, -0.5f, 0.0f, // left 0.5f, -0.5f, 0.0f, // right 0.0f, 0.5f, 0.0f // top }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); // bind the Vertex Array Object first, then bind and set vertex buffer(s), //and then configure vertex attributes(s). glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // note that this is allowed, the call to glVertexAttribPointer registered VBO // as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind glBindBuffer(GL_ARRAY_BUFFER, 0); // You can unbind the VAO afterwards so other VAO calls won't accidentally // modify this VAO, but this rarely happens. Modifying other // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind // VAOs (nor VBOs) when it's not directly necessary. glBindVertexArray(0); // uncomment this call to draw in wireframe polygons. //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // render loop while (!glfwWindowShouldClose(window)) { // input // ----- processInput(window); // render // ------ glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // draw our first triangle glUseProgram(shaderProgram); glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to // bind it every time, but we'll do so to keep things a bit more organized glDrawArrays(GL_TRIANGLES, 0, 3); // glBindVertexArray(0); // no need to unbind it every time // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.) glfwSwapBuffers(window); glfwPollEvents(); } // optional: de-allocate all resources once they've outlived their purpose: glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); // glfw: terminate, clearing all previously allocated GLFW resources. glfwTerminate(); return 0; } //************************************************** // process all input: query GLFW whether relevant keys are pressed/released // this frame and react accordingly void processInput(GLFWwindow *window) { if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); } //******************************************************************** // glfw: whenever the window size changed (by OS or user resize) this callback function executes void framebuffer_size_callback(GLFWwindow* window, int width, int height) { // make sure the viewport matches the new window dimensions; note that width and // height will be significantly larger than specified on retina displays. glViewport(0, 0, width, height); } As you see, about 200 lines of complicated code only for a simple triangle. 
      I don't know what parts are necessary for that output. And also, what the correct order of instructions for such an output or programs is, generally. That start point is too complex for a beginner of OpenGL like me and I don't know how to make the issue solved. What are your ideas please? What is the way to figure both the code and the whole program out correctly please?
      I wish I'd read a reference that would teach me OpenGL through a step-by-step method. 
  • Popular Now