This is not the document you are looking for? Use the search form below to find more!

Report home > Others

Modern OpenGL Usage: Using Vertex Buffer Objects Well

0.00 (0 votes)
Document Description
Modern OpenGL Usage: Using Vertex Buffer Objects Well
File Details
  • Added: April, 14th 2011
  • Reads: 333
  • Downloads: 15
  • File size: 109.97kb
  • Pages: 19
  • Tags: vetex buffer objects, immediate mode, opengl
  • content preview
Submitter
  • Name: martyna

We are unable to create an online viewer for this document. Please download the document instead.

Modern OpenGL Usage: Using Vertex Buffer Objects Well screenshot

Add New Comment




Related Documents

Fourier Series

by: tutorvistateam_team, 4 pages

Fourier Series are the two major topics that we are going to discuss here. So let me tell you about Fourier series first which was explicated by Jean – Baptiste Fourier. He gave the idea by ...

Jaypee Greens Krescent Homes Sector 129 Noida | +919560214267

by: aliva08, 8 pages

Call Arun @ +919560214267. Jaypee Greens Krescent Homes, Sec-129, Noida, The Magical City in Noida! Krescent Homes brings to you a delightful living with outstanding architecture, spacious modern ...

Critical Regionalism

by: david, 9 pages

CRITICAL REGIONALISM Critical regionalism An approach to architecture that strives to counter the placelessness and lack of meaning in Modern Architecture by using ...

Good Tips on Buying Dining Room Furniture

by: sandyhodge37, 2 pages

The dining room is one of the most important places in your home. You can have a good dining room that is exquisitely designed and positioned but you may be let down by the dinning set if you are not ...

SEO Services - How to Choose the Best One

by: dwightmontgo24, 1 pages

Owning a company that has not made any good progress since it was first time launched in the public can be a very stressful thing to be handled by the owner of the company. There are a lot of ...

SEO Services - How to Choose the Best One

by: dwightmontgo24, 1 pages

Owning a company that has not made any good progress since it was first time launched in the public can be a very stressful thing to be handled by the owner of the company. There are a lot of ...

Choosing a Dedicated Hosting Service

by: bennettdurha923, 1 pages

Dedicated hosting services are also referred to as managed hosting services or dedicated servers. These are kinds of World wide web hosting where the client rents a server from a internet hosting ...

How to Get Hold of Condoms

by: elijahsanche17, 1 pages

With sexual education what it is today, the days of making excuses for not carrying or using contraception should arguably long be over, especially seeing as getting hold of a condom seems greatly ...

How to Get Hold of Condoms

by: elijahsanche17, 1 pages

With sexual education what it is today, the days of making excuses for not carrying or using contraception should arguably long be over, especially seeing as getting hold of a condom seems greatly ...

Laser Acne Treatment Los Angeles

by: katherine Moore, 12 pages

Acne is common to people in modern society and can cause pain as well as embarrassment to individuals living with the disease. In Los Angeles, there is a variety of acne treatment programs provided ...

