# HG changeset patch # User Emmanuel Gil Peyrot # Date 1383313553 -3600 # Node ID bfea9e9a6845715bbe84304dc513b4f46f739c09 # Parent 69c73023f7a001c0fff9edeb9ea0cb7e2f7def62 Manage the texture-specific indices in the Texture, and some more renderer optimisations. diff --git a/pytouhou/ui/background.pyx b/pytouhou/ui/background.pyx --- a/pytouhou/ui/background.pyx +++ b/pytouhou/ui/background.pyx @@ -92,8 +92,8 @@ cdef class BackgroundRenderer: nb_vertices += 4 - self.texture = key % MAX_TEXTURES - self.blendfunc = key // MAX_TEXTURES + self.texture = key >> 1 + self.blendfunc = key & 1 self.nb_vertices = nb_vertices self.vertex_buffer = realloc(vertex_buffer, nb_vertices * sizeof(Vertex)) diff --git a/pytouhou/ui/gamerenderer.pyx b/pytouhou/ui/gamerenderer.pyx --- a/pytouhou/ui/gamerenderer.pyx +++ b/pytouhou/ui/gamerenderer.pyx @@ -24,6 +24,7 @@ from pytouhou.lib.opengl cimport \ from pytouhou.utils.maths cimport perspective, setup_camera, ortho_2d from pytouhou.game.text cimport NativeText, GlyphCollection from .shaders.eosd import GameShader, BackgroundShader, PassthroughShader +from .renderer cimport Texture from collections import namedtuple Rect = namedtuple('Rect', 'x y w h') @@ -199,9 +200,9 @@ cdef class GameRenderer(Renderer): if label.shadow: shadow_rect = Rect(label.x + 1, label.y + 1, label.width, label.height) shadow = [black._replace(a=label.alpha)] * 4 - self.render_quads([shadow_rect, rect], [shadow, gradient], label.texture) + self.render_quads([shadow_rect, rect], [shadow, gradient], (label.texture).texture) else: - self.render_quads([rect], [gradient], label.texture) + self.render_quads([rect], [gradient], (label.texture).texture) cdef void render_interface(self, interface, game_boss): diff --git a/pytouhou/ui/renderer.pxd b/pytouhou/ui/renderer.pxd --- a/pytouhou/ui/renderer.pxd +++ b/pytouhou/ui/renderer.pxd @@ -1,5 +1,6 @@ from cpython cimport PyObject from pytouhou.lib.opengl cimport GLuint +from .texture cimport TextureManager, FontManager cdef struct Vertex: int x, y, z @@ -12,20 +13,26 @@ cdef struct PassthroughVertex: float u, v +cdef class Texture: + cdef GLuint texture + cdef unsigned short indices[2][65536] + + cdef class Renderer: - cdef public texture_manager, font_manager + cdef TextureManager texture_manager + cdef FontManager font_manager cdef GLuint vbo, framebuffer_vbo - cdef Vertex *vertex_buffer + cdef Vertex vertex_buffer[MAX_ELEMENTS] cdef long x, y, width, height cdef bint use_fixed_pipeline #XXX - cdef unsigned short *indices[2][MAX_TEXTURES] + cdef unsigned short *indices[MAX_TEXTURES][2] cdef unsigned short last_indices[2 * MAX_TEXTURES] cdef PyObject *elements[640*3] cdef void render_elements(self, elements) except * - cdef void render_quads(self, rects, colors, texture) except * + cdef void render_quads(self, rects, colors, GLuint texture) except * cdef void render_framebuffer(self, Framebuffer fb) except * diff --git a/pytouhou/ui/renderer.pyx b/pytouhou/ui/renderer.pyx --- a/pytouhou/ui/renderer.pyx +++ b/pytouhou/ui/renderer.pyx @@ -31,20 +31,26 @@ from pytouhou.lib.opengl cimport \ GL_LINEAR, GL_TEXTURE_MAG_FILTER, GL_RGBA, GL_RENDERBUFFER, GL_DEPTH_COMPONENT, GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_COMPLETE, glClear, GL_COLOR_BUFFER_BIT, - GL_DEPTH_BUFFER_BIT) + GL_DEPTH_BUFFER_BIT, GLuint, glDeleteTextures) from pytouhou.lib.sdl import SDLError from pytouhou.game.element cimport Element from .sprite cimport get_sprite_rendering_data -from .texture import TextureManager, FontManager from pytouhou.utils.helpers import get_logger logger = get_logger(__name__) -DEF MAX_ELEMENTS = 640*4*3 +cdef class Texture: + def __cinit__(self, GLuint texture, Renderer renderer): + self.texture = texture + for i in xrange(2): + renderer.indices[texture][i] = self.indices[i] + + def __dealloc__(self): + glDeleteTextures(1, &self.texture) cdef long find_objects(Renderer self, object elements) except -1: @@ -64,23 +70,17 @@ cdef long find_objects(Renderer self, ob cdef class Renderer: - def __cinit__(self): - self.vertex_buffer = malloc(MAX_ELEMENTS * sizeof(Vertex)) - - def __dealloc__(self): - free(self.vertex_buffer) - if not self.use_fixed_pipeline: glDeleteBuffers(1, &self.framebuffer_vbo) glDeleteBuffers(1, &self.vbo) def __init__(self, resource_loader): - self.texture_manager = TextureManager(resource_loader, self) + self.texture_manager = TextureManager(resource_loader, self, Texture) font_name = join(resource_loader.game_dir, 'font.ttf') try: - self.font_manager = FontManager(font_name, 16, self) + self.font_manager = FontManager(font_name, 16, self, Texture) except SDLError: self.font_manager = None logger.error('Font file “%s” not found, disabling text rendering altogether.', font_name) @@ -90,36 +90,29 @@ cdef class Renderer: glGenBuffers(1, &self.framebuffer_vbo) - def add_texture(self, int texture): - for i in xrange(2): - self.indices[i][texture] = malloc(65536 * sizeof(unsigned short)) - - - def remove_texture(self, int texture): - for i in xrange(2): - free(self.indices[i][texture]) - - cdef void render_elements(self, elements): cdef int key cdef int x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, ox, oy cdef float left, right, bottom, top cdef unsigned char r, g, b, a + nb_elements = find_objects(self, elements) + if not nb_elements: + return + nb_vertices = 0 memset(self.last_indices, 0, sizeof(self.last_indices)) - nb_elements = find_objects(self, elements) for element_idx in xrange(nb_elements): element = self.elements[element_idx] sprite = element.sprite ox, oy = element.x, element.y key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite) - blendfunc = key // MAX_TEXTURES - texture = key % MAX_TEXTURES + blendfunc = key & 1 + texture = key >> 1 - rec = self.indices[blendfunc][texture] + rec = self.indices[texture][blendfunc] next_indice = self.last_indices[key] # Pack data in buffer @@ -142,9 +135,6 @@ cdef class Renderer: nb_vertices += 4 - if nb_vertices == 0: - return - if self.use_fixed_pipeline: glVertexPointer(3, GL_INT, sizeof(Vertex), &self.vertex_buffer[0].x) glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &self.vertex_buffer[0].u) @@ -161,23 +151,34 @@ cdef class Renderer: glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), 20) glEnableVertexAttribArray(2) + # Don’t change the state when it’s not needed. + previous_blendfunc = -1 + previous_texture = -1 + for key in xrange(2 * MAX_TEXTURES): nb_indices = self.last_indices[key] if not nb_indices: continue - blendfunc = key // MAX_TEXTURES - texture = key % MAX_TEXTURES + blendfunc = key & 1 + texture = key >> 1 - glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) - glBindTexture(GL_TEXTURE_2D, texture) - glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, self.indices[blendfunc][texture]) + if blendfunc != previous_blendfunc: + glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) + if texture != previous_texture: + glBindTexture(GL_TEXTURE_2D, texture) + glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, self.indices[texture][blendfunc]) + + previous_blendfunc = blendfunc + previous_texture = texture + + glBindTexture(GL_TEXTURE_2D, 0) if not self.use_fixed_pipeline: glBindBuffer(GL_ARRAY_BUFFER, 0) - cdef void render_quads(self, rects, colors, texture): + cdef void render_quads(self, rects, colors, GLuint texture): # There is nothing that batch more than two quads on the same texture, currently. cdef Vertex buf[8] cdef unsigned short indices[12] diff --git a/pytouhou/ui/sprite.pyx b/pytouhou/ui/sprite.pyx --- a/pytouhou/ui/sprite.pyx +++ b/pytouhou/ui/sprite.pyx @@ -16,6 +16,7 @@ from libc.math cimport M_PI as pi from pytouhou.utils.matrix cimport Matrix +from pytouhou.ui.renderer cimport Texture #XXX cpdef object get_sprite_rendering_data(Sprite sprite): @@ -67,7 +68,7 @@ cpdef object get_sprite_rendering_data(S ty * y_1 + toy, (ty + th) * y_1 + toy) - key = MAX_TEXTURES * sprite.blendfunc + sprite.anm.texture + key = ((sprite.anm.texture).texture << 1) | sprite.blendfunc r, g, b = sprite.color values = tuple([x for x in vertmat.data[:12]]), uvs, (r, g, b, sprite.alpha) sprite._rendering_data = key, values diff --git a/pytouhou/ui/texture.pxd b/pytouhou/ui/texture.pxd new file mode 100644 --- /dev/null +++ b/pytouhou/ui/texture.pxd @@ -0,0 +1,12 @@ +from pytouhou.lib.sdl cimport Font + +cdef class TextureManager: + cdef object loader, renderer, texture_class + + cdef load(self, dict anms) + +cdef class FontManager: + cdef Font font + cdef object renderer, texture_class + + cdef load(self, list labels) diff --git a/pytouhou/ui/texture.pyx b/pytouhou/ui/texture.pyx --- a/pytouhou/ui/texture.pyx +++ b/pytouhou/ui/texture.pyx @@ -16,55 +16,48 @@ from pytouhou.lib.opengl cimport \ (glTexParameteri, GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_BGRA, GL_RGBA, GL_RGB, GL_LUMINANCE, GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4_REV, - glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D, GLuint, - glDeleteTextures) + glGenTextures, glBindTexture, glTexImage2D, GL_TEXTURE_2D, GLuint) -from pytouhou.lib.sdl cimport load_png, create_rgb_surface, Font +from pytouhou.lib.sdl cimport load_png, create_rgb_surface from pytouhou.formats.thtx import Texture #TODO: perhaps define that elsewhere? from pytouhou.game.text cimport NativeText import os -class TextureId(int): - def __del__(self): - cdef GLuint texture = self - glDeleteTextures(1, &texture) - self.renderer.remove_texture(self) +cdef class TextureManager: + def __init__(self, loader=None, renderer=None, texture_class=None): + self.loader = loader + self.renderer = renderer + self.texture_class = texture_class -class TextureManager(object): - def __init__(self, loader=None, renderer=None): - self.loader = loader - self.renderer = renderer - - - def load(self, anms): - for anm in sorted(anms.values(), key=lambda x: x[0].first_name.endswith('ascii.png')): + cdef load(self, dict anms): + for anm in sorted(anms.values(), key=is_ascii): for entry in anm: if not hasattr(entry, 'texture'): texture = decode_png(self.loader, entry.first_name, entry.secondary_name) - entry.texture = load_texture(texture) - elif not isinstance(entry.texture, TextureId): - entry.texture = load_texture(entry.texture) - self.renderer.add_texture(entry.texture) - entry.texture.renderer = self.renderer + elif not isinstance(entry.texture, self.texture_class): + texture = entry.texture + entry.texture = self.texture_class(load_texture(texture), self.renderer) anms.clear() +def is_ascii(anm): + return anm[0].first_name.endswith('ascii.png') + + cdef class FontManager: - cdef Font font - cdef object renderer - - def __init__(self, fontname, fontsize=16, renderer=None): + def __init__(self, fontname, fontsize=16, renderer=None, texture_class=None): self.font = Font(fontname, fontsize) self.renderer = renderer + self.texture_class = texture_class - def load(self, label_list): + cdef load(self, list labels): cdef NativeText label - for label in label_list: + for label in labels: if label.texture is None: surface = self.font.render(label.text) label.width, label.height = surface.surface.w, surface.surface.h @@ -77,8 +70,7 @@ cdef class FontManager: assert label.align == 'left' texture = Texture(label.width, label.height, -4, surface.pixels) - label.texture = load_texture(texture) - label.texture.renderer = self.renderer + label.texture = self.texture_class(load_texture(texture), self.renderer) cdef decode_png(loader, first_name, secondary_name): @@ -102,7 +94,7 @@ cdef decode_png(loader, first_name, seco return Texture(width, height, -4, new_image.pixels) -cdef load_texture(thtx): +cdef GLuint load_texture(thtx): cdef GLuint texture if thtx.fmt == 1: @@ -140,4 +132,4 @@ cdef load_texture(thtx): format_, type_, thtx.data) - return TextureId(texture) + return texture diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -82,6 +82,7 @@ setup(name='PyTouhou', compiler_directives={'infer_types': True, 'infer_types.verbose': True}, compile_time_env={'MAX_TEXTURES': 1024, + 'MAX_ELEMENTS': 640 * 4 * 3, 'MAX_CHANNELS': 26, 'USE_GLEW': is_windows}), scripts=['eosd', 'anmviewer'],