DinkydauSet / ExploreFractals

A tool for testing the effect of Mandelbrot set Julia morphings
GNU General Public License v3.0
5 stars 1 forks source link

Creating a new tab is slow sometimes and I have no idea why #25

Closed DinkydauSet closed 3 years ago

DinkydauSet commented 3 years ago

I use the windows api message system to send work to the GUI thread, as described here: https://github.com/DinkydauSet/ExploreFractals/issues/23#issuecomment-886596932

This is how keypresses are intercepted. ctrl + T creates a new tab.

    main_form fm(defaultParameters, number_of_threads);
...
    subclass sc(fm);

    //Install a handler that will be called before old window proc, and it determines wheter
    //to pass the controll into the old window proc and the AFTER handler.

    sc.make_before(WM_KEYDOWN, [&fm](UINT, WPARAM wParam, LPARAM, LRESULT*)
    {
        // This is not a good solution. This only checks if the key is pressed right now. If the program hangs and the user presses ctrl+T, by the time the keypress for T is handled, ctrl is no longer pressed and it will be handled as if the user pressed just T.
        auto isDown = [](WPARAM key)
        {
            SHORT keyState = GetAsyncKeyState(key);
            return ( 1 << 15 ) & keyState;
        };

        bool ctrl = isDown(VK_CONTROL);
        bool shift = isDown(VK_SHIFT);

        if (ctrl) {
            //Override normal behavior when ctrl is pressed.
            //Returning true means control is passed to the old window proc.

            tabbar<string>& tbar = fm.tabs.bar;

            switch (wParam)
            {
                case 'T' : {
                    //create new tab
                    cout << "CREATE TAB WITH WINDOWS API MESSAGE IN THREAD" << std::this_thread::get_id() << endl;
                    //fm.create_fractal_tab(fm.defaultParameters, fm.defaultParameters.get_procedure().name());
                    return false;
                }
...

Creating a new tab takes about 0,3 seconds which feels slow.

Strangely enough, creating a tab by clicking the new tab button is much faster. It takes only 0,05 seconds.

What I have found:

  1. It's not because of different threads. Both CTRL+T and the new tab button click event are handled by the same thread. Proof: I used print statements like this: cout << "CREATE TAB WITH WINDOWS API MESSAGE IN THREAD" << std::this_thread::get_id() << endl; It's the same thread ID.
  2. It's not inherent to the event. An event can be emitted programmatically like this: API::emit_event<arg_click>(event_code::click, fm.newTabButton, arg_click()); Doing that at CTRL+T instead of calling create_fractal_tab directly is also slow.
  3. The resolution has a big influence. It takes more time the higher the resolution is. (small window: 0,05 seconds, maximized: 0,3 seconds). I can see the elements appear pixel by pixel (or line by line). The time taken when clicking the new tab button appears to be mostly unrelated to resolution. It's about 0,05 seconds when the window is either maximized or small (maybe 0,01 seconds slower when maximized).
  4. The specific function that takes almost all of time is nanas collocate, used in my function create_fractal_tab.
  5. Maximizing the window is also slow.
  6. It's not because of the lock used in the subclass class that I use to intercept windows api messages. I put the lock_guard in a comment: //lock_guard lock(mutex_); and it's still slow.
  7. CTRL+T and the new tab button make the screen flicker, but in a different way. In both cases, the newly created fractal tab is completely black at first (which indicates unnecessary refreshing of the screen). CTRL+T is slower overall, but shows the settings panel faster.
DinkydauSet commented 3 years ago

The solution is to use a nana::detail::bedrock::root_guard. ashbob999 here helped me to use it: https://github.com/cnjinhao/nana/issues/631

The root_guard disabled screen refreshing until it's destroyed. Nana's own event handlers use it, but only sometimes (which is inconsistent).

Something to keep in mind while using root_guard: if you create 2 root guards and one gets destroyed, refreshing gets re-enabled even though the second root_guard is still not destroyed. That means using root_guards doesn't guarantee there won't be any refreshing.

I still have that problem with tab colors. The active tab has a different color: image

I now use a root_guard when creating a tab with CTRL+T. During the existence of that root_guard, I do:

bar.append(title, *new_panel);

Here append is a nana function, after which the screen refreshes. I think that's because nana uses a root_guard and destroys it, even though my root_guard still exists. That happens before I change the color of the new tab, so the wrong color is visible for about 1 frame and is then changed immediately. It's a little annoying. It would be nice if there was a way to hard disable refreshing, for example by counting the number of existing root_guards.