]> git.tdb.fi Git - libs/gl.git/commitdiff
Toss out the locality optimizer as it's largely irrelevant to graphics hardware
authorMikko Rasa <tdb@tdb.fi>
Thu, 7 Oct 2010 19:31:41 +0000 (19:31 +0000)
committerMikko Rasa <tdb@tdb.fi>
Thu, 7 Oct 2010 19:31:41 +0000 (19:31 +0000)
Add an LRU cache optimizer instead

mesh_export.py

index 1ed2dcde60074c8aeba7dc9635a1af08febd9f6f..de11602c93729a9e04a40d0f0d1715202b93f2ec 100644 (file)
@@ -101,15 +101,12 @@ class Face:
        
        __repr__ = __str__
 
-       def pivot_vertices(self, reverse, *vt):
-               verts = self.verts[:]
-               if reverse:
-                       verts.reverse()
-               flags = [(v in vt) for v in verts]
-               l = len(verts)
+       def pivot_vertices(self, *vt):
+               flags = [(v in vt) for v in self.verts]
+               l = len(self.verts)
                for i in range(l):
                        if flags[i] and not flags[(i+l-1)%l]:
-                               return verts[i:]+verts[:i]
+                               return self.verts[i:]+self.verts[:i]
 
        def get_edge(self, v1, v2):     
                key = make_edge_key(v1.index, v2.index)
@@ -292,7 +289,7 @@ class Mesh:
                        v.tan.normalize()
                        v.bino.normalize()
 
-       def create_strip(self, face, reverse, debug):
+       def create_strip(self, face, max_len, debug):
                edge = None
                for e in face.edges:
                        other = e.other_face(face)
@@ -304,20 +301,21 @@ class Mesh:
                        return None
 
                if debug:
-                       print "Starting strip from %s, edge %s, reverse=%s"%([v.index for v in face.verts], (edge.v1.index, edge.v2.index), reverse)
+                       print "Starting strip from %s, edge %s"%([v.index for v in face.verts], (edge.v1.index, edge.v2.index))
 
-               verts = face.pivot_vertices(reverse, edge.v1, edge.v2)
+               verts = face.pivot_vertices(edge.v1, edge.v2)
                if len(verts)==3:
                        result = [verts[-1], verts[0]]
                else:
                        result = [verts[-2], verts[-1]]
 
                while 1:
-                       verts = face.pivot_vertices(reverse, *result[-2:])
-                       k = len(result)%2
                        if debug:
                                print "  Adding %s"%face
 
+                       verts = face.pivot_vertices(*result[-2:])
+                       k = len(result)%2
+
                        face.flag = True
                        if len(verts)==4 and not k:
                                result.append(verts[3])
@@ -325,6 +323,11 @@ class Mesh:
                        if len(verts)==4 and k:
                                result.append(verts[3])
 
+                       if len(result)>=max_len:
+                               if debug:
+                                       print "  Max length exceeded"
+                               break
+
                        edge = face.get_edge(*result[-2:])
 
                        if debug:
@@ -341,6 +344,37 @@ class Mesh:
                return result
 
 
+class VertexCache:
+       def __init__(self, size):
+               self.size = size
+               self.slots = [-1]*self.size
+
+       def fetch(self, v):
+               hit = v.index in self.slots
+               if hit:
+                       self.slots.remove(v.index)
+               self.slots.append(v.index)
+               if not hit:
+                       del self.slots[0]
+               return hit
+
+       def fetch_strip(self, strip):
+               hits = 0
+               for v in strip:
+                       if self.fetch(v):
+                               hits += 1
+               return hits
+
+       def test_strip(self, strip):
+               hits = 0
+               for i in range(len(strip)):
+                       if i>=self.size:
+                               break
+                       if strip[i].index in self.slots[i:]:
+                               hits += 1
+               return hits
+
+
 class Exporter:
        def __init__(self, fn):
                self.filename = fn
@@ -350,33 +384,117 @@ class Exporter:
                        self.out_file = file(fn, "w")
                self.use_strips = True
                self.use_degen_tris = True
-               self.optimize_locality = True
+               self.max_strip_len = 1024
+               self.optimize_cache = False
+               self.cache_size = 64
                self.export_lines = True
                self.tbn_vecs = False
                self.debug = False
                self.strip_debug = False
                self.split_debug = False
 
