Profile NetController.
Use struct.unpack instead of a loop of ord. Drops average execution time of read_datagrams in half on my machine.
Cache QByteArrays to avoid allocating one for every incoming datagram (most messages we receive from firemix are identically sized).
Index SceneController fixtures by strand and pre-sort them to avoid searching and sorting in the hot update path.
Don't update the fixture widgets from the model. Rely on the global scene update triggered by NetController.
Make Fixture._pixel_data a flat list instead of a list of tuples. This allows an easy optimization of the hot path of set_flat_array to just be assignment when the color mode is RGB8. This change drops the average cost of processing a network packet from 30ms to about 8ms on my machine.