   PyOpenGL --Feasible for sprite heavy 2D game ?(yes!) VBOs and Vertex arrays
  I have been making an STG in python with pygame (SDL) for the past several months and had been disappointed in the limitations of SDL (at least pygame's SDL). Drawing ~300 sprites or more in pygame causes the frame rate to drop below 60 which is quite low for an STG which can easily have 1500 or more bullets on screen at a time. (Touhou 6 runs on my computer at 700 FPS with more than 500 shots on screen).So the natural thing was to switch to OpenGL. I've finally gotten far enough in the tutorials to be able to draw some sprites in pyOpenGL, but the performance is only marginally better than pygame's SDL. I am able to draw ~650 sprites on screen before dropping below 60 fps.So my question is, is this a problem simply with my implementation, with PyOpenGL, or just OpenGL in general? I would find it difficult to believe it is an OpenGL issue since it should be able to do thousands of textures a frame. I can show my code if need be, but its really just what's in nehe's tutorial.I would really like to be able to have 2000 texture mapped quads on screen at a time and be well below 0.017 secs (60 fps). Is this feasible in PyOpenGL? If I did the graphics in C instead would this actually increase the speed at all, since PyOpenGL is just wrapping the C functions?EDIT: I grossly underestimated how many sprites I could do in SDL. It turns out I can do 2000 at 60 fps, but theres no real way to speed this up within SDL, and I'd like this to be 4x faster (plus openGL just has more goodies).[Edited by - moonpirate on May 25, 2010 11:39:25 PM]
  Even in C++ the overhead of immediate-mode (glBegin(), glVertex(), etc) function calls can quickly become severe, and the extra processing involved in getting there from Python code can exacerbate that. To at least determine whether it's GPU work or function calls slowing you down, try putting everything into a static display list (or a VBO, but that requires more up-front work), then rendering that same display list over and over and see what framerate you get. The best solution for you may be the use of VBOs with the ARB_point_sprite extension.   ;  Hmm very interesting, thanks for the quick reply!I made a display list with glNewList etc and have been able to increase the number of sprites I can render to around 2000 at 60 fps. Optimally I'd like this to be ~4 times as fast. Would using a VBO with ARB_point_sprite increase the speed even further (I read that VBO is more dynamic which to me means slower)?EDIT: Just realized that I would have to remake the list every frame, so its really a bit slower than before. Still shows that its probably function overhead though?   ;Quote:Would using a VBO with ARB_point_sprite increase the speed even further (I read that VBO is more dynamic which to me means slower)?I don't know what "dynamic" means in this context, but VBOs, properly used, will always be much, much faster than immediate-mode calls, and will usually be faster than display lists. (It is theoretically possible for display lists to outpace VBOs, but I've never seen real-world evidence of that happening.) A 4x speedup is not at all unreasonable, but there's only one way to find out. :)   ;Quote: but there's only one way to find out. :)Indeed! Thanks for the info, I'm a complete newbie at OpenGL and wouldntve known what to try. I'll be sure to update once I get it running.;  How big are the sprites you are rendering? In my experience the fill-rate becomes a bottleneck much before the number of triangles get critical. Try making the sprite smaller and look what happens.Other thing to keep in mind is that state changes cost a lot of time, are you sorting the primitives before rendering?   ;  Point sprites perhaps? But I don't know how that could be done efficiently in Python...Is python even a good tool for such low level OpenGL implementation?   ;  Hey, would you be willing to share the code you're using? Because I've been doing similar work (adapting an SDL game to OpenGL) and I haven't managed to get anywhere near that level of performance yet. I'm looking at 15-20FPS for a mere 200 100x100-pixel sprites -- and that's with using a static display list! My script's here, for reference. I'm also an OpenGL newbie and it'd be helpful to see if there are significant differences between what I'm doing and what you're doing that could account for my slowdown. I'd be perfectly happy with 600 sprites onscreen at a time, to be quite honest.   ;Quote:Point sprites perhaps? But I don't know how that could be done efficiently in Python...Is python even a good tool for such low level OpenGL implementation?Probably not. I've only had one CS class so far so python is the only thing I'm really any good in (and scheme... but ehhhh.....). I've thought about doing all the drawing in C, but I figure I'll learn how openGL works first, then tackle trying to learn how C works. I will look into point sprites though, thanks.Quote: How big are the sprites you are rendering?For some reason the size doesn't seem to matter appreciably. 625 10x10 sprites take ~0.015 secs to render, and 625 100x100 sprites take ~0.0155 secs to render. Most of the sprites in the game are below 25x25.Quote:Original post by DerakonHey, would you be willing to share the code you're using? Because I've been doing similar work (adapting an SDL game to OpenGL) and I haven't managed to get anywhere near that level of performance yet. I'm looking at 15-20FPS for a mere 200 100x100-pixel sprites -- and that's with using a static display list! My script's here, for reference. I'm also an OpenGL newbie and it'd be helpful to see if there are significant differences between what I'm doing and what you're doing that could account for my slowdown. I'd be perfectly happy with 600 sprites onscreen at a time, to be quite honest.Sure, let me know if anythings confusing, its sort of spliced together with how my game's code works:import os, pygame, pygame.image, timeimport OpenGL, sys, traceback, mathOpenGL.ERROR_CHECKING = FalseOpenGL.ERROR_LOGGING = Falsefrom OpenGL.GL import *from OpenGL.GLU import *from pygame.locals import *import rectangleclass Admin:    def __init__(self):   self.textures = [0,0]   self.sprites = []   self.timeline = 0   self.clock = pygame.time.Clock()   admin = Admin()   class Sprite:    def __init__(self, filepath, x, y, admin):   self.x = x   self.y = y   self.admin = admin   self.imageSurface = pygame.image.load(filepath)   self.string = pygame.image.tostring(self.imageSurface, "RGBA", 1)   self.width = self.imageSurface.get_width()   self.height = self.imageSurface.get_height()   #will be used for collisions, ignore this:   self.rect = rectangle.Rectangle(x,y,self.width, self.height)   self.location = None   self.create_Sprite()   self.load_Sprite()    def create_Sprite(self):   self.location = len(self.admin.textures)   self.admin.textures.append(self.location)    def load_Sprite(self):   glBindTexture(GL_TEXTURE_2D, self.admin.textures[self.location-1])   glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, self.width, self.height, 0,  GL_RGBA, GL_UNSIGNED_BYTE, self.string);   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)    def draw(self):   glBindTexture(GL_TEXTURE_2D,self.admin.textures[self.location-1])   glLoadIdentity()   glTranslatef(self.x,self.y,0)   glBegin(GL_QUADS)   glTexCoord2f(0.0, 0.0); glVertex3f(-0.2, -0.2,  0.)   glTexCoord2f(1.0, 0.0); glVertex3f(0.2, -0.2,  0.)   glTexCoord2f(1.0, 1.0); glVertex3f(0.2,  0.2,  0.)   glTexCoord2f(0.0, 1.0); glVertex3f(-0.2,  0.2,  0.)   glEnd();   def resize((width, height)):    glViewport(0,0,width, height)    glMatrixMode(GL_PROJECTION)    glLoadIdentity()    gluOrtho2D(-6.4,6.4,-4.8,4.8)    glMatrixMode(GL_MODELVIEW)    glLoadIdentity()def init():    global index    glEnable(GL_TEXTURE_2D)    admin.sprites.append(Sprite('hat.gif',-1,-1, admin))    index = glGenLists(1)    make_list()    for x in range(25):   for y in range(25):admin.sprites.append(Sprite('small.png',x/3.0-4,y/3.0-4, admin))    glShadeModel(GL_SMOOTH)    glClearColor(0.0, 0.0, 0.0, 0.0)    glClearDepth(1.0)    glEnable(GL_DEPTH_TEST)    glDepthFunc(GL_LEQUAL)    glEnable(GL_BLEND)    glEnable(GL_ALPHA_TEST)    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)    glAlphaFunc(GL_GREATER, 0.9)    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)def make_list():    glNewList(index, GL_COMPILE)    for x in range(900):   glBindTexture(GL_TEXTURE_2D,admin.textures[0])   glLoadIdentity()   glTranslatef(1.0,1.0,0.0)#self.z)   glBegin(GL_QUADS)   glTexCoord2f(0.0, 0.0); glVertex3f(-0.5, -0.5,  0.)# Bottom Left Of The Texture and Quad   glTexCoord2f(1.0, 0.0); glVertex3f(0.5, -0.5,  0.)# Bottom Right Of The Texture and Quad   glTexCoord2f(1.0, 1.0); glVertex3f(0.5,  0.5,  0.)# Top Right Of The Texture and Quad   glTexCoord2f(0.0, 1.0); glVertex3f(-0.5,  0.5,  0.)# Top Left Of The Texture and Quad   glEnd();    glEndList()def draw():    for sprite in admin.sprites:   sprite.draw()   #theoretically this would need to be called every frame    #make_list()    #glCallList(index)def main():    video_flags = OPENGL|DOUBLEBUF    pygame.init()    surface = pygame.display.set_mode((640,480), video_flags)    resize((640,480))    init()   while True:   event = pygame.event.poll()   if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):  break glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)   t = time.clock()   draw()   pygame.display.flip()   t = time.clock() - t   admin.timeline += 1   print t, 't'try:    main()except:    traceback.print_exc()    raw_input("Please enjoy your error, then press enter to exit.")    pygame.quit()    sys.exit()UPDATE:I've converted to point sprites, since there was no reason that I could see to use full quads, and I can now render ~1100 32x32 sprites in what I believe is called "immediate-mode" (glBegin and glEnd for every sprite every frame). Getting closer, but still not as fast as I would like.Would using VBO's speed up point sprite drawing? Since there's only one vertex per sprite now I figured it might not, but I honestly know nothing about them =] (not asking for anyone to show me, just wondering if it's something I should try).[Edited by - moonpirate on May 21, 2010 9:21:57 AM];  Hm, thanks...I was able to run your code without any difficult (just swapped out your two images with my two images), and now I'm getting 15FPS from your program, which matches the 19FPS from mine. How odd.I guess that means there's something wrong with my install. How the heck do I debug that? The PyOpenGL packages build and install without any complaints, and, assuming I turn off FORWARD_COMPATIBLE_ONLY in the tests so that glHistogram is available, they also pass.   