6

If you create an NSView and a custom NSOpenGLContext on macOS Mojave, the window is not being rendered to until it is being resized. But everything works if you use NSOpenGLView instead. I see lots of hacks that resize the window programmatically (http://people.bath.ac.uk/abscjkw/ComputerPrograms/C++programs/OpenGL/MojaveOpenGL.cpp) before rendering into it or call [NSOpenGLContext update] twice (https://github.com/go-gl/glfw/pull/229/commits/9e6129a572227a13ff9acb4904443d2ae7d66e77), but they seem really hacky and unreliable.

Elviss Strazdins
  • 1,404
  • 14
  • 30

3 Answers3

5

I disassembled Apple's frameworks and found out that they have changed how OpenGL rendering works on Mojave. It seems that even if you disable layered backing by setting NSView's wantsLayer to NO, NSView still creates and attaches a layer to your view on Mojave. Resizing the window before rendering to it works because that usually results in a call to [NSOpenGLContext update]. Calling the update twice works, because in the first frame NSView has no layer attached to it and the update method does nothing but on the second frame, the layer is there and [NSOpenGLContext update] actually initializes the framebuffer.

So the solution is to call the [NSOpenGLContext update] manually whenever the layer of the NSView is set, like this:

@interface OpenGLView: ViewMacOS
{
    NSOpenGLContext* _openGLContext;
}
@end

@implementation OpenGLView

-(void)setLayer:(CALayer*)layer
{
    [super setLayer:layer];

    [_openGLContext update];
}

@end

I tested it and it works both on Mojave and on older versions of macOS ([NSView setLayer:] is not being called on macOS 10.13 and older versions). Here is the complete commit I made for the Ouzel engine: https://github.com/elnormous/ouzel/commit/7e708636189d970bad6b013ecd5375cfe693f3f3

Elviss Strazdins
  • 1,404
  • 14
  • 30
  • I have a little difficulty to put [_openGLContext update] in setLayer() callback function. Therefore I call [_openGLContext update] at the beginning of rendering of each frame. It works well. Just not sure how much overhead it will cause. – Hongkun Wang Oct 26 '18 at 02:55
  • @HongkunWang I suggest finding a way to do it in the setLayer. Here is the disassembly of [NSOpenGLContext update]: https://pastebin.com/TXnUDGMn. As you can see it detaches and attaches offscreen surfaces, clears the drawable, sets a virtual screen and initializes new surface. I measured it, it took from 170 to 1900 microseconds (average 470) on my MacBook Pro Mid 2015 with macOS Mojave and 140 to 600 microseconds (average 171) on my MacBook Pro Late 2011 with macOS High Sierra – Elviss Strazdins Oct 27 '18 at 12:41
  • Thanks for your detailed information, it does cause some performance penalty. Just not sure "how long" we can use OpenGL on macOS and iOS anyway, I have decided start working on Metal API soon. – Hongkun Wang Nov 26 '18 at 02:38
  • 1
    It also works with NSOpenGLView, after I added the setLayer method, everything is ok! It is the best solution, thanks you! – Art Golf Jan 09 '19 at 03:50
  • @ElvissStrazdins can you show what changed from 10.14.4 to 10.14.5 version of mojave. Whenever i resize my nsopenglview the view is rendered at wrong poistion it appears shifted upwards. Also if you can help me understand how you disassembled the apple framework code so that i can find it of my own. Thanks very much in advance, i have been trying this for 3 weeks now. – Amit Hooda Jul 30 '19 at 05:05
0

On my NSView subclass which manages the NSOpenGLContext manually I needed to call NSView.displayIfNeeded instead of NSView.display for buffer swapping. Overriding NSView.setLayer and calling NSOpenGLContext.update did not help.

Please note my usage is like SDL where I use a custom run loop, so this may not have been the case in the posters program.

GenericPtr
  • 677
  • 1
  • 8
  • 18
0

After updating to Mojave 10.14.3 and Xcode 10.1 this issue has been fixed.

Raydelto Hernandez
  • 2,200
  • 2
  • 16
  • 11