opentk / opentk

The Open Toolkit library is a fast, low-level C# wrapper for OpenGL, OpenAL & OpenCL. It also includes windowing, mouse, keyboard and joystick input and a robust and fast math library, giving you everything you need to write your own renderer or game engine. OpenTK can be used standalone or inside a GUI on Windows, Linux, Mac.
https://opentk.net
Other
3.22k stars 632 forks source link

SwapBuffers fails for NativeWindow when maximized borderless #1631

Closed typpos closed 1 year ago

typpos commented 1 year ago

Description

When I use SwapBuffers with a maximized borderless NativeWindow, then nothing is swapped until the application loses focus.

The application (appears to) continuously draw into the same buffer with tearing visible despite SwapInterval=1. When the application loses focus, the swap seems to occur and a "very old" image is displayed.

Repro steps

Run this .NET 7, Windows Desktop application.

Warning: This goes full-screen without an ordinary means to exit the application.

using OpenTK.Graphics.OpenGL;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
using System.Runtime.InteropServices;

[DllImport("user32.dll")]
static extern bool WaitMessage();

var s = new NativeWindowSettings {
    API = ContextAPI.OpenGL,
    APIVersion = new Version(4, 6),
    AutoLoadBindings = true,
    IsEventDriven = false,
    Profile = ContextProfile.Core,
    Size = new OpenTK.Mathematics.Vector2i(1024, 576),
    StartFocused = true,
    StartVisible = true,
    Title = "Test",
    WindowBorder = WindowBorder.Resizable,
    WindowState = WindowState.Normal,
};
var w = new OpenTK.Windowing.Desktop.NativeWindow(s);
w.Context.SwapInterval = 1;
w.WindowBorder = WindowBorder.Hidden;
w.WindowState = WindowState.Maximized;
var col = 0f;
w.MouseMove += (o) => {
    if (!w.MouseState.IsButtonDown(MouseButton.Button1)) return;
    col = col > 0.8f ? 0.0f : col + .2f;
    GL.ClearColor(col, col, col, 1.0f);
    GL.Clear(ClearBufferMask.ColorBufferBit);
    w.Context.SwapBuffers();
};
while (true) {
    WaitMessage();
    Application.DoEvents();
}

Please provide the steps required to reproduce the problem

Issue 1: Actual behaviour: Application "reverts" to a previous color. Expected behaviour: Color does not change.

Issue 2: Observe tearing during drag.

This happens only when NativeWindow is borderless AND maximized. The window intentionally is not a full-screen ("monitor") window.

Change the source code a comment out the WindowBorder.Hidden and WindowState.Maximized Settings.

Related information

OpenTK 4.8.0 (nuget) VS 2022 .NET 7.0 Windows 10x64 Build 19045 NVIDIA GTX 1070; driver 536.23

No workaround.

NogginBops commented 1 year ago

Is there a reason you are calling/pinvoking WaitEvents()? And I feel like the repro is kind of broken as it uses Application.DoEvents() which comes from the System.Windows.Forms namespace which is not included?

typpos commented 1 year ago

Is there a reason you are calling/pinvoking WaitEvents()?

It prevents the event loop from running at 100% CPU. Not essential for the repro.

And I feel like the repro is kind of broken as it uses Application.DoEvents() which comes from the System.Windows.Forms namespace which is not included?

Implicit Global Usings. Create a new WinForms .NET 7.0 project with VS 2022 and you don't need a lot of clutter. Not essential to reproduce the issue at hand. https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/

NogginBops commented 1 year ago

And I feel like the repro is kind of broken as it uses Application.DoEvents() which comes from the System.Windows.Forms namespace which is not included?

Implicit Global Usings. Create a new WinForms .NET 7.0 project with VS 2022 and you don't need a lot of clutter. Not essential to reproduce the issue at hand. https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/

Including the implicit using could be a good idea in a repro. Also you are missing any call to functions that handle OpenTK events, without this you can't expect any OpenTK feature to run. You need to call NativeWindow.ProcessWindowEvents() (think of it like Application.DoEvents() but for OpenTK)

typpos commented 1 year ago

Also you are missing any call to functions that handle OpenTK events, ...

Will this address the issue I raised?

NogginBops commented 1 year ago

It might. Your code seems fundamentally broken so I can't really predict what is going to happen. You are not pumping OpenTK events so basically anything in the NativeWindow isn't going to work properly

Why do you have winforms and NativeWindow in the same project?

typpos commented 1 year ago

It might.

Tested, and as expected. it doesn't make a difference, because OpenTK calls straight through to _glfwPollEventsWin32 which does (1) standard event loop; (2) WM_QUIT handling; (3) keyboard state update; and (4) mouse cursor capture, none of which our application is using. A similar event loop pattern is used in our application and it works fine for several years.

Please provide me with an example of a behaviour that is actually needed that comes from the OpenTK ProcessWindowEvents.

Your code seems fundamentally broken ...

Well, it's not. I provided a piece of code that you can use to reproduce the issue, not a production-ready application. In comparison to various other issue submits, I think this fares above average. I'd appreciate if someone would actually try to replicate the issue. Take the snippet and either run it directly, or paste it into an application that suits your definition of "correct".

Why do you have winforms and NativeWindow in the same project?

Because our application is not a game. It uses OpenTK as an OpenGL API wrapper and other than NativeWindow as an OpenGL-enabled hwnd/hdc provider, it makes little use of OpenTK. There are various WinForms dialogs/forms for user interaction. Think "scientific application". NativeWindow is used to display computed imagery and to capture mouse events. Most of the interesting stuff happens in compute and fragment shaders that run for hours or days.

typpos commented 1 year ago

After more digging, I can reproduce the issue with glfw directly with a small modification of their hello triangle example. The issue goes even below glfw and is the result of OpenGL & Windows interaction. Workarounds essentially make the window not exactly the size of the monitor (add a border, oversize, etc).

So, closing.