Analysis
Overview
The Sophie2 author's preview mode is really slow. It must be optimized so that it will be usable.
Task requirements
Look for and fix performance issues related to preview mode and reader. Describe the changes that will be made and some possible future improvements if found. For every issue found, explain why it is a problem, so that for the next revision could serve as manual what to look for. If possible, DO NOT change any logic and model things. It is well known that the model is fast enough, so the less it is changed, the smaller the possibility of breaking something is. Here are several main aspects that must be optimized:
- NativeAudioOutput is the class which performs changes on a regular basis in preview mode. It actually uses BookView.setTime() method to change the book's time, then gets some audio chunks. This method is incredibly slow, because it sets the time of every view in the book hierarchy, which causes updates in particularly every part of Sophie author that depends on the current book. In a page with 3 chained frames it needs about 800ms to complete (on my PC). For a smooth media playing with about 25 fps, a step should be performed for less that 40ms (примерно).
- A well known fact is that adding the timelines palette in the trunk reduced greatly the performance on both author and preview mode. Investigate the palette and the timelines hud.
- Try reducing as much as possible the time dependencies in the views, since their time will change at least 25 times a second. This includes ElementView and all derivatives.
- PagePreviewPalette and AllResourcesPalette depend on the current book, so they could slow down a lot setting the preview window as current. Try making them update as rarely as possible.
- Changing the current page for a book with more than 20 pages is a very slow operation.
- Text frames also have a lot of performance problems. There should be a task dedicated to text chaining, creating layout, ans so on. So, for this task just try to reduce the calculations if time has changed but the layout should be the same.
- Look for potentially problematic operations for preview mode, like frame move, text wrapping, etc.
Task result
Much more usable preview mode. For example, playing smoothly media in a book with at least 25 pages, templated objects, chained text and other frame types. Links must also work fast enough. The reader uses the same mechanism for playing a book, so it has the same requirements.
Implementation idea
Use auto properties to reduce unnecessary calculations, memoization of some elements (be careful with it) is needed. BFS`s in the views are dangerous for the performance (findNearestElement).
Related
(Add links to related tasks that could be useful or helpful.)
How to demo
Start author, create a book with 50 pages (could use script: "var i; for(i=0;i<50;i++) book.newPage()"), fill with various content, open preview and enjoy.
Design
- TimelinesPalette. This palette re-draws very slowly. There are 2 issues there: first, the default zoom is 1ms, so the timeline is too large. Second, the activation channel is drawn pixel by pixel, which makes thousands of iterations every time something is changed. The first thing to do there is to remove abilities to select such a zoom - I don't think it's needed, since you have to scroll several screens in order to see the timeline. So, the first 3 entries will be removed and default will be 500ms. Second, take the getOptions() call out of the loop which draws the activation channel, it is the slowest part of the drawing.
- HeadTextFrameView. This one contains textLayout() - a prop, which is recomputed when time changes or when something in the page is moved. The result is making a new HotLayout - a very slow operation.
- Put a simple 'if' in this prop - if the text and the areas are the same, return the last layout. This makes sense in preview mode, when we have no changing locations and resizing frames.
- Change getChainedFrames to chainedFrames() - autoprop. This method searches through all the frameViews in the current book, so a bigger book will be faster with prop.
- In getAreas() every HeadTextFrame calculates the wrapped areas of all the frames from the chain. This can be potentially optimized if every text frame calculates its wrapped area, and the text just gets the needed information. So, move the calculations in AutoProp.
- BookView. Create autoProp currentPage(), which holds the resourceref of the current page. All the methods which depend on the timestate().getcurrentPageView() should use this. BookView.setTime() sets a new value of timeState(). So, depending on it when interested only in the current page is useless.
- ElementView. It has a sceneElement(), which has setupElements(). It depends on the current time, but the only thing it's interested in is the visibility and activity. Make props isVisible() and isOn(), which recompute these things. sceneElement should depend on the props, so it will re-setup only when it hides/shows/deactivates/actuivates.
- FrameView. Create a prop which computes the location of the frame in BoundMode.CONTENT. Also, make a method which uses the computed value to return the top left point in some different bound mode. Then, make computeLocalRect() and setupSceneDynamic() use it. The results are good :)
- ScenePageLogic. In the selection logic, put a simple check "!pwa.getSel().getSelected().equals(toSelect)" before the last line which performs the selection. The resultcan be demonstrated when you make a selection rectangle which covers a frame. Without this line, it selects again and again the frame on every move, with it it only makes some checks and does not select anything. Quite faster.
- ListPalette. This one has a prop items(), which is a listProp. When something is changed it depends on, it recomputes. But if the newly computed listProp has more than 2 different elements, it fires events that all the elements are removed 1 by 1, then the new elements are added again 1 by 1. The JList's setup method which tracks this prop, recieves n*n signals to recompute. Its setup method iterates through all the items and does some stuff. So, when you switch from a book with 100 pages to another one with 100 pages, the JList of the pagePreviewPalette makes 10K iterations. AllResourcesPalette does even more if the pages are not empty. So, replace the listProp items() with Prop<ImmList<ListPaletteItem>> and do so for every derivative.
- PwaSelector. It has a listprop called lastSelected(). It's private, but there are public methods which use it. For example, getSelected(). And so, all the haloMenus depend on this list Prop, which just like in the list palette, fires lots of changes. Replace it also with a prop<ImmList<ElementView>>. The result may best be visualized when you have lots of frames, select them all and then click on the page in order to deselect them.
The branch is in branches/private/kyli/myBranch.
Implementation
The implementation is made according to the design in the branch. Additional things done : TemplateInfoPersister, a bugfix for the timelines palette (when you point to the upper left corner, the tooltip cannot be calculated). The preformance wteaks also affect the author, so it is a bit faster now (changing doc windows, deselecting multiple elements, etc). What is still not done:
- Text performance: although it is part of another task, the another task will rather be related to text layout performance. But now, for every time change, every text frame calculates its "wrappedArea()" prop, which is time consuming. It also depends on the frames count in the page, so currently a page with 20 text frames in preview mode is 7-8 times slower than we can afford.
- The timelines palette draws some channels point by point (activation, media state). It can and must be optimized (the more frames we have, the more channels we draw).
- Some props like lastSelected(pwaSelector) and latelyAccessedDocuments(DefaultMainWindow) are being set fron the outside in 2 steps, so they cause twise as mucha updates. This can be optimized by using mechanism for not notifying about some changes.
- In the next revision, again everything should be searched for time-dependent props and the dependencies should be reduced.
- Commited to trunk in [7893].
Testing
(Place the testing results here.)
Comments
(Write comments for this or later revisions here.)