I'll explain what I'm doing before I get to the exact problem.

I've been making a 2D game in python. Originally I was making it in pygame but I faced a problem. To prevent a very low frame rate I either had to reduce the screen dimensions and remove features or use another library instead. I'm converting my game to use OpenGL instead. Easier said than done it seems.

I have made a module which manages the screen and graphics but also contains a class called Surface which acts like the pygame surfaces. I then have functions for loading images and converting pygame things to my Surface class objects. I have a function for adding a line to the screen and another for a line strip.

Now the problem I would like to alleviate is that during a minigame in the game, which involves lots of lines, the game slows down over time despite the number of lines being rendered staying reasonably stable. To clean the contents of my surface objects I must use the fill method. This is done and that isn't the problem.

Please don't waste your time if you have to think about it too much but I'd very much appreciate any help.

This is the module which does have some parts incomplete or unconverted which you can just ignore.

Edit: It may be a problem with the fonts instead. I'm not sure but there isn't a reason for why the problem should be with the lines.

#!/usr/bin/env python2.3
#
#  Automatic Game Scaling and 2D surface library for pygame and OpenGL
#
#  Allows resize of a Window while scaling the game, keeping the aspect ratio.
#
#  Created by Matthew Mitchell on 13/09/2009.
#  Copyright (c) 2009 Matthew Mitchell. All rights reserved.
#
#Import modules
import sys,os
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
MUSICEND = USEREVENT
def get_resolution(screen,ss,gs): 
	gap = float(gs[0]) / float(gs[1])
	sap = float(ss[0]) / float(ss[1])
	if gap > sap:
		#Game aspect ratio is greater than screen (wider) so scale width
		factor = float(gs[0]) /float(ss[0])
		new_h = gs[1]/factor #Divides the height by the factor which the width changes so the aspect ratio remians the same.
		game_scaled = (ss[0],new_h)
	elif gap < sap:
		#Game aspect ratio is less than the screens.
		factor = float(gs[1]) /float(ss[1])
		new_w = gs[0]/factor #Divides the width by the factor which the height changes so the aspect ratio remians the same.
		game_scaled = (new_w,ss[1])
	else:
		game_scaled = screen.get_size()
	return game_scaled
def sound(f):
	if len(sys.argv) > 1:
		if sys.argv[1] == "-m":
			return DummySound()
	return pygame.mixer.Sound(os.path.dirname(sys.argv[0]) + f)
def play_music(f):
	if f != "":
		mute = False
		if len(sys.argv) > 1:
			if sys.argv[1] == "-m":
				mute = True
		if not mute:
			global music_stop
			music_stop = False
			pygame.mixer.music.load(os.path.dirname(sys.argv[0]) + f)
			pygame.mixer.music.play(0)
			pygame.mixer.music.set_endevent(MUSICEND)
def draw_texture( (x,y), (w,h), texture,colour,data,size):
	#(x,y) is position, (w,h) is scaled size
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity() #Loads model matrix
	glBindTexture(GL_TEXTURE_2D, texture) #Binds the current 2D texture to the texture to be drawn
	if data != None:
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) #Required to be set for maping the pixel data
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) #Similar as above
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA,GL_UNSIGNED_BYTE, data) #Put surface pixel data into texture
		glColor4fv((1,1,1,1))
	else:
		glColor4fv(colour)
	glBegin(GL_QUADS) #Begin the Quadrilateral
	glTexCoord2f(0.0, 0.0) ; glVertex2f(x, y) #Top Left
	glTexCoord2f(0.0, 1.0) ; glVertex2f(x, y+h) #Bottom Left
	glTexCoord2f(1.0, 1.0) ; glVertex2f(x+w, y+h) #Bottom, Right
	glTexCoord2f(1.0, 0.0) ; glVertex2f(x+w, y) #Top, Right
	glEnd() #End the Quadrilateral
def open_image(path):
	img = pygame.image.load(path)
	surf = Surface(img.get_size())
	surf.data = pygame.image.tostring(img, "RGBA")
	return surf
def draw_line(surface,a,b,c,w):
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity() #Loads model matrix
	offset = surface.get_offset()
	glDisable(GL_LINE_SMOOTH)
	glDisable(GL_TEXTURE_2D) #Diable textures temporarily.
	glColor4fv(c) #Set Colour
	glLineWidth(w) #Set width
	glBegin(GL_LINES) #Begin the line
	glVertex2i(a[0] + offset[0],a[1] + offset[1]) #Point A
	glVertex2i(b[0] + offset[0],b[1] + offset[1]) #Point B
	glEnd()
	glEnable(GL_TEXTURE_2D)
	glEnable(GL_LINE_SMOOTH)
def draw_lines(surface,coordinates,c,w):
	glMatrixMode(GL_MODELVIEW)
	glLoadIdentity() #Loads model matrix
	offset = surface.get_offset()
	glDisable(GL_TEXTURE_2D) #Diable textures temporarily.
	glColor4fv(c) #Set Colour
	glLineWidth(w) #Set width
	glBegin(GL_LINE_STRIP) #Begin the line
	for c in coordinates: #Loop though the coordinates and add them as vertices.
		glVertex2i(c[0] + offset[0],c[1] + offset[1])
	glEnd()
	glEnable(GL_TEXTURE_2D)
def add_line(surface,c,a,b,w = 1):
	c = [float(sc)/255 for sc in c] #Divide colours by 255 because OpenGL uses 0-1
	if len(c) != 4:
		c.append(1) #Add a value for aplha transparency if needed
	surface.children.append([1,[surface,a,b,c,w]]) #1 states it is a line
def add_lines(surface,c,coordinates,w =1):
	c = [float(sc)/255 for sc in c] #Divide colours by 255 because OpenGL uses 0-1
	if len(c) != 4:
		c.append(1) #Add a value for aplha transparency if needed
	surface.children.append([1,[surface,coordinates,c,w]]) #1 states it is a line
def font_convert(font):
	surf = Surface(font.get_size())
	surf.data = pygame.image.tostring(font, "RGBA")
	return surf
class DummySound():
	def play(self):
		pass
	def set_volume(self):
		pass
class Surface():
	def __init__(self,size,extra = None):
		self.__offset = (0,0)
		self.children = []
		self.blitted = False
		self.last_offset = (0,0)
		self.size = size
		self.colour = (0,0,0,0)
		self.data = None
	def blit(self,surface,offset,area=None, special_flags = 0):
		try:
			offset_before_last = surface.last_offset
			surface.last_offset = (offset[0] + self.__offset[0],offset[1] + self.__offset[1])
			self.children.append([0,surface]) #0 states it is a surface
			if surface.get_offset() != surface.last_offset or not surface.blitted:
				surface.__set_offset(surface.last_offset)
				self.__recursive_offset_add(surface,offset_before_last,surface.last_offset) #Add to the children's offsets
			surface.blitted = True
		except AttributeError:
			pass
	def __recursive_offset_add(self,surface,offset_before_last,last_offset):
		for child in surface.children:
			try:
				child.__set_offset((child.get_offset()[0] - offset_before_last[0] + last_offset[0],child.get_offset()[1] - offset_before_last[1]  + last_offset[1]))
				self.__recursive_offset_add(child,offset_before_last,last_offset)
			except AttributeError:
				pass
	def get_offset(self):
		return self.__offset
	def __set_offset(self,offset):
		self.__offset = offset
	def fill(self,colour):
		colour = [float(b)/255 for b in colour]
		if len(colour) < 4:
			colour.append(1)
		self.children = []
		self.colour = colour
	def get_size(self):
		return self.size
	def get_width(self):
		return self.size[0]
	def get_height(self):
		return self.size[1]
