From 92bbaaf38e0d391a74ecd17271e476b23c967358 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Thu, 7 Oct 2010 19:31:41 +0000 Subject: [PATCH] Toss out the locality optimizer as it's largely irrelevant to graphics hardware Add an LRU cache optimizer instead --- mesh_export.py | 310 +++++++++++++++++++++++++++---------------------- 1 file changed, 169 insertions(+), 141 deletions(-) diff --git a/mesh_export.py b/mesh_export.py index 1ed2dcde..de11602c 100644 --- a/mesh_export.py +++ b/mesh_export.py @@ -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 scorebest_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 scorebest 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 -- 2.43.0