Why does UrhoSharp decrease performance of surrounding Window?

andrekoehlerandrekoehler DEMember ✭✭
edited April 20 in UrhoSharp

I created a Windows Forms project that references UrhoSharp and paints into a Panel of the main Window. The window and Urho both run on the main thread. I derived a class from Urho.Application and from it's OnUpdate override I update a control in the Window and call Invalidate on it. As long as I don't move the mouse the Control is painted smoothly, but as soon as I move the mouse, the Paint-Event is sent only occasionally. Even without the custom painting and without Invalidate, the main window background gets black as soon as Urho.Application.Run is called.

Source Code:
https://drive.google.com/file/d/0B28GqYi1uLDvRnJfb2NGMU51Q3M/view?usp=sharing

Best Answer

Answers

  • EgorBoEgorBo BYXamarin Team ✭✭✭
    edited April 20

    Let me take a look,
    as a workaround you can control the game loop from your code:
    1) Set DelayedStart = true in new ApplicationOptions() { ExternalWindow = form1.Handle, DelayedStart = true }
    2)

            game_ = new Game(this);
            game_.Run();
            while (!game_.IsExiting)
            {
                game_.Engine.RunFrame();
                await Task.Delay(16);
            }
    
  • andrekoehlerandrekoehler DEMember ✭✭
    edited April 20

    Thanks for looking into that. The workaround with the manual game loop exhibits the same issues. (I had to replace await Task.Delay with Thread.Sleep but that should not make a difference).

    I found out that calling System.Windows.Forms.Application.DoEvents() at the end of the Urho.Application.OnUpdate override fixes all the issues except for the black window background.

  • EgorBoEgorBo BYXamarin Team ✭✭✭
    edited April 20

    @andrekoehler said:
    Thanks for looking into that. The workaround with the manual game loop exhibits the same issues. (I had to replace await Task.Delay with Thread.Sleep but that should not make a difference).

    well, actually it should make a difference :smile: with Delay I can't reproduce the issue you mentioned. Sleep sleeps the main thread instead of giving that time to WinForms to render what it needs.

  • andrekoehlerandrekoehler DEMember ✭✭

    If I understand that code correctly, async void restartButton_Click spawns a new Task so the game loop does not run on the UI/main thread anymore. That means all interaction between Urho and the UI have to be marshalled to the appropriate threads and guarded from race conditions with locks and the like.

  • andrekoehlerandrekoehler DEMember ✭✭

    By the way, the black window was caused by setting ApplicationOptions.ExternalWindow to the whole form instead of just the panel.

  • EgorBoEgorBo BYXamarin Team ✭✭✭

    @andrekoehler said:
    If I understand that code correctly, async void restartButton_Click spawns a new Task so the game loop does not run on the UI/main thread anymore. That means all interaction between Urho and the UI have to be marshalled to the appropriate threads and guarded from race conditions with locks and the like.

    The game loop here is in the UI thread (only in case of WinForms and WPF)
    But anyway all actions from WPF/WinForms layout should be dispatched via Urho.Application.InvokeOnMain (in order to do changes when they should be done - inside Update). If you want to work with WPF/WinForms from the game loop - you don't have to dispatch anything.
    PS: https://github.com/xamarin/urho/blob/master/Tests/Playgrounds/Playgrounds.Wpf/MainWindow.xaml.cs#L22
    this line is an exception - it's not wrapped in order to avoid "clear color blinking"

  • andrekoehlerandrekoehler DEMember ✭✭

    You are right, the game loop really does run in the UI thread. I am not familiar with async/await and tested whether invoking an async void event handler on an event would create a new task or not. I tested this in a console application and there the runtime created a new task to execute the event handler. Then, without trying your example, I concluded that the example would do the same. Now I repeated that test in a fresh Windows Forms project and it turns out there is some behind the scenes magic that causes async void event handlers to be queued and invoked on the UI message loop.

    I now managed to run your WPF and Forms examples and noticed that the one thing that really fixes the issues is the line System.Windows.Forms.Application.DoEvents() in UrhoSurface.cs. Without it, the form is not refreshed properly just as in my example.

  • EgorBoEgorBo BYXamarin Team ✭✭✭
    edited April 22

    @andrekoehler said:
    You are right, the game loop really does run in the UI thread. I am not familiar with async/await and tested whether invoking an async void event handler on an event would create a new task or not. I tested this in a console application and there the runtime created a new task to execute the event handler. Then, without trying your example, I concluded that the example would do the same. Now I repeated that test in a fresh Windows Forms project and it turns out there is some behind the scenes magic that causes async void event handlers to be queued and invoked on the UI message loop.

    This is actually how SynchronizationContext works :smile: In Console Apps it just posts to a thread from the ThreadPool.

Sign In or Register to comment.