Timing how long it takes to create and show your editor?

Is there a good way to measure the amount of time it takes (in a host) from when you tell your plugin to open its editor to when it actually appears for the user?

I can store a time when createEditor() is called, but how to tell when it hits the screen?

It’s ultimately up to the OS how exactly your UI gets displayed to the screen so I imagine it’d be pretty tricky to work out at exactly what point the UI is displayed on the user’s monitor. It’ll be some time after your first paint() call, and probably some time before the next, but outside of that it’s up to the OS.

That being said, the most costly thing will be the actual rendering of your UI so you could just measure how long it takes to take a snapshot of the editor and take that as a rough estimate to how long it takes to show to the user:

auto duration = 0.0;

{
    const juce::ScopedTimeMeasurment timer{ duration };
    myEditor.createComponentSnapshot(myEditor.getLocalBounds());
}

DBG(duration);
1 Like

@ImJimmi - thanks. But I don’t think I’m talking about the painting/rendering.

For example, using the AudioPluginHost (Mac), I place an instance of my (Debug version) VST3 in the window. I then double-click it and it takes around 3-4 seconds (it seems) before the window opens. The Release version is quicker, of course. (It’s a pretty complicated GUI with many pages in TabbedComponents). But it seems longer to me recently and so I was wondering if there was a way to time it from createEditor() (the double-click) to the point at which the window opens. I couldn’t come up with anything. Component::broughtToFront() doesn’t get called in a plugin…

From my experience, if editor opening time take longer this is likely because the constructor and maybe the resize() function of your editor component takes that long. Everything else usually happens without any noticeable delay.

If you want to investigate where delays come from, I’d recommend you to use a profiler, on macOS the Xcode bundled Instruments time profiler is the best choice. You can also use it to measure custom time intervals by placing singposts in your code which will show up on the timeline of your profiling session. This helps a lot by finding the relevant events in a profiling session. Here you’ll find more information

https://developer.apple.com/documentation/os/logging/recording_performance_data?language=objc

1 Like

I believe that you could get a good approximation of that interval this way:

  1. record a “start” timestamp right before createEditor() is called, or at its very beginning.
  2. at its very end, trigger an AsyncUpdater, that will post a message on the event loop, and…
  3. in the handleAsnycUpdate() that will follow, record your “stop” timestamp and do the math.

The passage through the AsyncUpdater lets all messages generated by editor creation (for example, paints and whatnot to show it onscreen) be processed by the event loop BEFORE the message that is appended at the end which triggers the call to the handleAsnycUpdate().

I use a similar trick to display a message to the user, to be sure that it appears only after the editor has been displayed, not before

2 Likes

Thanks. I tried that. What’s weird is it shows 800 to 900 ms elapsed, when I can clearly count over 3 seconds from the double-click to the window opening on screen. Puzzling.

result:
elapsed 859.081

I guess what’s happening after returning the editor is a chunk of the time.

AudioProcessorEditor* PluginProcessor::createEditor()
{
    startTime = Time::getMillisecondCounterHiRes();

    auto ed = new MainComponent ( foo… );

    triggerAsyncUpdate();

    return ed;
}

//==============================================================================
class PluginProcessor : public AudioProcessor, public AsyncUpdater
{
public:
    
    PluginProcessor();
    ~PluginProcessor() override;
    
    double startTime;
    void handleAsyncUpdate() override
    {
        double nowTime = Time::getMillisecondCounterHiRes();
        double elapsedTime = nowTime - startTime;
        DBG ("elapsed " + String(elapsedTime));
    }