-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance: Stretch and translate instead of redraw #1006
Comments
Motivating example: This multiple sequence alignment visualization often is very laggy. Looking at the fame chart, we see that most of the time is spent drawing after zooming. |
Oooh that's cool! What is this graphics debugger called? I do remember reading that small Here's my current hypothesis for why things are slow:
override drawTile(tile: Tile) {
// every time drawTile() is called all of the graphics are cleared and are redrawn
tile.graphics?.clear();
tile.graphics?.removeChildren();
...
} Instead of redrawing everything at every frame, what certain HiGlass tracks do is to scale/move the graphics. The gist of it is this: override drawTile(tile: Tile) {
// every time drawTile(), the graphics are completely redrawn only some of the time
if (this.isSmallZoomChange()) {
scaleGraphics(tile.graphics)
} else {
tile.graphics?.clear();
tile.graphics?.removeChildren();
redrawEverything(tile.graphics);
}
...
} Here is similar logic in HiGlass. Does this make sense? |
This is spectre.js, a Chrome extension. For desktop there are some much more comprehensive tools, like RenderDoc, but they don't usually support WebGL. What you describe definitely sounds plausible. With the higlass approach, do you mean this stretchRects function (and I assume there are equivalent ones for different primitives)? I'll have to dig around more, because the abstraction here is not yet clear to me, i.e., what's behind the graphicsAccessor and how it gets translated through PIXI to actual draw calls. I've got two more thoughts:
I guess the question is how PIXI internally deals with this scaling graphics primitives, which is something I still need to dive into. |
Looked more into this. I made a separate example to test out two approaches to drawing with the PIXI.Graphics objects. Essentially I made two versions of the same graphic with some animation to simulate user panning:
pixi-test-smaller.movCode: https://github.com/dvdkouril/pixi-graphics-playground Personally, I don't see a difference. I would expect that the second approach would be faster, but even with the approach used right now in Gosling I can smoothly animate 10.000 objects with no problem. It doesn't seem to me that the problem here is with clearing the Graphics object every frame and then remaking the graphics primitives from scratch. I would guess that there's more work that happens on Gosling side after each pan/zoom that causes the performance drop. |
Nice experiment! Indeed it doesn't seem that there is much difference. I see that you are using PIXI@7 here. Gosling uses PIXI@6. I know that 7 was a pretty big modernization rewrite so I wonder if that would make a big difference. Another difference I see is that in Gosling there are many Graphics elements (one per tile), whereas here there is only one which the 10,000 rectangles get drawn to. |
Hm, that's a good idea, I'll try it out with v6. The second thing should be also pretty easily testable. |
Okay, one of those things isolated: with pixi.js@6.3.0 I'm getting the same results: pixi-v630-smaller.mov(I'm assuming the flickering with 10k objects is just aliasing in action...) |
The second thing tested: still 10k of rectangles but divided into many Graphics objects. pixi-heatmap-smaller.movI did have to do a bit more to free up the allocated child Graphics objects, to prevent memory leaking and crashing the browser. But I would assume this is covered in Gosling already:
All in all, still doesn't seem like rendering is the issue here. I'll probably start looking generally into what happens after interaction. |
Yeah that looks smooth! Hmm so its not just that there are a bunch of I finally got the stretching idea somewhat working in #1012 Here's a rough comparison of zooming around on a laggy visualization. It does seem noticeably faster for some large specs. Before: drawing takes 39% of the time when zooming After drawTile takes 4.5% of the time What I have so far works alright for stretching along the X axis, although it doesn't look great for text or non-square shapes (at least when we allow up to 2X stretching). I still haven't figured out how to stretch in along the Y axis, which is needed for examples like this one https://gosling.js.org/?example=MATRIX |
Goal
Improve the performance of Gosling by reducing the number of times that shapes are redrawn. Instead of redrawing, we can stretch and translate the shapes when appropriate.
Pixi Basics
Reference tutorial
A
PIXI.Container
is like an empty box where you display and group display objects. These includePIXI.Sprites
which renders aPIXI.Texture
(an image), andPIXI.Graphics
which are primitive shapes and lines.For example, all of the marks (except text) in Gosling are
PIXI.Graphics
objects and text is aPIXI.Sprite
.Laying out the terms here to avoid confusion:
Texture:
Sprite:
Graphics:
drawRect
,drawCircle
)Container:
Pixi in HiGlass
Where does the
PIXI.Container
used in HiGlass get instantiated? And how doPIXI.Graphics
objects that Gosling draws things to (such asTile.graphics
) to get added to that container?PIXI.Container
used in HiGlass is created in theHiGlassComponent
. All tracks that use Pixi will eventually add theirPIXI.Graphics
objects to otherPIXI.Graphics
objects which are inside of thisPIXI.Container
.Tile
(the basic unit of data in HiGlass/Gosling) has agraphics
property (Tile.graphics
) which is set to aPIXI.Graphics
object. Gosling adds shapes and lines to this graphics object.TiledPixiTrack
, whichGoslingTrack
extends, adds theTile.graphics
objects toPixiTrack.pMain
which is a child of thePIXI.Container
created inHiGlassComponent
What is the relationship between
GoslingTrack
andPixiTrack
?GoslingTrack
extendsBarTrack
extendsHorizontalLine1DPixiTrack
extendsHorizontalTiled1DPixiTrack
extendsTiled1DPixiTrack
extendsTiledPixiTrack
extendsPixiTrack
extendsTrack
.Track
is the base class for most HiGlass tracks.Stretching and translating instead of redrawing
When a user zooms in, we want the marks that are drawn to simply be stretched and translated, rather than redrawn completely.
Currently when a user zooms or pans, all of the shapes and lines which are drawn to the
PIXI.Graphics
object (Tile.graphics
) are cleared (Tile.graphics.clear()
) and everything is redrawn.In other HiGlass tracks, this is avoided by instead stretching or translating the shapes. For example, there is a function called
stretchRects
that is used by the gene annotation and Bed viewer track that stretches the drawn shapes.We would like to do something similar in Gosling.
When
draw()
gets called in GoslingTrack, we want to stretch and translate the marks when appropriate to avoid redrawing them over and over again.For inspiration:
movedY
andzoomedY
The text was updated successfully, but these errors were encountered: