1

I have a Mac OS X (10.9) application with a custom NSView for my main NSWindow's contentView property. The only thing the custom view does is override drawRect: so that it's transparent. Transparency is required so that my NSOpenGLView is visible (see below):

/* Transparent NSView Subclass */
- (void)drawRect:(NSRect)dirtyRect
{
  NSLog(@"Drawing Rect");
  [[NSColor clearColor] set];
  NSRectFillUsingOperation(dirtyRect, NSCompositeClear);
}

The contentView has an NSOpenGLView as a subview, with its surface order set to -1 (it's 'below' the main NSWindow, which is also transparent):

/* NSOpenGLView subclass: */
GLint order = -1;
[self.openGLContext setValues:&order forParameter:NSOpenGLCPSurfaceOrder];

I then instantiate a WebView and place it as a subview of this custom view:

_webview = [[WebView alloc] initWithFrame:frame];
[_webview setMaintainsBackForwardList:NO];
[_webview setTranslatesAutoresizingMaskIntoConstraints:NO];
[_webview setDrawsBackground:NO];
[_window.contentView addSubview:_webview];

The WebView is a small box in the window (on top of the NSOpenGLView) which is used to login to a service. Once login happens it should disappear, showing only the NSOpenGLView.

Here's the problem: when I call [_webview removeFromSuperview]; the contentView does not redraw; therefore, the WebView is still drawn on the screen (although not responsive to mouse or keyboard). If I use my mouse to resize the window that everything is in, the contentView redraws (I see the message in the logs) and the WebView's content disappears. Also, if I don't add the NSOpenGLView as a subview, the WebView goes away as it should.

Shouldn't the contentView fire a drawRect after a subview is removed? Even when I used setNeedsDisplay: or setNeedsLayout: after removing the WebView the contentView still didn't redraw.

brianfoshee
  • 37
  • 1
  • 7

1 Answers1

1

Apple's advice these days is to use Core Animation layers to put normal views on top of OpenGL views.

I suspect that the content view has redrawn itself. It draws clear. What you're seeing is the OpenGL surface behind it. The nearly-raw-VRAM nature of OpenGL surfaces may mean that the web view that was drawn on top of it actually modified the contents of the surface. So, I recommend that you force the OpenGL view to redraw itself. If it draws in its -drawRect:, then it should be enough to send it -setNeedsDisplay:. If you do rendering outside of -drawRect: by manually making the context current, issuing rendering commands, and then calling -flushBuffer, then you'll need to do that.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thanks Ken. To setup the use of CA layers, would I still subclass NSOpenGLView and use `setNeedsLayer:`, or do I need to subclass NSView and setup NSOpenGLContext manually? Also, in the CALayer case, is it necessary to use `NSOpenGLCPSurfaceOpacity` set to `-1` or do the layers remove the need for sending the context behind the window? I tried a manual `flushBuffer` on the `openGLContext` with no luck, and I moved drawing into `drawRect:` with no luck as well. I'm using a `CADisplayLink` for timing in both cases. Any good documentation you can point me to on using CA Layers for this? – brianfoshee May 30 '14 at 11:42
  • Apple provides example code, [LayerBackedOpenGLView](https://developer.apple.com/library/mac/samplecode/LayerBackedOpenGLView/Introduction/Intro.html), that demonstrates. All you have to do is make the `NSOpenGLView` layer-backed (in IB or using `setWantsLayer:YES`) and you can add subviews. No mucking with transparent views or windows or setting the context to be behind the window. The web view should be a subview of the GL view rather than a sibling. – Ken Thomases May 30 '14 at 13:28
  • Thanks. I went that route and moved my rendering code into `drawRect:` since everything has to be drawn in there and on the main thread. This uses more CPU (8% vs 2%) than drawing in the background on the `CVDisplayLink` thread. Is there any way around that? Also, my textures are now black - toggling `setWantsLayer:NO` shows them, but `setWantsLayer:YES` they're black. I copied the `NSOpenGLPixelFormat` from Apple's sample project but no luck. Any idea on that? – brianfoshee May 30 '14 at 15:53
  • On the black textures what I'm finding is that the context isn't actually setup at the time some textures get setup at the beginning of the program when `setWantsLayer:YES` is used. Is there some event I can tie into the determine when the context will be around? And finally, the [discussion here](http://stackoverflow.com/questions/7610117/layer-backed-openglview-redraws-only-if-window-is-resized) mentions that layer-backed NSOpenGLViews are outdated and recommends setting up the layers manually. I may try that route, although it'll be more refactoring to get there. – brianfoshee May 30 '14 at 16:15
  • Override `-prepareOpenGL` to do work after the context has been set up. – Ken Thomases May 31 '14 at 08:03