Browse Source

cocoa: support windowless offscreen rendering on macOS

Fixes: #183
rdb 8 years ago
parent
commit
ef7f856c46

+ 12 - 0
panda/src/cocoadisplay/cocoaGraphicsBuffer.I

@@ -0,0 +1,12 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file cocoaGraphicsBuffer.I
+ * @author rdb
+ * @date 2017-12-19
+ */

+ 61 - 0
panda/src/cocoadisplay/cocoaGraphicsBuffer.h

@@ -0,0 +1,61 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file cocoaGraphicsBuffer.h
+ * @author rdb
+ * @date 2017-12-19
+ */
+
+#ifndef COCOAGRAPHICSBUFFER_H
+#define COCOAGRAPHICSBUFFER_H
+
+#include "pandabase.h"
+#include "glgsg.h"
+
+/**
+ * This is a light wrapper around GLGraphicsBuffer (ie. FBOs) to interface
+ * with Cocoa contexts, so that it can be used without a host window.
+ */
+class CocoaGraphicsBuffer : public GLGraphicsBuffer {
+public:
+  CocoaGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
+                      const string &name,
+                      const FrameBufferProperties &fb_prop,
+                      const WindowProperties &win_prop,
+                      int flags,
+                      GraphicsStateGuardian *gsg,
+                      GraphicsOutput *host);
+
+  virtual bool begin_frame(FrameMode mode, Thread *current_thread);
+  virtual void end_frame(FrameMode mode, Thread *current_thread);
+
+protected:
+  virtual void close_buffer();
+  virtual bool open_buffer();
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GLGraphicsBuffer::init_type();
+    register_type(_type_handle, "CocoaGraphicsBuffer",
+                  GLGraphicsBuffer::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "cocoaGraphicsBuffer.I"
+
+#endif

+ 165 - 0
panda/src/cocoadisplay/cocoaGraphicsBuffer.mm

@@ -0,0 +1,165 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file cocoaGraphicsBuffer.mm
+ * @author rdb
+ * @date 2017-12-19
+ */
+
+#include "cocoaGraphicsBuffer.h"
+#include "cocoaGraphicsStateGuardian.h"
+#include "config_cocoadisplay.h"
+#include "cocoaGraphicsPipe.h"
+
+#import <OpenGL/OpenGL.h>
+
+TypeHandle CocoaGraphicsBuffer::_type_handle;
+
+/**
+ *
+ */
+CocoaGraphicsBuffer::
+CocoaGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
+                    const string &name,
+                    const FrameBufferProperties &fb_prop,
+                    const WindowProperties &win_prop,
+                    int flags,
+                    GraphicsStateGuardian *gsg,
+                    GraphicsOutput *host) : // Ignore the host.
+  GLGraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, nullptr)
+{
+}
+
+/**
+ * This function will be called within the draw thread before beginning
+ * rendering for a given frame.  It should do whatever setup is required, and
+ * return true if the frame should be rendered, or false if it should be
+ * skipped.
+ */
+bool CocoaGraphicsBuffer::
+begin_frame(FrameMode mode, Thread *current_thread) {
+  if (_gsg == nullptr) {
+    return false;
+  }
+
+  CocoaGraphicsStateGuardian *cocoagsg;
+  DCAST_INTO_R(cocoagsg, _gsg, false);
+  nassertr(cocoagsg->_context != nil, false);
+
+  // Lock the context and make it current.
+  {
+    PStatTimer timer(_make_current_pcollector, current_thread);
+    cocoagsg->lock_context();
+    [cocoagsg->_context makeCurrentContext];
+  }
+
+  return GLGraphicsBuffer::begin_frame(mode, current_thread);
+}
+
+/**
+ * This function will be called within the draw thread after rendering is
+ * completed for a given frame.  It should do whatever finalization is
+ * required.
+ */
+void CocoaGraphicsBuffer::
+end_frame(FrameMode mode, Thread *current_thread) {
+  nassertv(_gsg != nullptr);
+
+  GLGraphicsBuffer::end_frame(mode, current_thread);
+
+  // Release the context.
+  CocoaGraphicsStateGuardian *cocoagsg;
+  DCAST_INTO_V(cocoagsg, _gsg);
+  cocoagsg->unlock_context();
+}
+
+/**
+ * Opens the buffer right now.  Called from the window thread.  Returns true
+ * if the buffer is successfully opened, or false if there was a problem.
+ */
+bool CocoaGraphicsBuffer::
+open_buffer() {
+  CocoaGraphicsPipe *cocoa_pipe;
+  DCAST_INTO_R(cocoa_pipe, _pipe, false);
+
+  // GSG CreationInitialization
+  CocoaGraphicsStateGuardian *cocoagsg;
+  if (_gsg == nullptr) {
+    // There is no old gsg.  Create a new one.
+    cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, nullptr);
+    cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->get_display_id(), false);
+    _gsg = cocoagsg;
+  } else {
+    // If the old gsg has the wrong pixel format, create a new one that shares
+    // with the old gsg.
+    DCAST_INTO_R(cocoagsg, _gsg, false);
+    if (!cocoagsg->get_fb_properties().subsumes(_fb_properties)) {
+      cocoagsg = new CocoaGraphicsStateGuardian(_engine, _pipe, cocoagsg);
+      cocoagsg->choose_pixel_format(_fb_properties, cocoa_pipe->get_display_id(), false);
+      _gsg = cocoagsg;
+    }
+  }
+
+  FrameBufferProperties desired_props(_fb_properties);
+
+  // Lock the context, so we can safely operate on it.
+  cocoagsg->lock_context();
+
+  // Make the context current and initialize what we need.
+  [cocoagsg->_context makeCurrentContext];
+  [cocoagsg->_context update];
+  cocoagsg->reset_if_new();
+
+  // These properties are determined by choose_pixel_format.
+  _fb_properties.set_force_hardware(cocoagsg->_fbprops.get_force_hardware());
+  _fb_properties.set_force_software(cocoagsg->_fbprops.get_force_software());
+
+  bool success = GLGraphicsBuffer::open_buffer();
+  if (success) {
+    rebuild_bitplanes();
+    if (_needs_rebuild) {
+      // If it still needs rebuild, then something must have gone wrong.
+      success = false;
+    }
+  }
+
+  if (success && !_fb_properties.verify_hardware_software
+      (desired_props, cocoagsg->get_gl_renderer())) {
+    GLGraphicsBuffer::close_buffer();
+    success = false;
+  }
+
+  // Release the context.
+  cocoagsg->unlock_context();
+
+  if (!success) {
+    return false;
+  }
+
+  return true;
+}
+
+/**
+ * Closes the buffer right now.  Called from the window thread.
+ */
+void CocoaGraphicsBuffer::
+close_buffer() {
+  if (_gsg != nullptr) {
+    CocoaGraphicsStateGuardian *cocoagsg;
+    cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg);
+
+    if (cocoagsg != nullptr && cocoagsg->_context != nil) {
+      cocoagsg->lock_context();
+      GLGraphicsBuffer::close_buffer();
+      cocoagsg->unlock_context();
+    }
+    _gsg.clear();
+  } else {
+    GLGraphicsBuffer::close_buffer();
+  }
+}

