diff options
author | Bruce Smith <bruce@nanorex.com> | 2009-03-10 00:56:31 +0000 |
---|---|---|
committer | Bruce Smith <bruce@nanorex.com> | 2009-03-10 00:56:31 +0000 |
commit | 182540a2d4905d48e744df8d58570b90d32dfabb (patch) | |
tree | 5976005f2cb5e406c926f462a11a0f1a6635f9e7 | |
parent | ee557d002fa85e5c8bad66c2c900dc0e4e454e21 (diff) | |
download | nanoengineer-theirix-182540a2d4905d48e744df8d58570b90d32dfabb.tar.gz nanoengineer-theirix-182540a2d4905d48e744df8d58570b90d32dfabb.zip |
debug_pref to reuse DrawingSets when model/selection unchanged
-rw-r--r-- | cad/src/graphics/widgets/GLPane_drawingset_methods.py | 186 | ||||
-rw-r--r-- | cad/src/graphics/widgets/GLPane_rendering_methods.py | 31 | ||||
-rwxr-xr-x | cad/src/graphics/widgets/ThumbView.py | 7 |
3 files changed, 184 insertions, 40 deletions
diff --git a/cad/src/graphics/widgets/GLPane_drawingset_methods.py b/cad/src/graphics/widgets/GLPane_drawingset_methods.py index 37276047e..87ce2ea83 100644 --- a/cad/src/graphics/widgets/GLPane_drawingset_methods.py +++ b/cad/src/graphics/widgets/GLPane_drawingset_methods.py @@ -35,6 +35,7 @@ class DrawingSetCache(object): #bruce 090227 A persistent cache of DrawingSets, one for each "drawing intent" passed to draw_csdl. """ + saved_change_indicator = None #bruce 090309 def __init__(self, cachename, temporary): self.cachename = cachename self.temporary = temporary @@ -43,6 +44,7 @@ class DrawingSetCache(object): #bruce 090227 for dset in self.dsets.values(): dset.destroy() self.dsets = {} + self.saved_change_indicator = None return pass @@ -124,9 +126,11 @@ class GLPane_drawingset_methods(object): """ return self._csdl_collector is not None - _current_drawingset_cache = None + _current_drawingset_cache_policy = None # or a tuple of (temporary, cachename) - def before_drawing_csdls(self, bare_primitives = False): + def before_drawing_csdls(self, + bare_primitives = False, + dset_change_indicator = None ): """ Whenever some CSDLs are going to be drawn by self.draw_csdl, call this first, draw them, and then call self.after_drawing_csdls. @@ -137,6 +141,18 @@ class GLPane_drawingset_methods(object): permit reentrancy of ColorSorter.start, and will not be allowed to open up a "catchall display list" (an nfr for CSDL) since that might lead to nested display list compiles, not allowed by OpenGL. + + @param dset_change_indicator: if provided and not false, + and if a certain debug_pref is enabled, then if after_drawing_csdls + would use a drawingset_cache and one already + exists and has the same value of dset_change_indicator + saved from our last use of that cache, we assume there + is no need for the caller to remake the drawingsets in + that cache, which we tell it by returning True. + (This is never fully correct -- for details see caller docstring.) + + @return: usually False; True in special cases explained in the + docstring for our dset_change_indicator parameter. """ # someday we might take other args, e.g. an "intent map" del self.csdl_collector @@ -146,6 +162,8 @@ class GLPane_drawingset_methods(object): self._remake_display_lists = self._compute_remake_display_lists_now() # note: this affects how we use both debug_prefs, below. + + res = False if debug_pref("GLPane: use DrawingSets to draw model?", Choice_boolean_True, #bruce 090225 revised @@ -156,8 +174,17 @@ class GLPane_drawingset_methods(object): # sets self.csdl_collector.use_drawingsets, and more # (note: this is independent of self.permit_shaders, # since DrawingSets can be used even if shaders are not) - self._current_drawingset_cache = self._choose_drawingset_cache() - pass + self._current_drawingset_cache_policy = self._choose_drawingset_cache_policy() + if debug_pref("GLPane: reuse dsets unless model or sel changes? (has drawing bugs)", + Choice_boolean_False, + prefs_key = False + ): + if dset_change_indicator: + policy = self._current_drawingset_cache_policy + cache = self._find_or_make_dset_cache_to_use(policy, make = False) + if cache: + if cache.saved_change_indicator == dset_change_indicator: + res = True if debug_pref("GLPane: highlight atoms in CSDLs?", Choice_boolean_True, #bruce 090225 revised @@ -167,7 +194,7 @@ class GLPane_drawingset_methods(object): if bare_primitives and self._remake_display_lists: self.csdl_collector.setup_for_bare_primitives() - return + return res def _compute_remake_display_lists_now(self): #bruce 090224 remake_during_movies = debug_pref( @@ -241,7 +268,10 @@ class GLPane_drawingset_methods(object): highlight_color = highlight_color ) pass - def after_drawing_csdls(self, error = False): + def after_drawing_csdls(self, + error = False, + reuse_cached_dsets_unchanged = False, + dset_change_indicator = None ): """ @see: before_drawing_csdls @@ -250,6 +280,13 @@ class GLPane_drawingset_methods(object): If it's known to have failed, we might not do some things we normally do. Default value is False since most calls don't pass anything. (#REVIEW: good? true?) + + @param reuse_cached_dsets_unchanged: whether to do what it says. + Typically equal to the return value of the preceding call + of before_drawing_csdls. + + @param dset_change_indicator: if true, store in the dset cache + (whether found or made, modified or not) as .saved_change_indicator. """ self._remake_display_lists = self._compute_remake_display_lists_now() @@ -260,7 +297,10 @@ class GLPane_drawingset_methods(object): self.draw_csdl(csdl) pass if self.csdl_collector.use_drawingsets: - self._draw_drawingsets() + self._draw_drawingsets( + reuse_cached_dsets_unchanged = reuse_cached_dsets_unchanged, + dset_change_indicator = dset_change_indicator + ) # note, the DrawingSets themselves last between draw calls, # and are stored elsewhere in self. self.csdl_collector has # attributes used to collect necessary info during a @@ -271,10 +311,17 @@ class GLPane_drawingset_methods(object): del self._csdl_collector_class # expose class default value return + def _whole_model_drawingset_change_indicator(self): + """ + """ + return None # disable this optim by default + def _call_func_that_draws_model(self, func, bare_primitives = None, - drawing_phase = None ): + drawing_phase = None, + whole_model = True + ): """ Call func() between calls of self.before_drawing_csdls(**kws) and self.after_drawing_csdls(). Return whatever func() returns @@ -283,7 +330,9 @@ class GLPane_drawingset_methods(object): func should usually be something which calls before/after_drawing_model inside it, e.g. one of the standard functions for drawing the - entire model (e.g. part.draw or graphicsMode.Draw). + entire model (e.g. part.draw or graphicsMode.Draw), + or self._call_func_that_draws_objects which draws a portion of + the model between those methods. @param bare_primitives: passed to before_drawing_csdls. @@ -291,23 +340,54 @@ class GLPane_drawingset_methods(object): on entry; we'll set it as specified only during this call. If not provided, we don't check it or change it (typically, in that case, caller ought to do something - like we do itself). + to set it properly itself). The value of drawing_phase + matters and needs to correspond to what's drawn by func, + because it determines the "drawingset_cache" (see that + term in the code for details). + + @param whole_model: whether func draws the whole model. + Default True. Permits optimizations when the model appearance + doesn't change since it was last drawn. """ # todo: convert some older callers to pass drawing_phase # rather than implementing their own similar behavior. + if whole_model: + dset_change_indicator = self._whole_model_drawingset_change_indicator() + # note: if this is not false, then if a certain debug_pref is enabled, + # then if we'd use a drawingset_cache and one already + # exists and has the same value of dset_change_indicator + # saved from our last use of that cache, we assume there + # is no need to remake the drawingsets and therefore + # no need to call func at all; instead we just redraw + # the saved drawingsets. This is never correct -- in practice + # some faster but nonnull alternative to func would need + # to be called -- but is useful for testing the maximum + # speedup possible from an "incremental remake of drawing" + # optimization, and is a prototype for correct versions + # of similar optimizations. [bruce 090309] + else: + dset_change_indicator = None if drawing_phase is not None: assert self.drawing_phase == '?' self.set_drawing_phase(drawing_phase) - self.before_drawing_csdls(bare_primitives = bare_primitives) + skip_dset_remake = self.before_drawing_csdls( + bare_primitives = bare_primitives, + dset_change_indicator = dset_change_indicator + ) error = True res = None try: - res = func() + if not skip_dset_remake: + res = func() error = False finally: # (note: this is sometimes what does the actual drawing # requested by func()) - self.after_drawing_csdls( error) + self.after_drawing_csdls( + error, + reuse_cached_dsets_unchanged = skip_dset_remake, + dset_change_indicator = dset_change_indicator + ) if drawing_phase is not None: self.set_drawing_phase('?') return res @@ -343,7 +423,9 @@ class GLPane_drawingset_methods(object): finally: part.after_drawing_model(error) return - self._call_func_that_draws_model( func2, bare_primitives = bare_primitives) + self._call_func_that_draws_model( func2, + bare_primitives = bare_primitives, + whole_model = False ) return _dset_caches = None # or map from cachename to persistent DrawingSetCache @@ -357,10 +439,13 @@ class GLPane_drawingset_methods(object): # (not very important -- could reduce VRAM in some cases, # but won't matter after loading a new similar file) - def _draw_drawingsets(self): + def _draw_drawingsets(self, + reuse_cached_dsets_unchanged = False, + dset_change_indicator = None ): """ Using info collected in self.csdl_collector, - update our cached DrawingSets for this frame; + update our cached DrawingSets for this frame + (unless reuse_cached_dsets_unchanged is true), then draw them. """ ### REVIEW: move inside csdl_collector? @@ -382,30 +467,24 @@ class GLPane_drawingset_methods(object): # We own this dict, and can destructively modify it as convenient # (though ownership is needed only in our incremental case below). + if reuse_cached_dsets_unchanged: + # note: we assume reuse_cached_dsets_unchanged is never true + # unless caller verified that the cached dsets in question + # actually exist, so we don't need to check this + assert incremental # simple sanity check of caller behavior + intent_to_csdls.clear() + # set dset_cache to the DrawingSetCache we should use; # if it's temporary, make sure not to store it in any dict if incremental: # figure out (temporary, cachename) - cache_before = self._current_drawingset_cache # chosen during before_drawing_csdls - cache_after = self._choose_drawingset_cache() # chosen now + cache_before = self._current_drawingset_cache_policy # chosen during before_drawing_csdls + cache_after = self._choose_drawingset_cache_policy() # chosen now ## assert cache_after == cache_before if not (cache_after == cache_before): - print "bug: _choose_drawingset_cache differs: before %r vs after %r" % \ + print "bug: _choose_drawingset_cache_policy differs: before %r vs after %r" % \ (cache_before, cache_after) - temporary, cachename = cache_before - # find or make the DrawingSetCache to use - if temporary: - dset_cache = DrawingSetCache(cachename, temporary) - else: - if self._dset_caches is None: - self._dset_caches = {} - dset_cache = self._dset_caches.get(cachename) - if not dset_cache: - dset_cache = DrawingSetCache(cachename, temporary) - self._dset_caches[cachename] = dset_cache - pass - pass - pass + dset_cache = self._find_or_make_dset_cache_to_use( cache_before) else: # not incremental: # destroy all old caches (matters after runtime prefs change) @@ -417,15 +496,18 @@ class GLPane_drawingset_methods(object): # make a new DrawingSetCache to use temporary, cachename = True, None dset_cache = DrawingSetCache(cachename, temporary) + del temporary, cachename pass - del temporary, cachename + if dset_change_indicator: + dset_cache.saved_change_indicator = dset_change_indicator + persistent_dsets = dset_cache.dsets # review: consider refactoring to turn code around all uses of this # into methods in DrawingSetCache # handle existing dsets/intents - if incremental: + if incremental and not reuse_cached_dsets_unchanged: # - if any prior DrawingSets are completely unused, get rid of them # (note: this defeats optimizations based on intents which are # "turned on and off", such as per-Part or per-graphicsMode @@ -494,7 +576,39 @@ class GLPane_drawingset_methods(object): return - def _choose_drawingset_cache(self): #bruce 090227 + def _find_or_make_dset_cache_to_use(self, policy, make = True): + """ + Find, or make (if option permits), the DrawingSetCache to use, + based on policy, which can be None if not make, + or can be (temporary, cachename). + + @return: existing or new DrawingSetCache, or None if not make and + no existing one is found. + """ + # split out, bruce 090309 + if not make and not policy: + return None + temporary, cachename = policy + if not make: + return not temporary and \ + self._dset_caches and \ + self._dset_caches.get(cachename) + else: + if temporary: + dset_cache = DrawingSetCache(cachename, temporary) + else: + if self._dset_caches is None: + self._dset_caches = {} + dset_cache = self._dset_caches.get(cachename) + if not dset_cache: + dset_cache = DrawingSetCache(cachename, temporary) + self._dset_caches[cachename] = dset_cache + pass + pass + return dset_cache + pass + + def _choose_drawingset_cache_policy(self): #bruce 090227 """ Based on self.drawing_phase, decide which cache to keep DrawingSets in and whether it should be temporary. diff --git a/cad/src/graphics/widgets/GLPane_rendering_methods.py b/cad/src/graphics/widgets/GLPane_rendering_methods.py index 0b6750057..ac00d0e86 100644 --- a/cad/src/graphics/widgets/GLPane_rendering_methods.py +++ b/cad/src/graphics/widgets/GLPane_rendering_methods.py @@ -524,7 +524,7 @@ class GLPane_rendering_methods(GLPane_image_methods): _drawingset_temporary_cachenames = { 'temp': True } - def _choose_drawingset_cache(self): #bruce 090227 + def _choose_drawingset_cache_policy(self): #bruce 090227 """ Based on self.drawing_phase, decide which cache to keep DrawingSets in and whether it should be temporary. @@ -552,7 +552,7 @@ class GLPane_rendering_methods(GLPane_image_methods): drawing_phase = self.drawing_phase if drawing_phase == '?': - print_compact_stack("warning: _choose_drawingset_cache during '?' -- should clean up: ") ###### + print_compact_stack("warning: _choose_drawingset_cache_policy during '?' -- should clean up: ") ###### cachename = self._drawingset_cachename_from_drawing_phase.get(drawing_phase) if cachename is None: cachename = 'temp' @@ -992,6 +992,33 @@ class GLPane_rendering_methods(GLPane_image_methods): self._call_func_that_draws_model( func) return + def _whole_model_drawingset_change_indicator(self): + """ + #doc + """ + ## BUG: + # + # The following implementation is not correct: + # - view changes are not taken into account, but need to affect + # drawingsets due to frustum culling + # - doesn't include effect of visual preferences + # (workaround for both: select something to update the display) + # + # Also, current code that uses this has bugs: + # - ignores necessary drawing not inside DrawingSets + # - jigs, dna/protein styles, overlap indicators, atom sel wireframes, bond vanes... + # - extra drawing by graphicsMode, e.g. expr handles, dna ribbons + # - probably more + # + # So it is only used when a debug_pref is set. + # Another comment or docstring has more info on purpose and status. + # [bruce 090309] + return self.part and \ + (self.part, + self.assy.model_change_indicator(), + self.assy.selection_change_indicator() + ) + def _do_other_drawing_inside_stereo(self): #bruce 080919 split this out """ [might be misnamed -- does not (yet) do *all* other drawing diff --git a/cad/src/graphics/widgets/ThumbView.py b/cad/src/graphics/widgets/ThumbView.py index 8857bfd86..05e3e44e6 100755 --- a/cad/src/graphics/widgets/ThumbView.py +++ b/cad/src/graphics/widgets/ThumbView.py @@ -165,7 +165,8 @@ class ThumbView(GLPane_minimal): """ def func(): self.drawModel() - self._call_func_that_draws_model( func, drawing_phase = 'main' ) + self._call_func_that_draws_model( func, + drawing_phase = 'main' ) return def drawSelected(self, obj): @@ -181,7 +182,9 @@ class ThumbView(GLPane_minimal): """ def func(): self.drawSelected(obj) - self._call_func_that_draws_model( func, drawing_phase = 'selobj' ) + self._call_func_that_draws_model( func, + drawing_phase = 'selobj', + whole_model = False ) ### REVIEW: should we use _call_func_that_draws_objects instead? # That requires passing a part which contains obj, # and I don't know for sure whether there is one, or how to find it |