comparison pytouhou/ui/renderer.pyx @ 449:d56536ef28e8

Improve render_elements’ speed a lot, and fix it in some corner cases. Thanks liori!
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sat, 03 Aug 2013 15:49:11 +0200
parents 878273a984c4
children cae1ae9de430
comparison
equal deleted inserted replaced
448:3bc37791f0a2 449:d56536ef28e8
11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ## GNU General Public License for more details. 12 ## GNU General Public License for more details.
13 ## 13 ##
14 14
15 from libc.stdlib cimport malloc, free 15 from libc.stdlib cimport malloc, free
16 from itertools import chain 16 from libc.string cimport memset
17
18 from struct import pack
19 17
20 from pytouhou.lib.opengl cimport \ 18 from pytouhou.lib.opengl cimport \
21 (glVertexPointer, glTexCoordPointer, glColorPointer, 19 (glVertexPointer, glTexCoordPointer, glColorPointer,
22 glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc, 20 glVertexAttribPointer, glEnableVertexAttribArray, glBlendFunc,
23 glBindTexture, glDrawElements, glBindBuffer, glBufferData, 21 glBindTexture, glDrawElements, glBindBuffer, glBufferData,
24 GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, GL_UNSIGNED_BYTE, 22 GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, GL_UNSIGNED_BYTE,
25 GL_UNSIGNED_SHORT, GL_INT, GL_FLOAT, GL_SRC_ALPHA, 23 GL_UNSIGNED_SHORT, GL_INT, GL_FLOAT, GL_SRC_ALPHA,
26 GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_TEXTURE_2D, GL_TRIANGLES, 24 GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_TEXTURE_2D, GL_TRIANGLES,
27 glGenBuffers) 25 glGenBuffers)
28 26
27 from pytouhou.game.element cimport Element
29 from .sprite cimport get_sprite_rendering_data 28 from .sprite cimport get_sprite_rendering_data
30 from .texture import TextureManager 29 from .texture import TextureManager
31 30
32 31
33 MAX_ELEMENTS = 640*4*3 32 DEF MAX_ELEMENTS = 640*4*3
33
34
35 cdef long find_objects(Renderer self, object elements):
36 # Don’t type element as Element, or else the overriding of objects won’t work.
37 cdef Element obj
38 cdef long i = 0
39 for element in elements:
40 for obj in element.objects:
41 sprite = obj.sprite
42 if sprite and sprite.visible:
43 # warning: no reference is preserved on the object—assuming the object will not die accidentally
44 self.elements[i] = <PyObject*>obj
45 i += 1
46 if i >= 640*3-4:
47 return i
48 return i
34 49
35 50
36 cdef class Renderer: 51 cdef class Renderer:
37 def __cinit__(self): 52 def __cinit__(self):
38 # Allocate buffers
39 self.vertex_buffer = <Vertex*> malloc(MAX_ELEMENTS * sizeof(Vertex)) 53 self.vertex_buffer = <Vertex*> malloc(MAX_ELEMENTS * sizeof(Vertex))
40 54
41 55
42 def __dealloc__(self): 56 def __dealloc__(self):
43 free(self.vertex_buffer) 57 free(self.vertex_buffer)
44 58
45 59
46 def __init__(self, resource_loader): 60 def __init__(self, resource_loader):
47 self.texture_manager = TextureManager(resource_loader) 61 self.texture_manager = TextureManager(resource_loader, self)
48 62
49 if not self.use_fixed_pipeline: 63 if not self.use_fixed_pipeline:
50 glGenBuffers(1, &self.vbo) 64 glGenBuffers(1, &self.vbo)
51 65
52 66
67 def add_texture(self, int texture):
68 for i in xrange(2):
69 self.indices[i][texture] = <unsigned short*> malloc(65536 * sizeof(unsigned short))
70
71
72 def remove_texture(self, int texture):
73 for i in xrange(2):
74 free(self.indices[i][texture])
75
76
53 cpdef render_elements(self, elements): 77 cpdef render_elements(self, elements):
54 cdef unsigned short nb_vertices = 0, nb_indices, *new_indices 78 cdef int key
79 cdef int x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, ox, oy
80 cdef float left, right, bottom, top
81 cdef unsigned char r, g, b, a
55 82
56 indices_by_texture = {} 83 nb_vertices = 0
84 memset(self.last_indices, 0, sizeof(self.last_indices))
57 85
58 objects = chain(*[element.objects for element in elements]) 86 nb_elements = find_objects(self, elements)
59 for element in objects: 87 for element_idx in xrange(nb_elements):
60 if nb_vertices >= MAX_ELEMENTS - 4: 88 element = <object>self.elements[element_idx]
61 break 89 sprite = element.sprite
90 ox, oy = element.x, element.y
91 key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite)
62 92
63 sprite = element.sprite 93 blendfunc = key // MAX_TEXTURES
64 if sprite and sprite.visible: 94 texture = key % MAX_TEXTURES
65 ox, oy = element.x, element.y
66 key, (vertices, uvs, colors) = get_sprite_rendering_data(sprite)
67 rec = indices_by_texture.setdefault(key, [])
68 95
69 # Pack data in buffer 96 rec = self.indices[blendfunc][texture]
70 x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 = vertices 97 next_indice = self.last_indices[key]
71 left, right, bottom, top = uvs
72 r, g, b, a = colors
73 self.vertex_buffer[nb_vertices] = Vertex(x1 + ox, y1 + oy, z1, left, bottom, r, g, b, a)
74 self.vertex_buffer[nb_vertices+1] = Vertex(x2 + ox, y2 + oy, z2, right, bottom, r, g, b, a)
75 self.vertex_buffer[nb_vertices+2] = Vertex(x3 + ox, y3 + oy, z3, right, top, r, g, b, a)
76 self.vertex_buffer[nb_vertices+3] = Vertex(x4 + ox, y4 + oy, z4, left, top, r, g, b, a)
77 98
78 # Add indices 99 # Pack data in buffer
79 index = nb_vertices 100 x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4 = vertices
80 rec.extend((index, index + 1, index + 2, index + 2, index + 3, index)) 101 left, right, bottom, top = uvs
102 r, g, b, a = colors
103 self.vertex_buffer[nb_vertices] = Vertex(x1 + ox, y1 + oy, z1, left, bottom, r, g, b, a)
104 self.vertex_buffer[nb_vertices+1] = Vertex(x2 + ox, y2 + oy, z2, right, bottom, r, g, b, a)
105 self.vertex_buffer[nb_vertices+2] = Vertex(x3 + ox, y3 + oy, z3, right, top, r, g, b, a)
106 self.vertex_buffer[nb_vertices+3] = Vertex(x4 + ox, y4 + oy, z4, left, top, r, g, b, a)
81 107
82 nb_vertices += 4 108 # Add indices
109 rec[next_indice] = nb_vertices
110 rec[next_indice+1] = nb_vertices + 1
111 rec[next_indice+2] = nb_vertices + 2
112 rec[next_indice+3] = nb_vertices + 2
113 rec[next_indice+4] = nb_vertices + 3
114 rec[next_indice+5] = nb_vertices
115 self.last_indices[key] += 6
116
117 nb_vertices += 4
83 118
84 if nb_vertices == 0: 119 if nb_vertices == 0:
85 return 120 return
86 121
87 if self.use_fixed_pipeline: 122 if self.use_fixed_pipeline:
98 glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12) 133 glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(Vertex), <void*>12)
99 glEnableVertexAttribArray(1) 134 glEnableVertexAttribArray(1)
100 glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20) 135 glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, True, sizeof(Vertex), <void*>20)
101 glEnableVertexAttribArray(2) 136 glEnableVertexAttribArray(2)
102 137
103 for (texture, blendfunc), indices in indices_by_texture.items(): 138 for key in xrange(2 * MAX_TEXTURES):
139 nb_indices = self.last_indices[key]
140 if not nb_indices:
141 continue
104 142
105 #TODO: find a more elegent way. 143 blendfunc = key // MAX_TEXTURES
106 nb_indices = len(indices) 144 texture = key % MAX_TEXTURES
107 new_indices = <unsigned short*> malloc(nb_indices * sizeof(unsigned short))
108 for i in xrange(nb_indices):
109 new_indices[i] = indices[i]
110 145
111 glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc]) 146 glBlendFunc(GL_SRC_ALPHA, (GL_ONE_MINUS_SRC_ALPHA, GL_ONE)[blendfunc])
112 glBindTexture(GL_TEXTURE_2D, texture) 147 glBindTexture(GL_TEXTURE_2D, texture)
113 glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, new_indices) 148 glDrawElements(GL_TRIANGLES, nb_indices, GL_UNSIGNED_SHORT, self.indices[blendfunc][texture])
114 free(new_indices)
115 149
116 if not self.use_fixed_pipeline: 150 if not self.use_fixed_pipeline:
117 glBindBuffer(GL_ARRAY_BUFFER, 0) 151 glBindBuffer(GL_ARRAY_BUFFER, 0)