Opportunities for Performance Optimization - adobe-photoshop/spaces-design GitHub Wiki

Startup

I'm not sure how much this would help, but we always need to know a) the list of currently open document IDs; b) the list of recent files as soon as possible on startup; c) the current window transform; and d) the current tool. Conceivably the plugin could invoke the HTML page with this information right in the query string (i.e., it could load "index.html?docs=1,2,3&recentFiles=foo/bar.psd,foo/baz.psd"... ). The HTML app always makes the same blocking calls to disable various OWL features, enable overscroll, disable target paths, etc. Conceivably there could be some more efficient way to do this.

Font initialization does not actually seem to be terribly slow any longer. It seems to take ~200ms.

Finally, I have the incredibly vague feeling that we regressed at some point on the time it takes to launch Photoshop into Design Space. I have no data to back this up, but it feels very slow to me, even with no open documents

Document initialization

There are two basic phases here: the Photoshop requests to get the model information, and the HTML rendering.

Photoshop requests

On the former, Jesper notes that the vast majority of time in these requests seems to be spent fetching text properties:

Lot of time spent in getting the keyText of the layers (of the time spent in 
LayerElement on getting layer properties, this accounts for 97.4% of the time)
The data load block is about 5.5 seconds, and this means that keyText takes about
5.35 seconds of this.

Quite a bit of time spent on (de)serialization (700-800 MS).
My guess is that it is the text data that is causing the significant serialization cost.

7 seconds between I hand off the data to Cef and until I get a UI redraw update from CEF.
In these 7 seconds Photoshop is idling. See attached graph. First hill is multiget and the
like. Then Idle, then we get texture data from Cef.

startup

I also wonder if a similar problem might exist with the AGMStrokeStyleInfo property, which we attempt to fetch for every layer, and which is very large, but in which we only require a few values. Could there be some mechanism for more efficiently requesting particular sub-properties? Would this be any faster?

HTML rendering

When loading a large document, even after we've gotten all the information from PS, the browser thread becomes completely unresponsive for multiple (~3?) seconds while performing the first render of the view layer. I haven't been able to understand this completely yet, but there seem to be two basic places where time could be spent: in JavaScript and in the DOM. The former could be optimized by making our "virtual" rendering phase more efficient by writing our React components differently. The latter can only be optimized by creating fewer DOM structures. Alternatively, we can do all the same work, but try to break it up into smaller chunks so that the UI does not become unresponsive.

Two separate ideas:

  1. Stream results from PS and render partial results. This means requesting the layers in chunks from Photoshop, and rendering the view as we receive the chunks. Inevitably, this will complicate the UI, and might allow the user to interact with an incomplete document. But, if we can solve those code-complexity and UX problems, this would help us exploit parallelism between the Photoshop and CEF processes, and could improve both the time-to-complete-document and the time-to-first-rendered-document.
  2. Render the layers panel on demand according to scroll visibility. So, for example, we might never actually have more than ~20 rendered layers in the panel at once. This presents similar challenges as the previous option. The code complexity problem is probably slightly less (because this doesn't necessarily require changing how we request information from Photoshop and build models, and is isolated to a single React component), but the UX issue is slightly worse because we have to be vigilant of quick scrolls one way or another. This could actually be sub-divided into two options, depending on whether we want to just build the DOM on scroll demand at most once, or whether we want to actively prune layers that aren't currently visible.

Document selection/activation

Layer targeting

Canvas interaction

Doesn't seem terrible to me, but I can repro a difference with the "Flat Style" PSD by dragging around the iPhone screen layer. Notes from Jesper:

In our meeting with Romania today we were asked about why dragging layers is slower
with the HTML UI showing than without.

I instrumented PS and found the following:
- PerformModalTask is *not* being called during the drag interaction
- SendImmediateNotification is *not* being called during the drag interaction
- We are not receiving any "phone home" messages during the drag interaction
- The Cef timer is active and continues to call CefDoMessageLoopWork (which in turn does stuff with the OS event queue)

So a test could be to decrease the Cef timer frequency and see if that affects drag performance.
As a start we could simply allow Settings.json to override the cef timer frequency.

Zoom in/out

I don't think the issue is in JavaScript per se, but the action descriptor we're executing seems to be calling into a different procedure than whatever zoom in/out is doing in standard PS. Note that in standard PS the canvas animates while zooming, and the operation seems to start more quickly.

In the case of the option-scroll gesture for zooming, I suspect that whatever is slowing down canvas interaction generally (as described above by Jesper) is also at fault here.

Misc

Idle thought: could PS add hashes to its objects (efficiently, somewhat easily) to allow Design Space to make conditional get requests? This would allow us to cache the results of these requests client side across sessions. The hash could be very small since collisions would merely result in a redundant, fully serviced request. (Alternate suggestion: implement HTTP.) Potential use case: the font lists, recent documents list, maybe even document and layer descriptors?