kanato's picture

Fullscreen API

So the approach to creating a full screen application is to use GameWindow, call DisplayDevice.SetResolution and then set GameWindow.FullScreen = true (or WindowState to FullScreen). This approach is (apparently) less than ideal on the Mac. The DisplayDevice.SetResolution also has the side-effect of repositioning/resizing all other windows; not a major issue but something that is not necessary.

The simplest approach on the Mac is to call aglSetFullScreen on a context to make the graphics context a full-screen context and set the resolution with one function call. The full screen context does not reposition windows but it will resize the display and restore the resolution when the context is destroyed, so there is no need for DisplayDevice.RestoreResolution to be called since the resolution will switch back when the context is destroyed. Also, in order for aglSetFullScreen to work, a fullscreen flag must be passed in the context attributes when creating it, and a display device identifier has to passed too.

There's a catch to aglSetFullScreen though... it doesn't seem like there is a straightforward way to make a context not full screen after making it full screen. What I've seen people recommend on some mac developer forums is to create a separate context for doing full screen rendering, then enable/disable each context when switching between fullscreen and windowed. I think emulating this approach on other platforms would make a simpler interface, so here is what I am proposing:

* Remove DisplayDevice.SetResolution, make GameWindow.FullScreen a readonly property (change name to IsFullScreen maybe), and maybe replace WindowState with Minimize/Maximize properties and are read/write.
* Add GameWindow.SwitchToFullscreenContext method that takes a DisplayDevice, width, height, etc. for the new resolution. This would create a second context for doing full screen rendering, and store the windowed context. If necessary it would create a window to cover the screen area on other platforms. Also the full screen context will share must share with the old context; so maybe an exception should be thrown if sharing is disabled. The object returned from GameWindow.Context would be dependent on whether the app was running in windowed or fullscreen mode.

(Above where I say remove, I really mean deprecate for now.)

The above would require changing the public API so it's probably something that should be done after 0.9.2 is released, unless we are still fairly far from that release. Aside from this I think the Mac OS stuff is about ready to be merged into the trunk. There are a couple of minor issues to resolve, and I want to do some more testing first which will take a couple of weeks at least. If we want to get this out there for more testing sooner, the macos branch could be merged into the trunk before 0.9.2 is released if we want, without fullscreen support.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

Going fullscreen and changing resolution are two different things: one does not entail the other. Windows and X do not have the notion of "fullscreen contexts" per se (*): there is no fullscreen property you can set on a contex, fullscreen is rather a window state.

(*) Fullscreen windows may be treated differently (page flipping on windows, un-redirection on X + composite), but that's not the point.

As I understand it, there are two issues here:
1. CGDisplaySwitchToMode moves / resizes windows, even though the resolution change is temporary.
2. Go fullscreen with aglSetFullScreen and there's no way back.

I don't think we can do anything about issue #1 (for example, the relevant method on win32 can take a CDS_FULLSCREEN parameter to avoid reordering open windows.)

However, it might be possible to fix #2 without going the multiple context route. According to CGLSetFullScreen, "To exit full-screen mode, call CGLClearDrawable." I am not positive on the difference of AGL vs CGL (AGL builds on CGL?), but maybe there is a way.

Question: Does aglSetFullScreen solve the problem with CGDisplaySwitchToMode?

Edit: I found this comment from glfw/lib/macosx/macosx_window.c quite interesting:

633     // Windowed or fullscreen; AGL or CGL? Quite the mess...
634     // AGL appears to be the only choice for attaching OpenGL contexts to
635     // Carbon windows, but it leaves the user no control over fullscreen
636     // mode stretching. Solution: AGL for windowed, CGL for fullscreen.
kanato's picture

So I'm still doing research on this before I respond to the above post, but one quick question has come up:

If switching to fullscreen fails for some reason, should an exception be thrown or should the transition fail silently with some Debug output?

the Fiddler's picture

I think an exception is preferable (inform the developer about the problem and let him decide how to cope).

kanato's picture

I saw that comment in the glfw source code, but it doesn't seem relevant. Glfw actually does the resolution switching/selecting through CoreGraphics (CG), which can be used with aglSetFullscreen, so it seems like they could be using AGL. I can't test out the stretched/nonstretched video modes myself though because my mac only reports stretched video modes. (CG is unrelated to CGL.)

Question: Does aglSetFullScreen solve the problem with CGDisplaySwitchToMode?
It looks like aglSetFullScreen was originally written to use the deprecated DisplayManager API, but CG provides equilvalent functionality. I can call CGDisplayCapture to capture the display before resizing to avoid resizing other windows, which I suspect is equivalent to what AGL does, but this makes the screen go black because when an app captures the display, it really owns it and nothing else can draw to the screen. I am currently doing this, because I assume the typical behavior for an OpenTK consumer would be to set the resolution and then make the window full screen, and this makes OpenTK play nice with other desktop applications. I think this also allows for the application to quit without restoring the resolution, and Mac OS will take care of it.

CGLClearDrawable
It turns out that calling aglSetDrawable with null has a similar effect. So this solves the problem without creating a second context.

There is still a couple of other issues:
1) When creating a pixel format for an AGL context to go full screen, the AGL_FULLSCREEN must be specified, and as well as a display device handle. At the moment I am just submitting the primary display device, but AFAICT this restricts the context to operate on that device, so it does not allow an app to render full screen to a secondary display. Probably it does not allow for any rendering at all on the secondary display but I can't test this as my mac mini has only a single display adapter.
2) When switching from fullscreen to windowed it is a complicated mess to retain the window size because of the amount of abstraction between switching to/from fullscreen and resolution switching, so at the moment it's not done.

I realize that mode switching and making a window fullscreen are separate concepts on Windows and Linux, but they are pseudo-unified on Mac OS, and I don't see much utility in having an API which allows micromanagement of them separately. (How many games change the resolution but don't go fullscreen?) Most similar API's (SDL, glfw, ClanLib) take this kind of approach. Plus I think it would be good to have an API where we specify which DisplayDevice to go fullscreen on, which might be useful for an application like Golem3D to use a second monitor for fullscreen rendering or something. So I think we should consider unifying these two parts of the API, at least partially.