-       def get_locality(self, strip):
-               total = 0
-               for i in range(1, len(strip)):
-                       if strip[i].index!=strip[i-1].index:
-                               total += 1.0/(abs(strip[i].index-strip[i-1].index))
-               return total/len(strip)
-
-       def get_followers(self, strip):
-               result = {}
-               for i in range(len(strip)-1):
-                       v = strip[i]
-                       n = strip[i+1]
-                       if v.index!=n.index:
-                               if v.index not in result:
-                                       result[v.index] = {}
-                               if n.index not in result[v.index]:
-                                       result[v.index][n.index] = 1
+       def stripify(self, mesh):
+               for f in mesh.faces:
+                       f.flag = False
+
+               strips = []
+
+               while 1:
+                       best = 5
+                       face = None
+                       for f in mesh.faces:
+                               if f.flag:
+                                       continue
+                               score = 0
+                               for e in f.edges:
+                                       other = e.other_face(f)
+                                       if other and not other.flag:
+                                               score += 1
+                               if score>0 and score<best:
+                                       face = f
+                                       best = score
+
+                       if not face:
+                               break
+
+                       strip = mesh.create_strip(face, self.max_strip_len, self.strip_debug)
+                       if strip:
+                               strips.append(strip)
+
+               loose = [f for f in mesh.faces if not f.flag]
+
+               if self.debug:
+                       print "%d strips:"%len(strips)
+                       for i in range(len(strips)):
+                               print "  %d: %d indices"%(i, len(strips[i]))
+                       print "%d loose faces"%len([f for f in mesh.faces if not f.flag])
+                       nind = sum([len(s) for s in strips])+sum([len(f.verts) for f in loose])
+                       print "%d indices total"%nind
+
+               if self.use_degen_tris and strips:
+                       big_strip = []
+
+                       cache = None
+                       total_hits = 0
+                       if self.optimize_cache:
+                               cache = VertexCache(self.cache_size)
+
+                       while strips:
+                               best = 0
+                               if cache:
+                                       best_hits = 0
+                                       for i in range(len(strips)):
+                                               hits = cache.test_strip(strips[i])
+                                               if hits>best_hits:
+                                                       best = i
+                                                       best_hits = hits
+
+                               s = strips[best]
+
+                               if big_strip:
+                                       glue = [big_strip[-1], s[0]]
+                                       if len(big_strip)%2:
+                                               glue += [s[0]]
+
+                                       big_strip += glue
+                                       if cache:
+                                               total_hits += cache.fetch_strip(glue)
+
+                               big_strip += s
+                               if cache:
+                                       total_hits += cache.fetch_strip(s)
+
+                               del strips[best]
+
+                       for f in loose:
+                               if len(big_strip)%2:
+                                       order = (-1, -2, 0, 1)
                                else:
-                                       result[v.index][n.index] += 1
-               return result
+                                       order = (0, 1, -1, -2)
+                               verts = [f.verts[i] for i in order[:len(f.verts)]]
+                               if big_strip:
+                                       glue = [big_strip[-1], verts[0]]
+                                       big_strip += glue
+                                       if cache:
+                                               total_hits += cache.fetch_strip(glue)
+                               big_strip += verts
+                               if cache:
+                                       total_hits += cache.fetch_strip(verts)
+
+                       strips = [big_strip]
+                       loose = []
+                       
+                       if self.debug:
+                               nind = len(big_strip)
+                               print "Big strip has %d indices"%nind
+                               if self.optimize_cache:
+                                       print "%d cache hits"%total_hits
+
+               if self.debug:
+                       ntris = sum([len(f.verts)-2 for f in mesh.faces])
+                       print "%.2f indices per triangle"%(float(nind)/max(ntris, 1))
+
+               return strips, loose
 
        def export(self):
                scene = bpy.data.scenes.active
@@ -411,104 +529,7 @@ class Exporter:
 
                strips = []
                if self.use_strips:
-                       for f in mesh.faces:
-                               f.flag = False
-
-                       while 1:
-                               best = 5
-                               face = None
-                               for f in mesh.faces:
-                                       if f.flag:
-                                               continue
-                                       score = 0
-                                       for e in f.edges:
-                                               other = e.other_face(f)
-                                               if other and not other.flag:
-                                                       score += 1
-                                       if score>0 and score<best:
-                                               face = f
-                                               best = score
-
-                               if not face:
-                                       break
-
-                               strip = mesh.create_strip(face, self.use_degen_tris and sum([len(s) for s in strips])%2, self.strip_debug)
-                               if strip:
-                                       strips.append(strip)
-
-                       if self.debug:
-                               print "%d strips:"%len(strips)
-                               for i in range(len(strips)):
-                                       print "  %d: %d indices"%(i, len(strips[i]))
-                               print "%d loose faces"%len([f for f in mesh.faces if not f.flag])
-                               nind = sum([len(s) for s in strips])+sum([len(f.verts) for f in mesh.faces if not f.flag])
-                               print "%d indices total"%nind
-
-                       if self.use_degen_tris and strips:
-                               big_strip = []
-                               for s in strips:
-                                       if big_strip:
-                                               big_strip += [big_strip[-1], s[0]]
-                                       big_strip += s
-
-                               for f in mesh.faces:
-                                       if not f.flag:
-                                               if len(big_strip)%2:
-                                                       order = (-1, -2, 0, 1)
-                                               else:
-                                                       order = (0, 1, -1, -2)
-                                               if big_strip:
-                                                       big_strip += [big_strip[-1], f.verts[order[0]]]
-                                               big_strip += [f.verts[i] for i in order[:len(f.verts)]]
-                                               f.flag = True
-
-                               strips = [big_strip]
-                               
-                               if self.debug:
-                                       nind = len(big_strip)
-                                       print "Big strip has %d indices"%len(big_strip)
-
-               if self.debug:
-                       print "%.2f indices per triangle"%(float(nind)/max(ntris, 1))
-                       print "Locality before optimization: "+" ".join(["%.3f"%self.get_locality(s) for s in strips])
-
-               if self.optimize_locality and self.use_strips and strips:
-                       followers = {}
-                       for s in strips:
-                               followers.update(self.get_followers(s))
-
-                       verts2 = []
-                       vert = strips[0][0]
-                       while 1:
-                               vert.flag = True
-                               verts2.append(vert)
-
-                               next = None
-                               if vert.index in followers:
-                                       flw = followers[vert.index]
-                                       best = 0
-                                       for n in flw:
-                                               if flw[n]>best and not mesh.verts[n].flag:
-                                                       next = mesh.verts[n]
-                                                       best = flw[n]+0.9/abs(vert.index-n)
-
-                               if not next:
-                                       for v in mesh.verts:
-                                               if not v.flag:
-                                                       next = v
-                                                       break
-                                       if not next:
-                                               break
-
-                               vert = next
-
-                       mesh.verts = verts2
-
-                       for i in range(len(mesh.verts)):
-                               mesh.verts[i].index = i
-
-                       if self.debug:
-                               print "Locality after optimization: "+" ".join(["%.3f"%self.get_locality(s) for s in strips])
+                       strips, loose = self.stripify(mesh)
 
                self.out_file.write("vertices NORMAL3")
                if mesh.faceUV:
