Thread: Re: [PyOpenGL-Users] VBO help and performance
Brought to you by:
mcfletch
From: Wakefield, R. <rjw...@en...> - 2010-05-02 07:31:25
|
I finally managed to get things working with the VBOs (big thanks to Ian Mallett for the tip on the VBO class), but I have one question left. I noticed in the pyOpenGL spec that glVertexPointer (and the others) are deprecated and scheduled to be removed in the newer OpenGL versions. Is the only replacement using a GLSL shader? If not, what's the other option. I found a helpful example in the mail archive here: http://blog.gmane.org/gmane.comp.python.opengl.user/month=20090801 using that (also see vboattrib.py by clicking on '17 comments' for Josh Davis; you'll have to rename the bin file to .py), but I'm not really sure how to apply this to simple tiling. What is the quickest way to set vloc/tloc so OpenGL know that these are the vertices/textures respectively so the code below works? # main body of drawVBO in the draw loop vbo.bind() glVertexAttribPointer(vloc, 2, GL_FLOAT, False, 0, vbo) # vertex location, start of VBO glVertexAttribPointer(tloc, 2, GL_FLOAT, False, 0, vbo + (len(vbo)/2*4)) #texture location, halfway through VBO glEnableVertexAttribArray(vloc) glEnableVertexAttribArray(tloc) glDrawArrays(GL_TRIANGLES, 0, len(vbo)/2) glDisableVertexAttribArray(vloc) glDisableVertexAttribArray(tloc) vbo.unbind() For reference, the working code I use with vertex pointers is below. My earlier problem was apparently doing the glBufferData call wrong, and in case this helps anyone else the VBO class is a helpful way to outsource the job. By the way, does interleaving data offer a good performance gain over a linear VBO (list of verts, then colors, then textures)? # in the initialization; vbo3 is passed to drawVBO later. dat = verts[:] + textures[:] # a flat list works fine vbo3 = VBO(numpy.asarray(dat, 'f'), GL_STATIC_DRAW, GL_ARRAY_BUFFER) def drawVBO(texid, vbo): glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glBindTexture( GL_TEXTURE_2D, texid) vbo.bind() glEnableClientState(GL_VERTEX_ARRAY) glEnableClientState(GL_TEXTURE_COORD_ARRAY) glVertexPointer(2, GL_FLOAT, 0, vbo) glTexCoordPointer(2, GL_FLOAT, 0, vbo + (len(vbo)/2*4)) # the *4 accounts for sizeof(float) glDrawArrays(GL_TRIANGLES, 0, len(vbo)/2) glDisableClientState(GL_TEXTURE_COORD_ARRAY) glDisableClientState(GL_VERTEX_ARRAY) vbo.unbind() glFlush() glFinish() ''' I'm using pygame, so I pygame put it all on the screen; GLUT can do the same ''' pygame.display.flip() |
From: Wakefield, R. <rjw...@en...> - 2010-05-04 19:07:44
|
Now that I have a VBO working, I'm wondering what is the best way to speed up culling? My specific problem involves a 2D tilemap that is significantly larger than the screen, and the obvious solution is to calculate the indices of the on-screen vertices and use glDrawElements. I did the index array in immediate mode and there's almost no performance gain, I suspect because of CPU to GPU communication. To be clear, I have no buffer object for the indices, but the verts/textures are stored in a static VBO for the map. If I can't get a decent gain by culling to view, tests show that I'm better off using a display list. The best approach I can think of is to store the geometry data in a VBO like I do now, store the full list of Indices in another buffer object (IBO), and then have a third IBO for the data to be drawn, which will use GL_DYNAMIC_DRAW and have the appropriate memory copied from the full index with glBufferSubData calls, 1 per row of vertices (there are 20-30 rows on screen at once). Is this a good way to do things, or am I overengineering? Is there a better way to draw only the triangles appearing on-screen using some other function, perhaps glScissor? Thanks in advance! |
From: Ian M. <geo...@gm...> - 2010-05-05 00:33:55
|
If it's a 2D map, you could just render it all to a giant texture and then draw only one polygon. If stuff changes, you could update the texture or just draw another quad with the changes over your main one. |
From: Wakefield, R. <rjw...@en...> - 2010-05-07 01:17:48
|
How do you get openGL to cull offscreen triangles? In performance tests, if I don't cull myself the framerate drops proportionate to mapsize. My IBO is already contiguous by row, with (screenheight/tileheight)+1 rows used to cover the screen, the screen IBO. At each frame I make one call to glDrawElements with the screen IBO or an index array in immediate mode. Speed is about the same for either, and I suspect the issue is that I transfer the data from the CPU either way (a screen worth of indices). If I could speed this up a bit I think I'd be okay. Do you know if glBufferSubData would help? The issue I see is that I send much less net data (a few pointers and sizes instead of a full index array), but I'd also need to make ~20 or so calls per frame. I realize these issues would be lessened if I used a larger rectangle for the tile size, but that would be a last resort since it adds a lot of work on the mapping end (each permutation of 4/16/etc small tiles making up a large tile will need to be made into part of the texture atlas). ________________________________________ From: Greg Ewing [gre...@ca...] Sent: Wednesday, May 05, 2010 7:36 PM To: Wakefield, Robert Cc: pyo...@li... Subject: Re: [PyOpenGL-Users] VBO help and performance Wakefield, Robert wrote: > Now that I have a VBO working, I'm wondering what is the best way to speed up culling? First of all, have you tried just drawing the whole map with one big glDrawElements call and relying on OpenGL to cull the off-screen triangles? Unless your map is really enormous, you may find that it's fast enough. If not, I'd suggest dividing the map up into rectangles, sized so that a handful of them cover the screen at any given scrolling position. Organize your indices so that each rectangle occupies a contiguous range in the IBO. Then you can make one call for each rectangle that overlaps the screen. -- Greg |
From: Ian M. <geo...@gm...> - 2010-05-07 01:40:26
|
On Thu, May 6, 2010 at 6:17 PM, Wakefield, Robert <rjw...@en...>wrote: > The issue I see is that I send much less net data (a few pointers and sizes > instead of a full index array), but I'd also need to make ~20 or so calls > per frame. > What you'll really want to avoid is binding and unbinding VBOs as much as possible. For objects with multiple materials, my object class uses separate VBOs. For objects with around 10 materials, that's quite a few operations. I'm going to have to convert the VBOs to an interleaved VBO to raise performance. Long story short, I reiterate: try to minimize VBO binding. Ian |
From: Wakefield, R. <rjw...@en...> - 2010-05-07 02:14:55
|
Thanks Ian, that really helps. Have you ever had any issues with very small float values in pyOpenGL? I can get a good savings by bunching all backgrounds/foregrounds in 1 render call, but to do that I'll need to put a visually indistinguishable z-distance between the two middle layers (ex. 0.00002), so the player sprite can be placed between them without visual distortions. I'll try the IBO combined with that and see how it goes. ________________________________________ From: Ian Mallett [geo...@gm...] Sent: Thursday, May 06, 2010 9:30 PM To: Wakefield, Robert Cc: Greg Ewing; pyo...@li... Subject: Re: [PyOpenGL-Users] VBO help and performance On Thu, May 6, 2010 at 6:17 PM, Wakefield, Robert <rjw...@en...<mailto:rjw...@en...>> wrote: The issue I see is that I send much less net data (a few pointers and sizes instead of a full index array), but I'd also need to make ~20 or so calls per frame. What you'll really want to avoid is binding and unbinding VBOs as much as possible. For objects with multiple materials, my object class uses separate VBOs. For objects with around 10 materials, that's quite a few operations. I'm going to have to convert the VBOs to an interleaved VBO to raise performance. Long story short, I reiterate: try to minimize VBO binding. Ian |
From: Ian M. <geo...@gm...> - 2010-05-07 03:29:21
|
If your game is 2D, there will be no distortions with a Ortho matrix. Plus, you'll be able to use actual screen pixel coordinates, like PyGame, (if you use that) but it's not flipped. You can set your characters at z = 0, and the foreground and background at 1 and -1. Personally, I don't know how well OpenGL handles very small floats. My guess would be a truncation. In any case, the Z-buffer doesn't have a resolution fine enough to handle that except under very specific circumstances (like your near plane is 0.00001 and your far plane is 0.00003. I don't recommend that. The essentials of my 2D view class system, for Ortho projections. You clear screen, use .set_view(), and draw: class glLibInternal_glLibView(): def __init__(self,rect): self.rect = list(rect) self.x = rect[0] self.y = rect[1] self.width = rect[2] self.height = rect[3] self.size = [rect[2],rect[3]] class glLibView2D(glLibInternal_glLibView): def __init__(self,rect): glLibInternal_glLibView.__init__(self,rect) def set_view(self): glViewport(*self.rect) glMatrixMode(GL_PROJECTION) glLoadIdentity() gluOrtho2D(self.rect[0],self.rect[0]+self.rect[2],self.rect[1],self.rect[1]+self.rect[3]) glMatrixMode(GL_MODELVIEW) glLoadIdentity() Ian |
From: Wakefield, R. <rjw...@en...> - 2010-05-07 08:52:23
|
I'm at a loss as how to fix things, please help if you can. I just can't figure out how to get glBufferSubData to work with the built-in VBO class from GL.arrays.vbo. Without the VBO class, I can't get anything to display at all; I guess I'm missing how to do the calls. My understanding was that glBindBuffer() would cause subsequent pointer calls (ex. glVertexPointer) to work as an offset from the bound buffer, so offset 0 would be the start of the buffer. However, using the VBO class, I have to pass it to get the correct offset, e.g. vbo.bind() ... glEnableClientState(GL_VERTEX_ARRAY) glVertexPointer(2, GL_FLOAT, 0, vbo + self.voffset) # would expect '0 + self.voffset' would work, but if I do that, result is that nothing draws, same as above If I try to use glBufferData, how would I even get a pointer like this? I don't really understand what's going on behind the scenes well enough to fix it, but I've tried everything I can think of fiddling around with the VBO class and other code samples. I've attached my actual code, which I've stripped of everything that could possibly interfere. Before trying to run the code, please note: 1.) I've built the basic GUI on pygame and use numpy arrays for the VBO submission, so you'll need those libs to run 2.) the PNG should be placed in a folder 'gfx', 1 level down from the .py files 3.) the use of IBO or VBO only is controlled by the default parameter to the VBO_dat wrapper class, if useIBO is false, we use VBO only and glDrawArrays. Without the culling step, it's actually faster than the IBO version due to less glBindBuffer calls (at least on my machine). 4.) the calculation of the visible region works fine, and in fact on large maps (256*256 tiles = 4096*4096 pixels) immediate-mode performance is notably better than non-culling methods. Still slower than I'd like though. Many thanks in advance! |
From: Greg E. <gre...@ca...> - 2010-05-07 03:51:39
|
On 07/05/10 13:17, Wakefield, Robert wrote: >I suspect the issue is that I transfer the data from the CPU either way (a screen > worth of indices). Don't do that. Transfer all the indices into an IBO once, and use glDrawRangeElements to draw the subranges needed to cover the screen. -- Greg |
From: Ian M. <geo...@gm...> - 2010-05-02 16:18:28
|
On Sun, May 2, 2010 at 12:31 AM, Wakefield, Robert <rjw...@en...>wrote: > I finally managed to get things working with the VBOs (big thanks to Ian > Mallett for the tip on the VBO class), but I have one question left. I > noticed in the pyOpenGL spec that glVertexPointer (and the others) are > deprecated and scheduled to be removed in the newer OpenGL versions. I doubt it. Do you mean glVertex*f, glNormal*f, etc.? 'Cause those are scheduled to be deprecated . . . in fact they have in OpenGL 3. Ditto for display lists. > Is the only replacement using a GLSL shader? Shaders are how the geometry is drawn, not how it is defined. > If not, what's the other option. > Vertex arrays and VBOs will be the only way to draw geometry. My advice on the whole thing: don't worry about it. It will be a very long time before anything is not supported. > I found a helpful example in the mail archive here: > http://blog.gmane.org/gmane.comp.python.opengl.user/month=20090801 using > that (also see vboattrib.py by clicking on '17 comments' for Josh Davis; > you'll have to rename the bin file to .py), but I'm not really sure how to > apply this to simple tiling. What is the quickest way to set vloc/tloc so > OpenGL know that these are the vertices/textures respectively so the code > below works? > > # main body of drawVBO in the draw loop > vbo.bind() > glVertexAttribPointer(vloc, 2, GL_FLOAT, False, 0, vbo) # vertex > location, start of VBO > glVertexAttribPointer(tloc, 2, GL_FLOAT, False, 0, vbo + (len(vbo)/2*4)) > #texture location, halfway through VBO > glEnableVertexAttribArray(vloc) > glEnableVertexAttribArray(tloc) > glDrawArrays(GL_TRIANGLES, 0, len(vbo)/2) > glDisableVertexAttribArray(vloc) > glDisableVertexAttribArray(tloc) > vbo.unbind() > > For reference, the working code I use with vertex pointers is below. My > earlier problem was apparently doing the glBufferData call wrong, and in > case this helps anyone else the VBO class is a helpful way to outsource the > job. By the way, does interleaving data offer a good performance gain over > a linear VBO (list of verts, then colors, then textures)? > Probably. Might not be too significant right now. I'd worry about optimizing it later. From experience, it's only when you have several hundred VBOs that you're binding/unbinding that it becomes a problem. > > # in the initialization; vbo3 is passed to drawVBO later. > dat = verts[:] + textures[:] # a flat list works fine > vbo3 = VBO(numpy.asarray(dat, 'f'), GL_STATIC_DRAW, GL_ARRAY_BUFFER) > > def drawVBO(texid, vbo): > glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) > glBindTexture( GL_TEXTURE_2D, texid) > > vbo.bind() > glEnableClientState(GL_VERTEX_ARRAY) > glEnableClientState(GL_TEXTURE_COORD_ARRAY) > glVertexPointer(2, GL_FLOAT, 0, vbo) > glTexCoordPointer(2, GL_FLOAT, 0, vbo + (len(vbo)/2*4)) # the *4 > accounts for sizeof(float) > glDrawArrays(GL_TRIANGLES, 0, len(vbo)/2) > glDisableClientState(GL_TEXTURE_COORD_ARRAY) > glDisableClientState(GL_VERTEX_ARRAY) > vbo.unbind() > > glFlush() > glFinish() > ''' I'm using pygame, so I pygame put it all on the screen; GLUT can do > the same ''' > pygame.display.flip() > Ian |