class ScaledScreen(Surface):
	game_size = None
	first_screen = None
	screen = None
	fs = False #Fullscreen false to start
	clock = None
	resize = True
	game_gap = None
	game_scaled = None
	title = None
	fps = -1
	enter_fullscreen = False
	exit_fullscreen = False
	scale_to_screen = False
	iconify = False
	on_focus_fullscreen = False
	f_key = False
	def __init__(self,title,game_size,on_exit):
		pygame.init()
		self.title = title
		self.game_size = game_size
		screen_info = pygame.display.Info() #Required to set a good resolution for the game screen
		self.first_screen = (screen_info.current_w, screen_info.current_h - 120) #Take 120 pixels from the height because the menu bar, window bar and dock takes space
		pygame.display.set_caption(self.title)
		self.clock = pygame.time.Clock()
		self.game_gap = (0,0)
		self.on_exit = on_exit
		self.mod_key = 1024 if sys.platform == "darwin" else 64
		pygame.display.set_mode(self.first_screen,RESIZABLE|DOUBLEBUF|OPENGL)
		#OpenGL Parts
		Surface.__init__(self,game_size)
		self.textures = []
	def update(self,events):
		#Updates screen properly
		win_size_done = False #Changes to True if the window size is got by the VIDEORESIZE event below
		for event in events:
			if event.type == QUIT:
				self.on_exit()
			if event.type == VIDEORESIZE:
				ss = [event.w,event.h]
				self.resize = True
				win_size_done = True
		keys = pygame.key.get_pressed() #Get the pressed keys
		if pygame.key.get_mods() & self.mod_key:
			if(keys[K_q] or keys[K_w]):
				self.on_exit()
			if keys[K_f]:
				if self.f_key == False:
					self.f_key = True
					if self.fs == False:
						self.enter_fullscreen = True
					else:
						self.exit_fullscreen = True
			else:
				self.f_key = False
		if self.on_focus_fullscreen and pygame.display.get_active():
			self.on_focus_fullscreen = False
			self.enter_fullscreen = True
		if self.enter_fullscreen:
			pygame.mouse.set_visible(False)
			self.screen = pygame.display.set_mode((self.first_screen[0],self.first_screen[1]+ 120))
			if self.scale_to_screen:
				self.game_scaled = (self.screen.get_width(),self.screen.get_height())
			else:
				self.game_scaled = get_resolution(self.screen,(self.screen.get_width(),self.screen.get_height()),self.game_size)
			self.game_gap = [(self.screen.get_width() - self.game_scaled[0])/2,(self.screen.get_height() - self.game_scaled[1])/2]
			pygame.display.set_mode((0,0), FULLSCREEN|HWSURFACE|DOUBLEBUF|OPENGL)
			self.fs = True
			self.enter_fullscreen = False
			self.resize = False
		elif self.exit_fullscreen:
			pygame.mouse.set_visible(True)
			pygame.display.set_mode(self.first_screen,RESIZABLE|DOUBLEBUF|OPENGL)
			self.fs = False
			self.resize = True
			self.game_gap = (0,0)
			self.exit_fullscreen = False
			if self.iconify:
				self.on_focus_fullscreen = True
		#Scale game to screen resolution, keeping aspect ratio
		if self.resize:
			if(win_size_done == False): #Sizes not gotten by resize event
				ss = [self.screen.get_width(),self.screen.get_height()]
			self.game_scaled = get_resolution(self.screen,ss,self.game_size)
			pygame.display.set_mode(self.game_scaled,RESIZABLE|DOUBLEBUF|OPENGL)
			self.resize = False #Next time do not scale unless resize or fullscreen events occur
		if self.iconify:
			pygame.display.iconify() #Minimise
			self.iconify = False
		#Open GL Screen setup
		glViewport(0,0,self.game_scaled[0],self.game_scaled[1]) #Creates the viewport which is mapped to the window
		glEnable(GL_LINE_SMOOTH,GL_FASTEST) #Create antialiased lines
		glEnable(GL_BLEND) #Enable alpha blending
		glEnable(GL_TEXTURE_2D) #Enable 2D Textures
		glDisable(GL_DEPTH_TEST) #Disable depth
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) #The recomended blending functions.
		glMatrixMode(GL_PROJECTION)
		glLoadIdentity() #Load the projection matrix
		gluOrtho2D(0,1280,720,0) #Set an orthorgraphic view
		self.draw_game([0,self],(0,0))
		pygame.display.flip() #Flip buffer
		glDeleteTextures(self.textures) #Remove the redundant texture data from memory.
		self.children = []
		self.clock.tick(60)
		self.fps = self.clock.get_fps()
		pygame.display.set_caption(self.title + " - " + str(int(self.fps)) + "fps")
		glClear(GL_COLOR_BUFFER_BIT)
	def draw_game(self,surface,parent_offset):
		if surface[0] == 0:
			offset = surface[1].get_offset()
			size = surface[1].get_size()
			if offset[0] < parent_offset[0]:
				size[0] -= parent_offset[0] - offset[0]
				offset[0] = parent_offset[0]
			if offset[1] < parent_offset[1]:
				size[1] -= parent_offset[1] - offset[1]
				offset[1] = parent_offset[1]
			texture = glGenTextures(1)
			self.textures.append(texture)
			draw_texture(offset,size,texture,surface[1].colour,surface[1].data,surface[1].get_size()) #Add game to screen with the scaled size and gap required.
			for child in surface[1].children:
				self.draw_game(child,offset)
		else:
			if len(surface[1]) == 5:
				draw_line(*surface[1])
			else:
				draw_lines(*surface[1])
class Game(ScaledScreen):
	fade = 0
	p_key = False
	music_stop = False
	unfade = False
	event_after_fade = -1
	loaded = False
	fade = 255
	unfade = True
	homedir = os.path.expanduser("~")
	fade_screen = False
	keys = []
	pygame_events = []
	sections = []
	back_key = False
	transfer_args = ()
	def __init__(self,title,game_size,on_exit = sys.exit):
		ScaledScreen.__init__(self,title,game_size,on_exit)
		self.fade_surface = pygame.Surface([1280,720])
		self.fade_surface.fill((0,0,0))
		self.fade_surface.set_alpha(0)
	def add_section(self,section_object):
		self.sections.append(section_object)
	def game_loop(self):
		self.keys = pygame.key.get_pressed()
		self.pygame_events = pygame.event.get()
		self.section.loop()
		if self.unfade:
			if self.fade == 255:
				play_music(self.section.music)
			if self.fade > 0:
				self.fade -= 5
			else:
				self.music_stop = False
				self.unfade = False
		if self.fade_screen and not self.unfade: #Fade out
			if self.fade == 0:
				sound("/sounds/menu3/fade.ogg").play()
				self.music_stop = True
				pygame.mixer.music.fadeout(850)
			if self.fade < 255:
				self.fade += 5
			else:
				self.fade_screen = False
				self.unfade = True
		if self.fade_screen == False:
			if self.event_after_fade != -1:
				self.section = self.sections[self.event_after_fade]
				self.section.transfer(*self.transfer_args)
				self.event_after_fade = -1
		self.fade_surface.set_alpha(self.fade)
		self.blit(self.fade_surface,(0,0))
		for event in self.pygame_events:
			if event.type == MUSICEND and self.music_stop == False:
				play_music(self.section.music)
		self.update(self.pygame_events) #Updates game
	def transfer_section(self,section,args=()):
		self.transfer_args = args
		self.event_after_fade = section
		self.fade_screen = True

The fonts aren't a problem. I removed them and they were fine. :( I'm getting really lost.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.