@@ -546,13 +567,12 @@ class Exporter:
                        self.out_file.write(";\n};\n")
 
                first = True
-               for f in mesh.faces:
-                       if not f.flag:
-                               if first:
-                                       self.out_file.write("batch TRIANGLES\n{\n")
-                                       first = False
-                               for i in range(2, len(f.verts)):
-                                       self.out_file.write("\tindices %u %u %u;\n"%(f.verts[0].index, f.verts[i-1].index, f.verts[i].index))
+               for f in loose:
+                       if first:
+                               self.out_file.write("batch TRIANGLES\n{\n")
+                               first = False
+                       for i in range(2, len(f.verts)):
+                               self.out_file.write("\tindices %u %u %u;\n"%(f.verts[0].index, f.verts[i-1].index, f.verts[i].index))
                if not first:
                        self.out_file.write("};\n")
 
@@ -571,7 +591,9 @@ class FrontEnd:
        def run(self):
                self.use_strips = Blender.Draw.Create(self.config.get('use_strips', True))
                self.use_degen_tris = Blender.Draw.Create(self.config.get('use_degen_tris', True))
-               self.optimize_locality = Blender.Draw.Create(self.config.get('optimize_locality', True))
+               self.max_strip_len = Blender.Draw.Create(self.config.get('max_strip_len', 1024))
+               self.optimize_cache = Blender.Draw.Create(self.config.get('optimize_cache', False))
+               self.cache_size = Blender.Draw.Create(self.config.get('cache_size', 64))
                self.export_lines = Blender.Draw.Create(self.config.get('export_lines', False))
                self.tbn_vecs = Blender.Draw.Create(self.config.get('tbn_vecs', False))
                self.debug = Blender.Draw.Create(self.config.get('debug', False))
@@ -580,7 +602,9 @@ class FrontEnd:
                ret = Blender.Draw.PupBlock("Export MSP GL mesh",
                        [("Use strips", self.use_strips, "Generage OpenGL triangle strips"),
                                ("Use degen tris", self.use_degen_tris, "Use degenerate triangles to combine triangle strips"),
-                               ("Optimize locality", self.optimize_locality),
+                               ("Max strip len", self.max_strip_len, 4, 16384, "Maximum length of a triangle strip"),
+                               ("Optimize cache", self.optimize_cache, "Optimize for vertex cache"),
+                               ("Cache size", self.cache_size, 8, 1024, "Cache size to optimize for"),
                                ("Export lines", self.export_lines, "Export lone edges as lines"),
                                ("Compute T/B vecs", self.tbn_vecs, "Compute tangent/binormal vectors for bumpmapping"),
                                ("Debugging options"),
@@ -598,7 +622,9 @@ class FrontEnd:
        def export(self, fn):
                self.config['use_strips'] = self.use_strips.val
                self.config['use_degen_tris'] = self.use_degen_tris.val
-               self.config['optimize_locality'] = self.optimize_locality.val
+               self.config['max_strip_len'] = self.max_strip_len.val
+               self.config['optimize_cache'] = self.optimize_cache.val
+               self.config['cache_size'] = self.cache_size.val
                self.config['export_lines'] = self.export_lines.val
                self.config['tbn_vecs'] = self.tbn_vecs.val
                self.config['debug'] = self.debug.val
@@ -613,7 +639,9 @@ class FrontEnd:
                exp = Exporter(fn)
                exp.use_strips = self.use_strips.val
                exp.use_degen_tris = self.use_degen_tris.val
-               exp.optimize_locality = self.optimize_locality.val
+               exp.max_strip_len = self.max_strip_len.val
+               exp.optimize_cache = self.optimize_cache.val
+               exp.cache_size = self.cache_size.val
                exp.export_lines = self.export_lines.val
                exp.tbn_vecs = self.tbn_vecs.val
                exp.debug = self.debug.val