Content Preview
Modern OpenGL Usage: Using Vertex Buffer Objects Well Mark J. Kilgard NVIDIA Corporation Austin, Texas September 9, 2008 1 Introduction This whitepaper is a follow-up to an article titled Avoiding 16 Common OpenGL Pitfalls written back in 19981 for a course on OpenGL game development at the (then-named) Computer Game Developers Conference, now known as the Game Developer Conference (GDC). This new whitepaper focuses on what to doinstead of what to avoid—to get the most out of modern OpenGL implementations. OpenGL’s functionality has evolved remarkably since 1998. Back then, the OpenGL standard was just on the verge of supporting multi-texturing. Today’s GPUs shade vertices, primitives, and fragments all in 32-bit floating-point. OpenGL implementations available today expose all the features present in DirectX 10-class GPUs such as the GeForce 8. Back in 1998, I found OpenGL had a lot of programming pitfalls that novices stumbled over repeatedly. A decade later, there are thousands of experienced OpenGL programmers who know how to avoid OpenGL’s pitfalls, but these programmers still don’t always know the best ways to use OpenGL to get the best performance and quality from their GPU. So rather than focus on API pitfalls (I do mention a few along the way…), this whitepaper concentrates what to do to maximize your OpenGL application’s vertex transformation rate. The approach discussed is primarily through using vertex buffer objects efficiently and optimizing vertex index orders. 2 Maximizing Vertex Processing Rates To sustain the vertex transformation and primitive assembly rates of modern GPUs, high-performance OpenGL applications should store vertex attributes in vertex buffer objects and then assemble vertices by specifying vertex array indices. Vertex arrays stored in vertex buffer objects are in contrast to OpenGL’s original so-called immediate mode API 1 In an update in 2000, the original pitfall article was expanded from 16 to 19 pitfalls. Download the PDF from http://developer.nvidia.com/object/Avoiding_Common_ogl_Pitfalls.html 1 where vertex attributes such as colors, texture coordinate sets, and positions are specified one function call at a time using glColor4f, glTexCoord2fv, glVertex3f, etc. Vertex buffer objects have been part of core OpenGL since version 1.5. If you’ve not switched your application over to using vertex buffers, now is the time. Hopefully most OpenGL applications (surely yours, right?) have already switched over to vertex buffers for bulk vertex rendering. Still all advice should be taken in moderation. While sourcing vertex arrays stored in vertex buffers is crucial for attaining a GPU’s fastest vertex processing rates, the notion that an OpenGL application is “wrong” to ever use immediate mode is overzealous. The OpenGL 3.0 specification has even gone so far as to mark immediate mode in OpenGL for “deprecation” (whatever that means!); such extremism is counter-productive and foolish. The right way to encourage good API usage isn’t to try to deprecate or ban API usage, but rather educate developers about the right API usage for particular situations. The truth is that modern OpenGL implementations are highly tuned at processing immediate mode; there are many simple situations where immediate mode is more convenient and less overhead than configuring and using vertex arrays with buffer objects. 2.1 Immediate Mode versus Vertex Arrays If you are sending tens of thousands of vertices to render a complex model, sure, it certainly makes sense to use vertex arrays stored in vertex buffers. But if part of what your application does is interactively stretching a single, large shaded rectangle over the screen, there’s nothing wrong with immediate mode to render such a rectangle. Indeed, because immediate mode doesn’t require extra commands to enable and specify vertex arrays and there’s no coding to create, initialize, and bind vertex buffer objects, immediate mode winds up being easier to code, easier to debug, and roughly as fast—potentially faster—in rendering situations where you are rendering primitives with just a few vertices. Likewise, immediate mode commands can also be compiled into very efficient display lists which can surpass the performance of even carefully used vertex arrays in vertex buffer objects. 3 Start with Immediate Mode Let’s review how to use vertex arrays with vertex buffer objects. First let’s consider an immediate mode routine to draw a list of rectangles: 2 typedef struct { GLfloat x, y, width, height; GLfloat depth_order; GLfloat left_side_color[3]; // red, green, then blue GLfloat right_side_color[3]; // red, green, then blue } RectInfo; void drawRectangles(int count, const RectInfo *list) { glBegin(GL_QUADS); for (int i=0; i<count; i++) { const RectInfo *r = &list[i]; glColor3fv(r->left_side_color); glVertex3f(r->x, r->y, r->depth_order); glColor3fv(r->right_side_color); glVertex3f(r->x+r->width, r->y, r->depth_order); // right_side_color “sticks” glVertex3f(r->x+r->width, r->y+r->height, r->depth_order); glColor3fv(r->left_side_color); glVertex3f(r->x, r->y+r->height, r->depth_order); } glEnd(); } As shown in Figure 1, each rectangle has an (x,y) anchor position, a depth order (used as the rectangle’s depth for depth testing), width and height dimensions, and a left- and right-side color. The drawRectangles routine iterates over all the rectangles, drawing each as a quadrilateral (quad for short) with the appropriate left- and right-side colors assigned. left side color1.0right side colordepth orderheight0.0(x,y)width Figure 1: Rectangle with its parameters. 3 4 Convert to Conventional Vertex Arrays Let’s first convert the drawRectangle function to use vertex arrays instead of immediate mode commands. 4.1 Initializing Vertex Array Memory The initVarrayRectangles routine below allocates a buffer of system memory and initializes the memory with the same pattern of RGB and (x,y,z) vertex attributes as the immediate mode routine sends to OpenGL. void *initVarrayRectangles(int count, const RectInfo *list) { void *varray = (char*) malloc(sizeof(GLfloat)*6*4*count); GLfloat *p = varray; for (int i=0; i<count; i++, p+=24) { const RectInfo *r = &list[i]; // quad vertex #1 memcpy(&p[0], r->left_side_color, sizeof(GLfloat)*3); p[3] = r->x; p[4] = r->y; p[5] = r->depth_order; // quad vertex #2 memcpy(&p[6], r->right_side_color, sizeof(GLfloat)*3); p[9] = r->x+r->width; p[10] = r->y; p[11] = r->depth_order; // quad vertex #3 memcpy(&p[12], r->right_side_color, sizeof(GLfloat)*3); p[15] = r->x+r->width; p[16] = r->y+r->height; p[17] = r->depth_order; // quad vertex #4 memcpy(&p[18], r-> left_side_color, sizeof(GLfloat)*3); p[21] = r->x; p[22] = r->y+r->height; p[23] = r->depth_order; } return varray; } The system memory used to store the vertex arrays generated vertex attributes is just regular memory allocated from your process heap by malloc. This is memory belongs to your application’s process and is part of the process’s address space. The CPU can read and write this memory; but the GPU does not have unfettered access to your application’s address space and memory. When your application calls OpenGL commands, the OpenGL library passes data within your address space on to the GPU. The designers of OpenGL think of OpenGL as a sort of service for accessing the 3D rendering and imaging capabilities of graphics hardware. This is why OpenGL’s designers describe OpenGL as having client-server architecture. The client is your application and the server is the combination of the OpenGL library your application links with, an OpenGL driver residing in some combination of the operating system kernel and the window system, and the GPU. For this reason, the designers of OpenGL refer to your application’s address space and all the memory within it as client-memory. This memory includes your application’s global variables, its heap for dynamic memory allocations, its stack, and anything else mapped into its address space. By design, OpenGL accesses your application’s client memory only in direct response to your 4 application’s calling OpenGL commands and queries. Why I belabor this point will be clearer later and help you better appreciate the role of buffer objects in modern OpenGL programming. 4.2 Vertex Array Layout Figure 2 shows the layout of vertices initVarrayRectangles creates in memory. Each vertex requires 24 bytes (12 for color, 12 for position) so each independent quad requires 96 bytes of vertex attributes. float = 4 bytesredgreencolorbluevertex 0xypositionzredgreencolorbluevertex 1xypositionz Figure 2: Vertex array layout for rectangle list. Color and vertex attributes are interleaved. 4.3 Rendering with Conventional Vertex Arrays Now we can re-implement the drawRectangles routine so it initializes a system memory buffer with the appropriate vertex array data for the rectangles, configures OpenGL’s vertex array state to source the vertex attributes, and then draws the arrays: void drawVarrayRectangles(int count, const RectInfo *list) { char *varray = initVarrayRectangles(count, list); const GLfloat *p = (const GLfloat*) varray; const GLsizei stride = sizeof(GLfloat)*6; // 3 RGB floats, 3 XYZ floats glColorPointer(/*rgb*/3, GL_FLOAT, stride, p+0); glVertexPointer(/*xyz*/3, GL_FLOAT, stride, p+3); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_QUADS, /*firstIndex*/0, /*indexCount*/count*4); free(varray); } 5 Comparing the vertex array version to the immediate mode version, you’ll notice the vertex array version must allocate memory to store the entire set of rectangle vertex attributes. In contrast, the immediate mode version performs no memory allocation or de-allocation. If drawRectangles was used to render a just one rectangle, the immediate mode version is arguably more efficient since it would call 9 fast, simple OpenGL commands whereas the vertex array version calls 5 OpenGL state-configuration commands that involve reconfiguring and revalidating internal OpenGL state. This extra work is more involved than simply feeding vertex attributes, and then still has the burden of allocating, initializing, and de-allocating the vertex array memory. The point is that when we render enough rectangles—perhaps a dozen or more—the extra vertex array setup overhead is easily amortized over all the rectangles being processed. Something else not shown in the vertex array version of drawRectangles is that we are assuming that no other vertex arrays (for normal, texture coordinate sets, etc.) have been enabled. If so, we’d like to make additional glDisableClientState calls to disable these arrays. Using vertex arrays is more complicated because there’s a substantial amount of flexible state to first configure correctly. The immediate mode code has the benefit of being simpler, both to read and debug, if there are any problems. 4.4 Repeated Rendering from Vertex Arrays When we are rendering hundreds of rectangles, it’s no contest that the vertex array code is more efficient. When rendering hundreds of rectangles, amortizing the vertex array configuration overhead pays off well over all the rendered rectangles. The extra complexity of building the vertex buffer is justified by the more efficient communication of vertex attributes to OpenGL through a vertex array than a function call per vertex attribute with immediate mode. In the common situation where we render the same set of rectangles repeatedly, say once every window refresh, we can build the vertex array once with initVarrayRectangles and have a faster version of drawRectangles that expects the vertex array to be allocated and initialized. void drawInitializedVarrayRectangles(int count, const void *varray) { const GLfloat *p = (const GLfloat*) varray; const GLsizei stride = sizeof(GLfloat)*6; // 3 RGB floats, 3 XYZ floats glColorPointer(/*rgb*/3, GL_FLOAT, stride, p+0); glVertexPointer(/*xyz*/3, GL_FLOAT, stride, p+3); // Assume GL_COLOR_ARRAY and GL_VERTEX_ARRAY are already enabled! glDrawArrays(GL_QUADS, /*firstIndex*/0, /*indexCount*/count*4); } In the drawInitializedVarrayRectangles version of drawVarrayRectangles, we assume the varray parameter to be pre-initialized by a prior call to initVarrayRectangles. We also assume drawPreInitRectangles is not responsible for de-allocating varray. We also assume that the color and vertex arrays are already enabled. 6 5 Motivation for Vertex Buffer Objects So far, we are sourcing vertex attributes through vertex arrays allocated in conventional system memory. The problem with conventional system memory is that the GPU doesn’t have arbitrary access to conventional system memory—remember this is the memory OpenGL designers call client-memory. So the OpenGL library must spoon-feed all the vertex attributes from system memory to the GPU when glDrawArrays is called. Rather than spoon-feeding the GPU, we’d really like to simply point the GPU at the vertex arrays and let the GPU read the vertex attributes from these arrays without the CPU having to be involved at all. This is hard with client-memory for a couple of reasons. First, the GPU runs asynchronously from the CPU running your application so there’s no easy way to arbitrate consistent access to your application’s address space and memory. Second, the GPU doesn’t, in general, have access to your application’s address space for lots of reasons including keeping the overall system operating robustly. The solution to this dilemma is storing vertex arrays in specially negotiated regions of memory maintained as OpenGL buffer objects that both your application and the OpenGL implementation can access and the OpenGL API can arbitrate a policy for consistent access to this memory. 5.1 GPU-accessible Buffer Objects With buffer objects, OpenGL can allow the GPU to source the vertex attributes directly where the GPU uses Direct Memory Access (DMA) to initiate the required memory read requests. Such DMA transfers are much more efficient than spoon-feeding vertex attributes in vertex arrays from conventional system memory to the GPU because the CPU is not involved in these DMA transfers. OpenGL allows such DMA transfers of vertex attributes when vertex array data is stored in what OpenGL calls a buffer object. Each buffer object in OpenGL is a range of contiguous untyped memory where the OpenGL implementation is allowed to configure the memory for use by the GPU. Both the GPU and CPU have access to the memory within a buffer object though OpenGL restricts how updates to the memory occur. There are commands to copy data to a buffer object and query data from a buffer object as well. An application can also map a buffer object into its address space (so-called client-memory from the OpenGL driver’s perspective), but a buffer object must be unmapped before the GPU can use it. Figure 3 and Figure 4 show the way vertex and command data flows when vertex arrays are sourced through client-memory and buffers objects respectively. 7 vertexapplicationdata travelscommand queueGPU transfer of(client)throughcommand + vertexmemoryCPUdataCPU writes ofcommand + vertexcommanddataprocessorvertexCPUvertexarraypullerhardwarerenderingmemorypipelinereadsCPUGPU Figure 3: Client-memory vertex array operation. All the vertex data typically ends up moving through the CPU in this mode of operation. applicationcommand queueGPU DMA transfer of(client)command datamemoryCPU writes ofcommand + vertexcommanddataprocessorCentralvertexProcessorpullerhardwarerenderingmemoryOpenGLpipelinereads(vertex)bufferGraphicsobjectProcessorvertexGPU DMA transferarrayof vertex data—CPU never touches data Figure 4: Client-memory vertex array operation. All the vertex data typically ends up moving through the CPU in this mode of operation. Calling the memory in a buffer object “untyped” means that the memory itself has no particular layout or format associated with it. Your application can place arbitrary words or bytes of memory within the buffer object. 5.2 Specifying Vertex Layout with a Buffer Object How the memory is interpreted depends on how OpenGL is told to access the memory. When you configure vertex arrays with the glVertexPointer, glColorPointer, etc. commands, your application is effectively telling OpenGL how to interpret the (otherwise untyped) data in a buffer object. 8 A buffer objects used to store vertex arrays is referred to as a Vertex Buffer Object or VBO for short. Buffer objects can be used for other purposes as well. For example, you can read or write pixel data from a buffer object. In this case, the buffer object is referred to as a Pixel Buffer Object or PBO for short. 5.3 Generic Nature of Buffer Objects While the terms VBO and PBO are handy jargon, please understand that there is nothing vertex-specific or pixel-specific about a given buffer object. A buffer object is described as a VBO if it is used to store vertex arrays; a buffer object is described as a PBO if it is used to read and write pixel data. When you create a buffer object, there’s nothing that forces a buffer object to be used only for pixels or only for vertices. In fact, a buffer object is just untyped memory that can be used for whatever usage OpenGL allows. Indeed, the ability to use an OpenGL buffer object any way you please is what makes buffer objects so powerful. Indeed, OpenGL 3.0 adds the ability to stream the results of vertex transformation into a buffer object. Other broadly-supported OpenGL extensions allow you to treat a buffer object as a special 1D texture object (the EXT_texture_buffer_object extension) or source parameters for an assembly shader directly from a buffer object (the NV_parameter_buffer_object extension), or source uniform values for a GLSL shader directly from a buffer object (the EXT_bindable_uniform extension). So when you use the terms VBO and PBO (or XBO, PaBO, BuBO for transform, parameter, and bindable uniform buffers respectively), keep in mind that you are describing your usage of the buffer but that description doesn’t require the buffer object to be used in that way only. A buffer object in OpenGL is a logically just a bucket of bytes (technically called the buffer’s data store) that the GPU knows how to directly access. A buffer object can be used as a VBO, PBO, XBO, PaBO, and BuBO and that may dictate how the bytes will be interpreted but the buffer’s data store itself is just bytes. Ultimately this makes buffers a very powerful, efficient, and central mechanism for transferring and converting data within OpenGL. 6 Using Vertex Buffer Objects Using vertex buffer objects is not much harder than using vertex arrays. The primary difference is the vertex attributes must be stored in the data store of a buffer object rather the client memory. 6.1 Vertex Buffer Initialization The API for establishing a buffer object is simple enough. Buffer objects are named by 32-bit values of type GLuint (like textures, display lists, and other OpenGL objects). The glBindBuffer command binds a named object to a buffer binding. GL_ARRAY_BUFFER is the buffer binding used for vertex arrays. If you bind a buffer name that has not been used before, the name is initialized to a zero-length buffer. Once bound to a buffer, the 9 glBufferData command initializes the currently bound buffer for the specified buffer binding with a specified number of bytes of data. The initial data is copied into the buffer object. The initVarrayRectanglesInVBO initializes a buffer object named bufferName to contain the vertex attributes for the rectangles. void initVarrayRectanglesInVBO(GLuint bufferName, int count, const RectInfo *list) { char *varray = initVarrayRectangles(count, list); const GLsizei stride = sizeof(GLfloat)*6; // 3 RGB floats, 3 XYZ floats const GLint numVertices = 4*count; const GLsizeiptr bufferSize = stride*numVertices; glBindBuffer(GL_ARRAY_BUFFER, bufferName); glBufferData(GL_ARRAY_BUFFER, bufferSize, varray, GL_STATIC_DRAW); free(varray); } Because the data is copied into the buffer object, initVarrayRectanglesInVBO can free the data allocated by initVarrayRectangle after calling glBufferData. The GL_STATIC_DRAW usage parameter to glBufferData indicates how the buffer is expected to be used. The static-draw usage means that the buffer’s contents are expected to be static and the data itself will be used for drawing operations (rather than reading or copying). Static usage means the data is expected to remain unchanged. Dynamic usage means random-access updates to the buffer’s data are likely. Stream usage means the data with in the buffer will be used a few times and then replaced with new data and this process will repeat. Buffer objects can be used in lots of different ways and their usage pattern may change with time. Setting the usage parameter reasonably when the buffer is first created assists the OpenGL implementation to provide a good initial placement, but the nine different usage parameters (the combinations of draw, read, and copy with static, dynamic, and stream) don’t capture all varied ways buffer objects can be used. The usage parameter is really an initial seed for how the buffer object will be used. You can expect that the OpenGL implementation will allocate the buffer to maximize performance for the buffer’s actual usage based on how the buffer is actually observed to be used. 10 Document Outline
  • ÿ
  • ÿ
    • ÿ
    • ÿ
  • ÿ
    • ÿ
  • ÿ
    • ÿ
      • ÿ
    • ÿ
      • ÿ
  • ÿ
    • ÿ
      • ÿ
      • ÿ
  • ÿ

Download
Modern OpenGL Usage: Using Vertex Buffer Objects Well

 

 

Your download will begin in a moment.
If it doesn't, click here to try again.

Share Modern OpenGL Usage: Using Vertex Buffer Objects Well to:

Insert your wordpress URL:

example:

http://myblog.wordpress.com/
or
http://myblog.com/

Share Modern OpenGL Usage: Using Vertex Buffer Objects Well as:

From:

To:

Share Modern OpenGL Usage: Using Vertex Buffer Objects Well.

Enter two words as shown below. If you cannot read the words, click the refresh icon.

loading

Share Modern OpenGL Usage: Using Vertex Buffer Objects Well as:

Copy html code above and paste to your web page.

loading