﻿id	summary	reporter	owner	description	type	status	priority	milestone	component	version	resolution	keywords	cc	blockedby	blocking	notify_on_close	platform	project
1041	Surprising slowdown when drawing multiple drawings	Tristan Croll	Tom Goddard	"I have a Distance_Restraint_Mgr class, where the relevant code for drawing the restraints looks like this:

{{{

class Distance_Restraint_Mgr(Model):
    def __init__(self, model, c_pointer=None):
        '''
        Prepare a distance restraint manager for a given atomic model.
        '''

        session = model.session
        if not hasattr(session, 'isolde_changes') or session.isolde_changes.deleted:
            ct = self._change_tracker = Restraint_Change_Tracker(session)
        else:
            ct = self._change_tracker = session.isolde_changes

        if c_pointer is None:
            f = c_function('distance_restraint_mgr_new',
                args=(ctypes.c_void_p, ctypes.c_void_p,),
                ret=ctypes.c_void_p)
            c_pointer =(f(model._c_pointer, ct._c_pointer))
        super().__init__('Distance Restraints', model, c_pointer)
        self._prepare_drawing()
        self.model = model
        model.add([self])
        self._model_update_handler = self.model.triggers.add_handler('changes', self._model_changes_cb)

   #SNIP#

    def _model_changes_cb(self, trigger_name, changes):
        update_needed = False
        changes = changes[1]
        if changes.num_deleted_atoms():
            update_needed = True
        atom_reasons = changes.atom_reasons()
        if 'display changed' in atom_reasons or 'hide changed' in atom_reasons:
            update_needed = True
        if 'coord changed' in atom_reasons:
            update_needed = True
        if update_needed:
            self._update_graphics()

    _show_bd = True
    _show_td = True

    def _update_graphics(self):
        bd = self._bond_drawing
        td = self._target_drawing
        visibles = self.visible_restraints
        n = len(visibles)
        if n==0:
            bd.display = False
            td.display = False
            return
        bd.display = self._show_bd
        td.display = self._show_td
        self._update_bond_drawing(bd, visibles, n)
        self._update_target_drawing(td, visibles, n)

    def _update_bond_drawing(self, bd, visibles, n):
        bd.positions = visibles._bond_cylinder_transforms

    def _update_target_drawing(self, td, visibles, n):
        td.positions = visibles._target_transforms

}}}

The two drawings are simple coaxial cylinders (one uncapped, with 20 triangles, the other capped, with 40 triangles). In my test case I have 50 restraints drawn. Under these circumstances, the `_update_graphics()` method takes about 160 microseconds. 

If I have a live simulation of IGF-I running in stick representation (with ~32 coordinate updates per second on my Linux machine), the graphics runs at a steady 55-60 fps with just the molecule shown. Adding the Distance_Restraint_Mgr crashes it to 30 fps - meaning that about 15ms per frame is disappearing somewhere. Setting either one of _show_bd or _show_td to False brings the frame rate back to a somewhat variable 50-60fps, while setting both to false (so the transforms are calculated but the drawings aren't actually drawn) gives a framerate indistinguishable from that with only the molecule. 

With just the molecule present and simulating, `session.view.report_framerate()` gives a value around 400. With both `Distance_Restraint_Mgr` drawings visible it drops to 275 - only about 1ms difference, so that's not the cause. 


To thicken the plot I have my Position_Restraint_Mgr, which also has two drawings: a ""pin"" and a dashed pseudobond. The pin has 88 triangles and the bond 72, so with 533 restraints present they account for ~40,000 triangles. They're updated using the following methods:

{{{
    def _update_pin_drawing(self, pd, visibles, n):
        from chimerax.core.geometry import Places
        xyzr = numpy.ones((n,4), numpy.double)
        xyzr[:, :3] = visibles.targets
        pd.positions = Places(shift_and_scale=xyzr)

    def _update_bond_drawing(self, bd, visibles, n):
        from chimerax.core.geometry import Places
        bd.positions = Places(opengl_array = visibles._bond_cylinder_transforms)
}}}

... which combined time to ~200 microseconds in this case. Both are currently run on every coordinate update (although that's not actually necessary for the pin drawings, but I haven't gotten around to teasing out the specific cases where they need redrawing). With both shown, the framerate is down to 14 fps. With the pin drawing hidden, it's 14fps. With the bond drawing hidden and the pin drawing shown, it's 55-60 fps.

Visually, everything *looks* right, so I'm struggling to understand where this ~100-fold performance hit is coming from."	defect	closed	major		Graphics		fixed						all	ChimeraX
