@@ -11,6 +11,7 @@ from libc.stdint cimport int32_t, int64_t
1111from ... import name_patch
1212from ...cache import SharedCache
1313from ...model cimport true_
14+ from ...timer cimport perf_counter
1415
1516
1617logger = name_patch(logger, __name__ )
@@ -25,13 +26,30 @@ cdef Matrix44 IdentityTransform = Matrix44._identity()
2526
2627cdef class Model:
2728 @staticmethod
28- def flush_cache ():
29+ def flush_caches (double min_age = 30 , int64_t min_size = 2000 ):
30+ cdef double cutoff = perf_counter() - min_age
2931 cdef Model model
30- count = len (ModelCache)
32+ cdef int64_t unload_count= 0 , dump_count= 0
33+ cdef list unloaded= []
34+ while len (ModelCache) > min_size:
35+ for model in ModelCache.values():
36+ if model.touch_timestamp < cutoff and not model.dependents:
37+ unloaded.append(model)
38+ if not unloaded:
39+ break
40+ while unloaded:
41+ model = unloaded.pop()
42+ del ModelCache[model.name]
43+ model.unload()
44+ unload_count += 1
3145 for model in ModelCache.values():
32- model.invalidate()
33- ModelCache.clear()
34- return count
46+ if model.cache_timestamp < cutoff and model.cache is not None :
47+ model.cache = None
48+ dump_count += 1
49+ if dump_count:
50+ logger.trace(" Dumped sub-caches on {} models" , dump_count)
51+ if unload_count:
52+ logger.trace(" Unloaded {} models from cache, {} remaining" , unload_count, len (ModelCache))
3553
3654 @staticmethod
3755 def by_name (str name ):
@@ -62,6 +80,16 @@ cdef class Model:
6280 def __repr__ (self ):
6381 return f' <Model: {self.name}>'
6482
83+ cpdef void unload(self ):
84+ assert not self .dependents
85+ self .dependents = None
86+ self .cache = None
87+ if self .buffer_caches is not None :
88+ for cache in self .buffer_caches:
89+ if self .name in cache:
90+ del cache[self .name]
91+ self .buffer_caches = None
92+
6593 cpdef bint is_manifold(self ):
6694 raise NotImplementedError ()
6795
@@ -103,6 +131,9 @@ cdef class Model:
103131 self .dependents = set ()
104132 self .dependents.add(model)
105133
134+ cpdef void remove_dependent(self , Model model):
135+ self .dependents.remove(model)
136+
106137 cpdef void invalidate(self ):
107138 cdef Model model
108139 cdef dict cache
@@ -117,6 +148,7 @@ cdef class Model:
117148 del cache[self .name]
118149
119150 cpdef object get_trimesh(self ):
151+ self .cache_timestamp = perf_counter()
120152 if self .cache is None :
121153 self .cache = {}
122154 elif ' trimesh' in self .cache:
@@ -126,6 +158,7 @@ cdef class Model:
126158 return trimesh_model
127159
128160 cpdef object get_manifold(self ):
161+ self .cache_timestamp = perf_counter()
129162 if self .cache is None :
130163 self .cache = {}
131164 elif ' manifold' in self .cache:
@@ -135,6 +168,7 @@ cdef class Model:
135168 return manifold
136169
137170 cpdef Vector get_bounds(self ):
171+ self .cache_timestamp = perf_counter()
138172 if self .cache is None :
139173 self .cache = {}
140174 elif ' bounds' in self .cache:
@@ -153,6 +187,7 @@ cdef class Model:
153187 return bounds_vector
154188
155189 cdef tuple get_buffers(self , object glctx, dict objects):
190+ self .cache_timestamp = perf_counter()
156191 cdef str name = self .name
157192 if name in objects:
158193 return objects[name]
@@ -280,6 +315,10 @@ cdef class Model:
280315cdef class UnaryOperation(Model):
281316 cdef Model original
282317
318+ cpdef void unload(self ):
319+ self .original.remove_dependent(self )
320+ super (UnaryOperation, self ).unload()
321+
283322 cpdef bint is_manifold(self ):
284323 return self .original.is_manifold()
285324
@@ -299,6 +338,7 @@ cdef class Flatten(UnaryOperation):
299338 model.original = original
300339 model.original.add_dependent(model)
301340 ModelCache[name] = model
341+ model.touch_timestamp = perf_counter()
302342 return model
303343
304344 cpdef bint is_manifold(self ):
@@ -364,6 +404,7 @@ cdef class Repair(UnaryOperation):
364404 model.original = original
365405 model.original.add_dependent(model)
366406 ModelCache[name] = model
407+ model.touch_timestamp = perf_counter()
367408 return model
368409
369410 cpdef Model repair(self ):
@@ -403,6 +444,7 @@ cdef class SnapEdges(UnaryOperation):
403444 model.snap_angle = snap_angle
404445 model.minimum_area = minimum_area
405446 ModelCache[name] = model
447+ model.touch_timestamp = perf_counter()
406448 return model
407449
408450 cpdef bint is_manifold(self ):
@@ -441,6 +483,7 @@ cdef class Transform(UnaryOperation):
441483 model.original.add_dependent(model)
442484 model.transform_matrix = transform_matrix
443485 ModelCache[name] = model
486+ model.touch_timestamp = perf_counter()
444487 return model
445488
446489 cpdef Model repair(self ):
@@ -481,6 +524,7 @@ cdef class UVRemap(UnaryOperation):
481524 model.original.add_dependent(model)
482525 model.mapping = mapping
483526 ModelCache[name] = model
527+ model.touch_timestamp = perf_counter()
484528 return model
485529
486530 cdef object remap_sphere(self , trimesh_model):
@@ -526,6 +570,7 @@ cdef class Slice(UnaryOperation):
526570 model.origin = origin
527571 model.normal = normal.normalize()
528572 ModelCache[name] = model
573+ model.touch_timestamp = perf_counter()
529574 return model
530575
531576 cpdef bint is_manifold(self ):
@@ -596,8 +641,14 @@ cdef class BooleanOperation(Model):
596641 for child_model in collected_models:
597642 child_model.add_dependent(model)
598643 ModelCache[name] = model
644+ model.touch_timestamp = perf_counter()
599645 return model
600646
647+ cpdef void unload(self ):
648+ for model in self .models:
649+ model.remove_dependent(self )
650+ super (BooleanOperation, self ).unload()
651+
601652 cpdef bint is_manifold(self ):
602653 return True
603654
@@ -722,6 +773,7 @@ cdef class Box(PrimitiveModel):
722773 model.name = name
723774 model.uv_map = uv_map
724775 ModelCache[name] = model
776+ model.touch_timestamp = perf_counter()
725777 return model
726778
727779 cpdef object build_trimesh(self ):
@@ -742,6 +794,7 @@ cdef class Sphere(PrimitiveModel):
742794 model.name = name
743795 model.segments = segments
744796 ModelCache[name] = model
797+ model.touch_timestamp = perf_counter()
745798 return model
746799
747800 @ cython.cdivision (True )
@@ -817,6 +870,7 @@ cdef class Cylinder(PrimitiveModel):
817870 model.name = name
818871 model.segments = segments
819872 ModelCache[name] = model
873+ model.touch_timestamp = perf_counter()
820874 return model
821875
822876 @ cython.cdivision (True )
@@ -901,6 +955,7 @@ cdef class Cone(PrimitiveModel):
901955 model.name = name
902956 model.segments = segments
903957 ModelCache[name] = model
958+ model.touch_timestamp = perf_counter()
904959 return model
905960
906961 @ cython.cdivision (True )
@@ -969,6 +1024,7 @@ cdef class ExternalModel(Model):
9691024 model.name = filename
9701025 model.cache_path = SharedCache[filename]
9711026 ModelCache[filename] = model
1027+ model.touch_timestamp = perf_counter()
9721028 return model
9731029
9741030 cpdef bint is_manifold(self ):
0 commit comments