+ 0 - 8
panda/src/cocoadisplay/cocoaGraphicsPipe.I

@@ -18,11 +18,3 @@ INLINE CGDirectDisplayID CocoaGraphicsPipe::
 get_display_id() const {
   return _display;
 }
-
-/**
- * Returns the Cocoa NSScreen pointer associated with this graphics pipe.
- */
-INLINE NSScreen *CocoaGraphicsPipe::
-get_nsscreen() const {
-  return _screen;
-}

+ 2 - 8
panda/src/cocoadisplay/cocoaGraphicsPipe.h

@@ -35,13 +35,10 @@ class FrameBufferProperties;
  */
 class CocoaGraphicsPipe : public GraphicsPipe {
 public:
-  CocoaGraphicsPipe();
-  CocoaGraphicsPipe(CGDirectDisplayID display);
-  CocoaGraphicsPipe(NSScreen *screen);
+  CocoaGraphicsPipe(CGDirectDisplayID display = CGMainDisplayID());
   virtual ~CocoaGraphicsPipe();
 
   INLINE CGDirectDisplayID get_display_id() const;
-  INLINE NSScreen *get_nsscreen() const;
 
   virtual string get_interface_name() const;
   static PT(GraphicsPipe) pipe_constructor();
@@ -64,11 +61,8 @@ protected:
 private:
   void load_display_information();
 
-  // _display and _screen refer to the same thing, NSScreen being the tiny
-  // Cocoa wrapper around the Quartz display ID.  NSScreen isn't generally
-  // useful, but we need it when creating the window.
+  // This is the Quartz display identifier.
   CGDirectDisplayID _display;
-  NSScreen *_screen;
 
   friend class CocoaGraphicsWindow;
 

+ 23 - 112
panda/src/cocoadisplay/cocoaGraphicsPipe.mm

@@ -12,7 +12,7 @@
  */
 
 #include "cocoaGraphicsPipe.h"
-// #include "cocoaGraphicsBuffer.h"
+#include "cocoaGraphicsBuffer.h"
 #include "cocoaGraphicsWindow.h"
 #include "cocoaGraphicsStateGuardian.h"
 #include "cocoaPandaApp.h"
@@ -30,104 +30,32 @@
 
 TypeHandle CocoaGraphicsPipe::_type_handle;
 
-static void init_app() {
-  if (NSApp == nil) {
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    [CocoaPandaApp sharedApplication];
-
-#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
-    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
-#endif
-    [NSApp finishLaunching];
-    [NSApp activateIgnoringOtherApps:YES];
-
-    // Put Cocoa into thread-safe mode by spawning a thread which immediately
-    // exits.
-    NSThread* thread = [[NSThread alloc] init];
-    [thread start];
-    [thread autorelease];
-  }
-}
-
 /**
- * Uses the main screen (the one the user is most likely to be working in at
- * the moment).
+ * Takes a CoreGraphics display ID, which defaults to the main display.
  */
 CocoaGraphicsPipe::
-CocoaGraphicsPipe() {
+CocoaGraphicsPipe(CGDirectDisplayID display) : _display(display) {
   _supported_types = OT_window | OT_buffer | OT_texture_buffer;
   _is_valid = true;
 
-  init_app();
-
-  _screen = [NSScreen mainScreen];
-  NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"];
-  _display = (CGDirectDisplayID) [num longValue];
-
-  _display_width = CGDisplayPixelsWide(_display);
-  _display_height = CGDisplayPixelsHigh(_display);
-  load_display_information();
-
-  cocoadisplay_cat.debug()
-    << "Creating CocoaGraphicsPipe for main screen "
-    << _screen << " with display ID " << _display << "\n";
-}
-
-/**
- * Takes a CoreGraphics display ID.
- */
-CocoaGraphicsPipe::
-CocoaGraphicsPipe(CGDirectDisplayID display) {
-  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
-  _is_valid = true;
-  _display = display;
-
-  init_app();
-
-  // Iterate over the screens to find the one with our display ID.
-  NSEnumerator *e = [[NSScreen screens] objectEnumerator];
-  while (NSScreen *screen = (NSScreen *) [e nextObject]) {
-    NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"];
-    if (display == (CGDirectDisplayID) [num longValue]) {
-      _screen = screen;
-      break;
-    }
-  }
-
-  _display_width = CGDisplayPixelsWide(_display);
-  _display_height = CGDisplayPixelsHigh(_display);
-  load_display_information();
+  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
-  cocoadisplay_cat.debug()
-    << "Creating CocoaGraphicsPipe for screen "
-    << _screen << " with display ID " << _display << "\n";
-}
+  // Put Cocoa into thread-safe mode by spawning a thread which immediately
+  // exits.
+  NSThread* thread = [[NSThread alloc] init];
+  [thread start];
+  [thread autorelease];
 
-/**
- * Takes an NSScreen pointer.
- */
-CocoaGraphicsPipe::
-CocoaGraphicsPipe(NSScreen *screen) {
-  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
-  _is_valid = true;
-
-  init_app();
-
-  if (screen == nil) {
-    _screen = [NSScreen mainScreen];
-  } else {
-    _screen = screen;
-  }
-  NSNumber *num = [[_screen deviceDescription] objectForKey: @"NSScreenNumber"];
-  _display = (CGDirectDisplayID) [num longValue];
+  // We used to also obtain the corresponding NSScreen here, but this causes
+  // the application icon to start bouncing, which may be undesirable for
+  // apps that will never open a window.
 
   _display_width = CGDisplayPixelsWide(_display);
   _display_height = CGDisplayPixelsHigh(_display);
   load_display_information();
 
   cocoadisplay_cat.debug()
-    << "Creating CocoaGraphicsPipe for screen "
-    << _screen << " with display ID " << _display << "\n";
+    << "Creating CocoaGraphicsPipe for display ID " << _display << "\n";
 }
 
 /**
@@ -308,10 +236,12 @@ make_output(const string &name,
                                    flags, gsg, host);
   }
 
-  // Second thing to try: a GLGraphicsBuffer
+  // Second thing to try: a GLGraphicsBuffer.  This requires a context, so if
+  // we don't have a host window, we instead create a CocoaGraphicsBuffer,
+  // which wraps around GLGraphicsBuffer and manages a context.
 
   if (retry == 1) {
-    if (!gl_support_fbo || host == NULL ||
+    if (!gl_support_fbo ||
         (flags & (BF_require_parasite | BF_require_window)) != 0) {
       return NULL;
     }
@@ -334,33 +264,14 @@ make_output(const string &name,
         precertify = true;
       }
     }
-    return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
-                                flags, gsg, host);
-  }
-/*
-  // Third thing to try: a CocoaGraphicsBuffer
-  if (retry == 2) {
-    if (((flags&BF_require_parasite)!=0)||
-        ((flags&BF_require_window)!=0)||
-        ((flags&BF_resizeable)!=0)||
-        ((flags&BF_size_track_host)!=0)||
-        ((flags&BF_can_bind_layered)!=0)) {
-      return NULL;
-    }
-
-    if (!support_rtt) {
-      if (((flags&BF_rtt_cumulative)!=0)||
-          ((flags&BF_can_bind_every)!=0)) {
-        // If we require Render-to-Texture, but can't be sure we support it,
-        // bail.
-        return NULL;
-      }
+    if (host != NULL) {
+      return new GLGraphicsBuffer(engine, this, name, fb_prop, win_prop,
+                                  flags, gsg, host);
+    } else {
+      return new CocoaGraphicsBuffer(engine, this, name, fb_prop, win_prop,
+                                     flags, gsg, host);
     }
-
-    return new CocoaGraphicsBuffer(engine, this, name, fb_prop, win_prop,
-                                 flags, gsg, host);
   }
-*/
 
   // Nothing else left to try.
   return NULL;

+ 18 - 0
panda/src/cocoadisplay/cocoaGraphicsStateGuardian.I

@@ -19,3 +19,21 @@ INLINE const FrameBufferProperties &CocoaGraphicsStateGuardian::
 get_fb_properties() const {
   return _fbprops;
 }
+
+/**
+ * Locks the context.
+ */
+INLINE void CocoaGraphicsStateGuardian::
+lock_context() {
+  nassertv(_context != nil);
+  CGLLockContext((CGLContextObj) [_context CGLContextObj]);
+}
+
+/**
+ * Unlocks the context.
+ */
+INLINE void CocoaGraphicsStateGuardian::
+unlock_context() {
+  nassertv(_context != nil);
+  CGLUnlockContext((CGLContextObj) [_context CGLContextObj]);
+}

+ 4 - 0
panda/src/cocoadisplay/cocoaGraphicsStateGuardian.h

@@ -19,6 +19,7 @@
 #include "glgsg.h"
 
 #import <AppKit/NSOpenGL.h>
+#import <OpenGL/OpenGL.h>
 
 /**
  * A tiny specialization on GLGraphicsStateGuardian to add some Cocoa-specific
@@ -38,6 +39,9 @@ public:
 
   virtual ~CocoaGraphicsStateGuardian();
 
+  INLINE void lock_context();
+  INLINE void unlock_context();
+
   NSOpenGLContext *_share_context;
   NSOpenGLContext *_context;
   FrameBufferProperties _fbprops;

+ 32 - 12
panda/src/cocoadisplay/cocoaGraphicsWindow.mm

@@ -65,6 +65,16 @@ CocoaGraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
   _fullscreen_mode = NULL;
   _windowed_mode = NULL;
 
+  // Now that we know for sure we want a window, we can create the Cocoa app.
+  // This will cause the application icon to appear and start bouncing.
+  if (NSApp == nil) {
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+#endif
+    [NSApp finishLaunching];
+    [NSApp activateIgnoringOtherApps:YES];
+  }
+
   GraphicsWindowInputDevice device =
     GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
   add_input_device(device);
@@ -144,7 +154,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   nassertr(_view != nil, false);
 
   // Place a lock on the context.
-  CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+  cocoagsg->lock_context();
 
   // Set the drawable.
   if (_properties.get_fullscreen()) {
@@ -210,7 +220,7 @@ end_frame(FrameMode mode, Thread *current_thread) {
   CocoaGraphicsStateGuardian *cocoagsg;
   DCAST_INTO_V(cocoagsg, _gsg);
 
-  CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+  cocoagsg->unlock_context();
 
   if (mode == FM_render) {
     // end_render_texture();
@@ -239,7 +249,7 @@ end_flip() {
     CocoaGraphicsStateGuardian *cocoagsg;
     DCAST_INTO_V(cocoagsg, _gsg);
 
-    CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+    cocoagsg->lock_context();
 
     // Swap the front and back buffer.
     [cocoagsg->_context flushBuffer];
@@ -247,7 +257,7 @@ end_flip() {
     // Flush the window
     [[_view window] flushWindow];
 
-    CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+    cocoagsg->unlock_context();
   }
   GraphicsWindow::end_flip();
 }
@@ -399,6 +409,16 @@ open_window() {
     }
   }
 
+  // Iterate over the screens to find the one with our display ID.
+  NSScreen *screen;
+  NSEnumerator *e = [[NSScreen screens] objectEnumerator];
+  while (screen = (NSScreen *) [e nextObject]) {
+    NSNumber *num = [[screen deviceDescription] objectForKey: @"NSScreenNumber"];
+    if (cocoa_pipe->_display == (CGDirectDisplayID) [num longValue]) {
+      break;
+    }
+  }
+
   // Center the window if coordinates were set to -1 or -2 TODO: perhaps in
   // future, in the case of -1, it should use the origin used in a previous
   // run of Panda
@@ -406,7 +426,7 @@ open_window() {
   if (parent_nsview != NULL) {
     container = [parent_nsview bounds];
   } else {
-    container = [cocoa_pipe->_screen frame];
+    container = [screen frame];
     container.origin = NSMakePoint(0, 0);
   }
   int x = _properties.get_x_origin();
@@ -453,7 +473,7 @@ open_window() {
     _window = [[CocoaPandaWindow alloc]
                initWithContentRect: rect
                styleMask:windowStyle
-               screen:cocoa_pipe->_screen
+               screen:screen
                window:this];
 
     if (_window == nil) {
@@ -464,7 +484,7 @@ open_window() {
   }
 
   // Lock the context, so we can safely operate on it.
-  CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+  cocoagsg->lock_context();
 
   // Create the NSView to render to.
   NSRect rect = NSMakeRect(0, 0, _properties.get_x_size(), _properties.get_y_size());
@@ -588,7 +608,7 @@ open_window() {
   cocoagsg->reset_if_new();
 
   // Release the context.
-  CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+  cocoagsg->unlock_context();
 
   if (!cocoagsg->is_valid()) {
     close_window();
@@ -637,9 +657,9 @@ close_window() {
     cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg);
 
     if (cocoagsg != NULL && cocoagsg->_context != nil) {
-      CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+      cocoagsg->lock_context();
       [cocoagsg->_context clearDrawable];
-      CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+      cocoagsg->unlock_context();
     }
     _gsg.clear();
   }
@@ -1429,9 +1449,9 @@ handle_close_event() {
     cocoagsg = DCAST(CocoaGraphicsStateGuardian, _gsg);
 
     if (cocoagsg != NULL && cocoagsg->_context != nil) {
-      CGLLockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+      cocoagsg->lock_context();
       [cocoagsg->_context clearDrawable];
-      CGLUnlockContext((CGLContextObj) [cocoagsg->_context CGLContextObj]);
+      cocoagsg->unlock_context();
     }
     _gsg.clear();
   }

+ 2 - 0
panda/src/cocoadisplay/config_cocoadisplay.mm

@@ -12,6 +12,7 @@
  */
 
 #include "config_cocoadisplay.h"
+#include "cocoaGraphicsBuffer.h"
 #include "cocoaGraphicsPipe.h"
 #include "cocoaGraphicsStateGuardian.h"
 #include "cocoaGraphicsWindow.h"
@@ -40,6 +41,7 @@ init_libcocoadisplay() {
   }
   initialized = true;
 
+  CocoaGraphicsBuffer::init_type();
   CocoaGraphicsPipe::init_type();
   CocoaGraphicsStateGuardian::init_type();
   CocoaGraphicsWindow::init_type();

+ 1 - 0
panda/src/cocoadisplay/p3cocoadisplay_composite1.mm

@@ -1,4 +1,5 @@
 #include "config_cocoadisplay.mm"
+#include "cocoaGraphicsBuffer.mm"
 #include "cocoaGraphicsPipe.mm"
 #include "cocoaGraphicsStateGuardian.mm"
 #include "cocoaGraphicsWindow.mm"

+ 31 - 14
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -213,12 +213,20 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     return false;
   }
 
-  if (!_host->begin_frame(FM_parasite, current_thread)) {
-    if (GLCAT.is_debug()) {
-      GLCAT.debug()
-        << get_name() << "'s host is not ready\n";
+  if (_host != nullptr) {
+    if (!_host->begin_frame(FM_parasite, current_thread)) {
+      if (GLCAT.is_debug()) {
+        GLCAT.debug()
+          << get_name() << "'s host is not ready\n";
+      }
+      return false;
+    }
+  } else {
+    // We don't have a host window, which is possible for CocoaGraphicsBuffer.
+    _gsg->set_current_properties(&get_fb_properties());
+    if (!_gsg->begin_frame(current_thread)) {
+      return false;
     }
-    return false;
   }
 
   // Figure out the desired size of the  buffer.
@@ -235,7 +243,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
       }
     }
     if (_creation_flags & GraphicsPipe::BF_size_track_host) {
-      if (_host->get_size() != _size) {
+      if (_host != nullptr && _host->get_size() != _size) {
         // We also need to rebuild if we need to change size.
         _needs_rebuild = true;
       }
@@ -356,7 +364,7 @@ rebuild_bitplanes() {
 
   // Calculate bitplane size.  This can be larger than the buffer.
   if (_creation_flags & GraphicsPipe::BF_size_track_host) {
-    if (_host->get_size() != _size) {
+    if (_host != nullptr && _host->get_size() != _size) {
       set_size_and_recalc(_host->get_x_size(),
                           _host->get_y_size());
     }
@@ -1253,7 +1261,11 @@ end_frame(FrameMode mode, Thread *current_thread) {
     generate_mipmaps();
   }
 
-  _host->end_frame(FM_parasite, current_thread);
+  if (_host != nullptr) {
+    _host->end_frame(FM_parasite, current_thread);
+  } else {
+    glgsg->end_frame(current_thread);
+  }
 
   if (mode == FM_render) {
     trigger_flip();
@@ -1315,8 +1327,11 @@ bool CLP(GraphicsBuffer)::
 open_buffer() {
   report_my_gl_errors();
 
-  // Double check that we have a host
-  nassertr(_host != 0, false);
+  // Double check that we have a valid gsg
+  nassertr(_gsg != nullptr, false);
+  if (!_gsg->is_valid()) {
+    return false;
+  }
 
   // Count total color buffers.
   int totalcolor =
@@ -1491,8 +1506,10 @@ open_buffer() {
   _fb_properties.set_back_buffers(0);
   _fb_properties.set_indexed_color(0);
   _fb_properties.set_rgb_color(1);
-  _fb_properties.set_force_hardware(_host->get_fb_properties().get_force_hardware());
-  _fb_properties.set_force_software(_host->get_fb_properties().get_force_software());
+  if (_host != nullptr) {
+    _fb_properties.set_force_hardware(_host->get_fb_properties().get_force_hardware());
+    _fb_properties.set_force_software(_host->get_fb_properties().get_force_software());
+  }
 
   _is_valid = true;
   _needs_rebuild = true;
@@ -1508,7 +1525,7 @@ open_buffer() {
  */
 GraphicsOutput *CLP(GraphicsBuffer)::
 get_host() {
-  return _host;
+  return (_host != nullptr) ? _host : this;
 }
 
 /**
@@ -1699,7 +1716,7 @@ report_my_errors(int line, const char *file) {
  */
 void CLP(GraphicsBuffer)::
 check_host_valid() {
-  if ((_host == 0)||(!_host->is_valid())) {
+  if (_host != nullptr && !_host->is_valid()) {
     _rb_data_size_bytes = 0;
     if (_rb_context != NULL) {
       // We must delete this object first, because when the GSG destructs, so

+ 0 - 2
panda/src/glstuff/glGraphicsBuffer_src.h

@@ -86,8 +86,6 @@ protected:
 
   void report_my_errors(int line, const char *file);
 
-private:
-
   void bind_slot(int layer, bool rb_resize, Texture **attach,
                  RenderTexturePlane plane, GLenum attachpoint);
   void bind_slot_multisample(bool rb_resize, Texture **attach,