Explorar o código

properly support single-buffered framebuffers

David Rose %!s(int64=22) %!d(string=hai) anos
pai
achega
cb66e630d6

+ 3 - 3
panda/src/display/Sources.pp

@@ -12,7 +12,7 @@
  
   #define SOURCES  \
     config_display.h \
-    clearableRegion.I clearableRegion.h \
+    drawableRegion.I drawableRegion.h \
     displayRegion.I displayRegion.h  \
     displayRegionStack.I \
     displayRegionStack.h \
@@ -42,7 +42,7 @@
     
  #define INCLUDED_SOURCES  \
     config_display.cxx \
-    clearableRegion.cxx \
+    drawableRegion.cxx \
     displayRegion.cxx \
     frameBufferProperties.cxx \
     geomContext.cxx geomNodeContext.cxx graphicsChannel.cxx  \
@@ -62,7 +62,7 @@
 
   #define INSTALL_HEADERS \
     config_display.h \
-    clearableRegion.I clearableRegion.h \
+    drawableRegion.I drawableRegion.h \
     displayRegion.I displayRegion.h displayRegionStack.I \
     displayRegionStack.h \
     frameBufferProperties.I frameBufferProperties.h \

+ 142 - 5
panda/src/display/displayRegion.cxx

@@ -16,12 +16,12 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-
+#include "displayRegion.h"
 #include "graphicsLayer.h"
 #include "graphicsChannel.h"
 #include "graphicsOutput.h"
 #include "config_display.h"
-#include "displayRegion.h"
+#include "pixelBuffer.h"
 #include "camera.h"
 #include "dcast.h"
 #include "mutexHolder.h"
@@ -36,6 +36,7 @@ DisplayRegion::
 DisplayRegion(GraphicsLayer *layer) :
   _l(0.), _r(1.), _b(0.), _t(1.),
   _layer(layer),
+  _window(layer->get_window()),
   _camera_node((Camera *)NULL),
   _active(true)
 {
@@ -52,6 +53,7 @@ DisplayRegion(GraphicsLayer *layer, const float l,
               const float r, const float b, const float t) :
   _l(l), _r(r), _b(b), _t(t),
   _layer(layer),
+  _window(layer->get_window()),
   _camera_node((Camera *)NULL),
   _active(true)
 {
@@ -66,10 +68,11 @@ DisplayRegion(GraphicsLayer *layer, const float l,
 //               typically for rendering a temporary pass.
 ////////////////////////////////////////////////////////////////////
 DisplayRegion::
-DisplayRegion(int xsize, int ysize) :
+DisplayRegion(GraphicsOutput *window, int xsize, int ysize) :
   _l(0.), _r(1.), _b(0.), _t(1.),
   _pl(0), _pr(xsize), _pb(0), _pt(ysize), _pbi(ysize), _pti(0),
   _layer((GraphicsLayer *)NULL),
+  _window(window),
   _camera_node((Camera *)NULL),
   _active(true)
 {
@@ -232,7 +235,7 @@ get_channel() const {
 GraphicsOutput *DisplayRegion::
 get_window() const {
   MutexHolder holder(_lock);
-  return (_layer != (GraphicsLayer *)NULL) ? _layer->get_window() : NULL;
+  return _window;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -245,7 +248,7 @@ get_window() const {
 GraphicsPipe *DisplayRegion::
 get_pipe() const {
   MutexHolder holder(_lock);
-  return (_layer != (GraphicsLayer *)NULL) ? _layer->get_pipe() : NULL;
+  return (_window != (GraphicsOutput *)NULL) ? _window->get_pipe() : NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -425,6 +428,140 @@ output(ostream &out) const {
       << ")";
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::save_screenshot_default
+//       Access: Published
+//  Description: Saves a screenshot of the region to a default
+//               filename, and returns the filename, or empty string
+//               if the screenshot failed.  The default filename is
+//               generated from the supplied prefix and from the
+//               Configrc variable screenshot-filename, which contains
+//               the following strings:
+//
+//                 %~p - the supplied prefix
+//                 %~f - the frame count
+//                 %~e - the value of screenshot-extension
+//                 All other % strings in strftime().
+////////////////////////////////////////////////////////////////////
+Filename DisplayRegion::
+save_screenshot_default(const string &prefix) {
+  time_t now = time(NULL);
+  struct tm *ttm = localtime(&now);
+  int frame_count = ClockObject::get_global_clock()->get_frame_count();
+
+  static const int buffer_size = 1024;
+  char buffer[buffer_size];
+
+  ostringstream filename_strm;
+
+  size_t i = 0;
+  while (i < screenshot_filename.length()) {
+    char ch1 = screenshot_filename[i++];
+    if (ch1 == '%' && i < screenshot_filename.length()) {
+      char ch2 = screenshot_filename[i++];
+      if (ch2 == '~' && i < screenshot_filename.length()) {
+        char ch3 = screenshot_filename[i++];
+        switch (ch3) {
+        case 'p':
+          filename_strm << prefix;
+          break;
+
+        case 'f':
+          filename_strm << frame_count;
+          break;
+
+        case 'e':
+          filename_strm << screenshot_extension;
+          break;
+        }
+
+      } else {
+        // Use strftime() to decode the percent code.
+        char format[3] = {'%', ch2, '\0'};
+        if (strftime(buffer, buffer_size, format, ttm)) {
+          for (char *b = buffer; *b != '\0'; b++) {
+            switch (*b) {
+            case ' ':
+            case ':':
+            case '/':
+              filename_strm << '-';
+              break;
+
+            case '\n':
+              break;
+
+            default:
+              filename_strm << *b;
+            }
+          }
+        }
+      }
+    } else {
+      filename_strm << ch1;
+    }
+  }
+
+  Filename filename = filename_strm.str();
+  if (save_screenshot(filename)) {
+    return filename;
+  }
+  return Filename();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::save_screenshot
+//       Access: Published
+//  Description: Saves a screenshot of the region to the indicated
+//               filename.  Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool DisplayRegion::
+save_screenshot(const Filename &filename) {
+  PNMImage image;
+  if (!get_screenshot(image)) {
+    return false;
+  }
+
+  if (!image.write(filename)) {
+    return false;
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DisplayRegion::get_screenshot
+//       Access: Published
+//  Description: Captures the most-recently rendered image from the
+//               framebuffer into the indicated PNMImage.  Returns
+//               true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool DisplayRegion::
+get_screenshot(PNMImage &image) {
+  GraphicsOutput *window = get_window();
+  nassertr(window != (GraphicsOutput *)NULL, false);
+  
+  GraphicsStateGuardian *gsg = window->get_gsg();
+  nassertr(gsg != (GraphicsStateGuardian *)NULL, false);
+
+  int x_size = get_pixel_width();
+  int y_size = get_pixel_height();
+  
+  window->make_current();
+
+  PixelBuffer p(x_size, y_size, 3, 1, PixelBuffer::T_unsigned_byte,
+                PixelBuffer::F_rgb);
+
+  RenderBuffer buffer = gsg->get_render_buffer(get_screenshot_buffer_type());
+  if (!p.copy(gsg, this, buffer)) {
+    return false;
+  }
+
+  if (!p.store(image)) {
+    return false;
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: DisplayRegion::win_display_regions_changed
 //       Access: Private

+ 14 - 4
panda/src/display/displayRegion.h

@@ -20,7 +20,7 @@
 
 #include "pandabase.h"
 
-#include "clearableRegion.h"
+#include "drawableRegion.h"
 #include "referenceCount.h"
 #include "nodePath.h"
 #include "cullResult.h"
@@ -35,18 +35,23 @@ class GraphicsOutput;
 class GraphicsPipe;
 class CullHandler;
 class Camera;
+class PNMImage;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : DisplayRegion
-// Description :
+// Description : A rectangular subregion within a window for rendering
+//               into.  Typically, there is one DisplayRegion that
+//               covers the whole window, but you may also create
+//               smaller DisplayRegions for having different regions
+//               within the window that represent different scenes.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA DisplayRegion : public ReferenceCount, public ClearableRegion {
+class EXPCL_PANDA DisplayRegion : public ReferenceCount, public DrawableRegion {
 public:
   DisplayRegion(GraphicsLayer *layer);
   DisplayRegion(GraphicsLayer *layer,
                 const float l, const float r,
                 const float b, const float t);
-  DisplayRegion(int xsize, int ysize);
+  DisplayRegion(GraphicsOutput *window, int xsize, int ysize);
 private:
   DisplayRegion(const DisplayRegion &copy);
   void operator = (const DisplayRegion &copy);
@@ -84,6 +89,10 @@ PUBLISHED:
 
   void output(ostream &out) const;
 
+  Filename save_screenshot_default(const string &prefix = "screenshot");
+  bool save_screenshot(const Filename &filename);
+  bool get_screenshot(PNMImage &image);
+
 private:
   void win_display_regions_changed();
   INLINE void do_compute_pixels(int x_size, int y_size);
@@ -102,6 +111,7 @@ private:
   int _pti;
 
   GraphicsLayer *_layer;
+  GraphicsOutput *_window;
   NodePath _camera;
 
   // This needs to be a PT(Camera) so we prevent the Camera node from

+ 1 - 1
panda/src/display/display_composite1.cxx

@@ -1,4 +1,4 @@
-#include "clearableRegion.cxx"
+#include "drawableRegion.cxx"
 #include "displayRegion.cxx"
 #include "geomContext.cxx"
 #include "geomNodeContext.cxx"

+ 69 - 38
panda/src/display/clearableRegion.I → panda/src/display/drawableRegion.I

@@ -1,4 +1,4 @@
-// Filename: clearableRegion.I
+// Filename: drawableRegion.I
 // Created by:  drose (11Jul02)
 //
 ////////////////////////////////////////////////////////////////////
@@ -18,12 +18,14 @@
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::Constructor
+//     Function: DrawableRegion::Constructor
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-INLINE ClearableRegion::
-ClearableRegion() : 
+INLINE DrawableRegion::
+DrawableRegion() : 
+  _screenshot_buffer_type(RenderBuffer::T_front),
+  _draw_buffer_type(RenderBuffer::T_back),
   _flags(0),
   _clear_color(0.0f, 0.0f, 0.0f, 0.0f),
   _clear_depth(1.0f)
@@ -31,12 +33,14 @@ ClearableRegion() :
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::Copy Constructor
+//     Function: DrawableRegion::Copy Constructor
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-INLINE ClearableRegion::
-ClearableRegion(const ClearableRegion &copy) :
+INLINE DrawableRegion::
+DrawableRegion(const DrawableRegion &copy) :
+  _screenshot_buffer_type(copy._screenshot_buffer_type),
+  _draw_buffer_type(copy._draw_buffer_type),
   _flags(copy._flags),
   _clear_color(copy._clear_color),
   _clear_depth(copy._clear_depth)
@@ -44,32 +48,34 @@ ClearableRegion(const ClearableRegion &copy) :
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::Copy Assignment Operator
+//     Function: DrawableRegion::Copy Assignment Operator
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
-INLINE void ClearableRegion::
-operator = (const ClearableRegion &copy) {
+INLINE void DrawableRegion::
+operator = (const DrawableRegion &copy) {
+  _screenshot_buffer_type = copy._screenshot_buffer_type;
+  _draw_buffer_type = copy._draw_buffer_type;
   _flags = copy._flags;
   _clear_color = copy._clear_color;
   _clear_depth = copy._clear_depth;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::copy_clear_settings
+//     Function: DrawableRegion::copy_clear_settings
 //       Access: Public
-//  Description: A convenience function that does the same thing as
-//               the assignment operator; this is just syntactically a
-//               little nicer (and a little clearer) to call from a
-//               derived class.
-////////////////////////////////////////////////////////////////////
-INLINE void ClearableRegion::
-copy_clear_settings(const ClearableRegion &copy) {
-  operator = (copy);
+//  Description: Copies only the clear settings from the other drawable
+//               region.
+////////////////////////////////////////////////////////////////////
+INLINE void DrawableRegion::
+copy_clear_settings(const DrawableRegion &copy) {
+  _flags = (_flags & ~F_clear_all) | (copy._flags & F_clear_all);
+  _clear_color = copy._clear_color;
+  _clear_depth = copy._clear_depth;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::set_clear_color_active
+//     Function: DrawableRegion::set_clear_color_active
 //       Access: Published
 //  Description: Toggles the flag that indicates whether the color
 //               buffer should be cleared every frame.  If this is
@@ -77,7 +83,7 @@ copy_clear_settings(const ClearableRegion &copy) {
 //               indicated by set_clear_color(); otherwise, it will be
 //               left alone.
 ////////////////////////////////////////////////////////////////////
-INLINE void ClearableRegion::
+INLINE void DrawableRegion::
 set_clear_color_active(bool clear_color_active) {
   if (clear_color_active) {
     _flags |= F_clear_color_active;
@@ -87,19 +93,19 @@ set_clear_color_active(bool clear_color_active) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::get_clear_color_active
+//     Function: DrawableRegion::get_clear_color_active
 //       Access: Published
 //  Description: Returns the current setting of the flag that
 //               indicates whether the color buffer should be cleared
 //               every frame.  See set_clear_color_active().
 ////////////////////////////////////////////////////////////////////
-INLINE bool ClearableRegion::
+INLINE bool DrawableRegion::
 get_clear_color_active() const {
   return ((_flags & F_clear_color_active) != 0);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::set_clear_depth_active
+//     Function: DrawableRegion::set_clear_depth_active
 //       Access: Published
 //  Description: Toggles the flag that indicates whether the depth
 //               buffer should be cleared every frame.  If this is
@@ -107,7 +113,7 @@ get_clear_color_active() const {
 //               value indicated by set_clear_depth(); otherwise, it
 //               will be left alone.
 ////////////////////////////////////////////////////////////////////
-INLINE void ClearableRegion::
+INLINE void DrawableRegion::
 set_clear_depth_active(bool clear_depth_active) {
   if (clear_depth_active) {
     _flags |= F_clear_depth_active;
@@ -117,19 +123,19 @@ set_clear_depth_active(bool clear_depth_active) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::get_clear_depth_active
+//     Function: DrawableRegion::get_clear_depth_active
 //       Access: Published
 //  Description: Returns the current setting of the flag that
 //               indicates whether the depth buffer should be cleared
 //               every frame.  See set_clear_depth_active().
 ////////////////////////////////////////////////////////////////////
-INLINE bool ClearableRegion::
+INLINE bool DrawableRegion::
 get_clear_depth_active() const {
   return ((_flags & F_clear_depth_active) != 0);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::set_clear_color
+//     Function: DrawableRegion::set_clear_color
 //       Access: Published
 //  Description: Sets the clear color to the indicated value.  This is
 //               the value that will be used to clear the color buffer
@@ -137,13 +143,13 @@ get_clear_depth_active() const {
 //               returns true.  If get_clear_color_active() returns
 //               false, this is meaningless.
 ////////////////////////////////////////////////////////////////////
-INLINE void ClearableRegion::
+INLINE void DrawableRegion::
 set_clear_color(const Colorf &color) {
   _clear_color = color;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::get_clear_color
+//     Function: DrawableRegion::get_clear_color
 //       Access: Published
 //  Description: Returns the current clear color value.  This is
 //               the value that will be used to clear the color buffer
@@ -151,13 +157,13 @@ set_clear_color(const Colorf &color) {
 //               returns true.  If get_clear_color_active() returns
 //               false, this is meaningless.
 ////////////////////////////////////////////////////////////////////
-INLINE const Colorf &ClearableRegion::
+INLINE const Colorf &DrawableRegion::
 get_clear_color() const {
   return _clear_color;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::set_clear_depth
+//     Function: DrawableRegion::set_clear_depth
 //       Access: Published
 //  Description: Sets the clear depth to the indicated value.  This is
 //               the value that will be used to clear the depth buffer
@@ -165,13 +171,13 @@ get_clear_color() const {
 //               returns true.  If get_clear_depth_active() returns
 //               false, this is meaningless.
 ////////////////////////////////////////////////////////////////////
-INLINE void ClearableRegion::
+INLINE void DrawableRegion::
 set_clear_depth(float depth) {
   _clear_depth = depth;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::get_clear_depth
+//     Function: DrawableRegion::get_clear_depth
 //       Access: Published
 //  Description: Returns the current clear depth value.  This is
 //               the value that will be used to clear the depth buffer
@@ -179,20 +185,45 @@ set_clear_depth(float depth) {
 //               returns true.  If get_clear_depth_active() returns
 //               false, this is meaningless.
 ////////////////////////////////////////////////////////////////////
-INLINE float ClearableRegion::
+INLINE float DrawableRegion::
 get_clear_depth() const {
   return _clear_depth;
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: ClearableRegion::is_any_clear_active
+//     Function: DrawableRegion::is_any_clear_active
 //       Access: Published
 //  Description: Returns true if any of the clear types (so far there
 //               are just color or depth) have been set active, or
 //               false if none of them are active and there is no need
 //               to clear.
 ////////////////////////////////////////////////////////////////////
-INLINE bool ClearableRegion::
+INLINE bool DrawableRegion::
 is_any_clear_active() const {
-  return (_flags != 0);
+  return (_flags & F_clear_all) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DrawableRegion::get_draw_buffer_type
+//       Access: Public
+//  Description: Returns the RenderBuffer into which the GSG should
+//               issue draw commands.  Normally, this is the back
+//               buffer for double-buffered windows, and the front
+//               buffer for single-buffered windows.
+////////////////////////////////////////////////////////////////////
+INLINE int DrawableRegion::
+get_draw_buffer_type() const {
+  return _draw_buffer_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DrawableRegion::get_screenshot_buffer_type
+//       Access: Public, Virtual
+//  Description: Returns the RenderBuffer that should be used for
+//               capturing screenshots from this particular
+//               DrawableRegion.
+////////////////////////////////////////////////////////////////////
+INLINE int DrawableRegion::
+get_screenshot_buffer_type() const {
+  return _screenshot_buffer_type;
 }

+ 2 - 2
panda/src/display/clearableRegion.cxx → panda/src/display/drawableRegion.cxx

@@ -1,4 +1,4 @@
-// Filename: clearableRegion.cxx
+// Filename: drawableRegion.cxx
 // Created by:  drose (11Jul02)
 //
 ////////////////////////////////////////////////////////////////////
@@ -16,4 +16,4 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#include "clearableRegion.h"
+#include "drawableRegion.h"

+ 27 - 14
panda/src/display/clearableRegion.h → panda/src/display/drawableRegion.h

@@ -1,4 +1,4 @@
-// Filename: clearableRegion.h
+// Filename: drawableRegion.h
 // Created by:  drose (11Jul02)
 //
 ////////////////////////////////////////////////////////////////////
@@ -16,26 +16,30 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#ifndef CLEARABLEREGION_H
-#define CLEARABLEREGION_H
+#ifndef DRAWABLEREGION_H
+#define DRAWABLEREGION_H
 
 #include "pandabase.h"
 #include "luse.h"
+#include "renderBuffer.h"
 
 ////////////////////////////////////////////////////////////////////
-//       Class : ClearableRegion
-// Description : This is just an interface definition for a
-//               rectangular region of the screen that might or might
-//               not need to be cleared every frame before rendering.
-//               This includes DisplayRegions and GraphicsWindows.
+//       Class : DrawableRegion
+// Description : This is a base class for GraphicsWindow (actually,
+//               GraphicsOutput) and DisplayRegion, both of which are
+//               conceptually rectangular regions into which drawing
+//               commands may be issued.  Sometimes you want to deal
+//               with a single display region, and sometimes you want
+//               to deal with the whole window at once, particularly
+//               for issuing clear commands and capturing screenshots.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA ClearableRegion {
+class EXPCL_PANDA DrawableRegion {
 public:
-  INLINE ClearableRegion();
-  INLINE ClearableRegion(const ClearableRegion &copy);
-  INLINE void operator = (const ClearableRegion &copy);
+  INLINE DrawableRegion();
+  INLINE DrawableRegion(const DrawableRegion &copy);
+  INLINE void operator = (const DrawableRegion &copy);
 
-  INLINE void copy_clear_settings(const ClearableRegion &copy);
+  INLINE void copy_clear_settings(const DrawableRegion &copy);
 
 PUBLISHED:
   INLINE void set_clear_color_active(bool clear_color_active);
@@ -52,11 +56,20 @@ PUBLISHED:
 
   INLINE bool is_any_clear_active() const;
 
+public:
+  INLINE int get_screenshot_buffer_type() const;
+  INLINE int get_draw_buffer_type() const;
+
+protected:
+  int _screenshot_buffer_type;
+  int _draw_buffer_type;
+
 private:
   // This data needs to be cycled.
   enum Flags {
     F_clear_color_active = 0x0001,
     F_clear_depth_active = 0x0002,
+    F_clear_all          = 0x0003, // = all of the above
   };
   int _flags;
 
@@ -64,6 +77,6 @@ private:
   float _clear_depth;
 };
 
-#include "clearableRegion.I"
+#include "drawableRegion.I"
 
 #endif

+ 14 - 0
panda/src/display/frameBufferProperties.I

@@ -107,6 +107,20 @@ clear_frame_buffer_mode() {
   _frame_buffer_mode = 0;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: FrameBufferProperties::is_single_buffered
+//       Access: Published
+//  Description: Returns true if the frame buffer indicates a
+//               single-buffered mode, false if it indicates double-
+//               or triple-buffering.  This is a convenience function
+//               to access this useful tidbit of data.
+////////////////////////////////////////////////////////////////////
+INLINE bool FrameBufferProperties::
+is_single_buffered() const {
+  nassertr(has_frame_buffer_mode(), false);
+  return (_frame_buffer_mode & FM_buffer) == FM_single_buffer;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: FrameBufferProperties::set_depth_bits
 //       Access: Published

+ 2 - 0
panda/src/display/frameBufferProperties.h

@@ -62,6 +62,8 @@ PUBLISHED:
   INLINE bool has_frame_buffer_mode() const;
   INLINE void clear_frame_buffer_mode();
 
+  INLINE bool is_single_buffered() const;
+
   INLINE void set_depth_bits(int depth_bits);
   INLINE int get_depth_bits() const;
   INLINE bool has_depth_bits() const;

+ 45 - 37
panda/src/display/graphicsEngine.cxx

@@ -215,8 +215,10 @@ make_window(GraphicsStateGuardian *gsg, const string &name, int sort) {
 
   // TODO: ask the window thread to make the window.
   PT(GraphicsWindow) window = gsg->get_pipe()->make_window(gsg, name);
-  window->_sort = sort;
-  do_add_window(window, gsg, threading_model);
+  if (window != (GraphicsWindow *)NULL) {
+    window->_sort = sort;
+    do_add_window(window, gsg, threading_model);
+  }
   return window;
 }
 
@@ -264,8 +266,10 @@ make_buffer(GraphicsStateGuardian *gsg, const string &name,
   // TODO: ask the window thread to make the buffer.
   PT(GraphicsBuffer) buffer = 
     gsg->get_pipe()->make_buffer(gsg, name, x_size, y_size, want_texture);
-  buffer->_sort = sort;
-  do_add_window(buffer, gsg, threading_model);
+  if (buffer != (GraphicsBuffer *)NULL) {
+    buffer->_sort = sort;
+    do_add_window(buffer, gsg, threading_model);
+  }
   return buffer;
 }
 
@@ -668,19 +672,25 @@ process_events(const GraphicsEngine::Windows &wlist) {
 //     Function: GraphicsEngine::flip_windows
 //       Access: Private
 //  Description: This is called by the RenderThread object to flip the
-//               buffers (resize, etc.) for the given list of windows.
-//               This is run in the draw thread.
+//               buffers for all of the non-single-buffered windows in
+//               the given list.  This is run in the draw thread.
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
 flip_windows(const GraphicsEngine::Windows &wlist) {
   Windows::const_iterator wi;
   for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
     GraphicsOutput *win = (*wi);
-    win->begin_flip();
+    if (win->is_active() && win->get_gsg()->is_active() && 
+        !win->get_gsg()->get_properties().is_single_buffered()) {
+      win->begin_flip();
+    }
   }
   for (wi = wlist.begin(); wi != wlist.end(); ++wi) {
     GraphicsOutput *win = (*wi);
-    win->end_flip();
+    if (win->is_active() && win->get_gsg()->is_active() && 
+        !win->get_gsg()->get_properties().is_single_buffered()) {
+      win->end_flip();
+    }
   }
 }
 
@@ -918,36 +928,34 @@ setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup) {
 void GraphicsEngine::
 do_add_window(GraphicsOutput *window, GraphicsStateGuardian *gsg,
               const GraphicsThreadingModel &threading_model) {
-  if (window != (GraphicsOutput *)NULL) {
-    MutexHolder holder(_lock);
-    _windows_sorted = false;
-    _windows.push_back(window);
-
-    WindowRenderer *cull = get_window_renderer(threading_model.get_cull_name());
-    WindowRenderer *draw = get_window_renderer(threading_model.get_draw_name());
-    draw->add_gsg(gsg);
-
-    if (threading_model.get_cull_sorting()) {
-      cull->add_window(cull->_cull, window);
-      draw->add_window(draw->_draw, window);
-    } else {
-      cull->add_window(cull->_cdraw, window);
-    }
-
-    // We should ask the pipe which thread it prefers to run its
-    // windowing commands in (the "window thread").  This is the
-    // thread that handles the commands to open, resize, etc. the
-    // window.  X requires this to be done in the app thread, but some
-    // pipes might prefer this to be done in draw, for instance.  For
-    // now, we assume this is the app thread.
-    _app.add_window(_app._window, window);
-
-    display_cat.info()
-      << "Created " << window->get_type() << "\n";
-
-    // By default, try to open each window as it is added.
-    window->request_open();
+  MutexHolder holder(_lock);
+  _windows_sorted = false;
+  _windows.push_back(window);
+  
+  WindowRenderer *cull = get_window_renderer(threading_model.get_cull_name());
+  WindowRenderer *draw = get_window_renderer(threading_model.get_draw_name());
+  draw->add_gsg(gsg);
+  
+  if (threading_model.get_cull_sorting()) {
+    cull->add_window(cull->_cull, window);
+    draw->add_window(draw->_draw, window);
+  } else {
+    cull->add_window(cull->_cdraw, window);
   }
+  
+  // We should ask the pipe which thread it prefers to run its
+  // windowing commands in (the "window thread").  This is the
+  // thread that handles the commands to open, resize, etc. the
+  // window.  X requires this to be done in the app thread, but some
+  // pipes might prefer this to be done in draw, for instance.  For
+  // now, we assume this is the app thread.
+  _app.add_window(_app._window, window);
+  
+  display_cat.info()
+    << "Created " << window->get_type() << "\n";
+  
+  // By default, try to open each window as it is added.
+  window->request_open();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 46 - 0
panda/src/display/graphicsOutput.I

@@ -173,6 +173,52 @@ get_sort() const {
   return _sort;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::save_screenshot_default
+//       Access: Published
+//  Description: Saves a screenshot of the region to a default
+//               filename, and returns the filename, or empty string
+//               if the screenshot failed.  The default filename is
+//               generated from the supplied prefix and from the
+//               Configrc variable screenshot-filename, which contains
+//               the following strings:
+//
+//                 %~p - the supplied prefix
+//                 %~f - the frame count
+//                 %~e - the value of screenshot-extension
+//                 All other % strings in strftime().
+////////////////////////////////////////////////////////////////////
+INLINE Filename GraphicsOutput::
+save_screenshot_default(const string &prefix) {
+  DisplayRegion dr(this, _x_size, _y_size);
+  return dr.save_screenshot_default(prefix);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::save_screenshot
+//       Access: Published
+//  Description: Saves a screenshot of the region to the indicated
+//               filename.  Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsOutput::
+save_screenshot(const Filename &filename) {
+  DisplayRegion dr(this, _x_size, _y_size);
+  return dr.save_screenshot(filename);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_screenshot
+//       Access: Published
+//  Description: Captures the most-recently rendered image from the
+//               framebuffer into the indicated PNMImage.  Returns
+//               true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsOutput::
+get_screenshot(PNMImage &image) {
+  DisplayRegion dr(this, _x_size, _y_size);
+  return dr.get_screenshot(image);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::win_display_regions_changed
 //       Access: Public

+ 13 - 151
panda/src/display/graphicsOutput.cxx

@@ -22,6 +22,7 @@
 #include "config_display.h"
 #include "mutexHolder.h"
 #include "hardwareChannel.h"
+#include "renderBuffer.h"
 
 TypeHandle GraphicsOutput::_type_handle;
 
@@ -52,6 +53,12 @@ GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
   _copy_texture = false;
   _sort = 0;
 
+  int mode = gsg->get_properties().get_frame_buffer_mode();
+  if ((mode & FrameBufferProperties::FM_buffer) == FrameBufferProperties::FM_single_buffer) {
+    // Single buffered; we must draw into the front buffer.
+    _draw_buffer_type = RenderBuffer::T_front;
+  }
+
   _display_regions_stale = false;
 
   // By default, each new GraphicsOutput is set up to clear color and
@@ -291,139 +298,6 @@ get_display_region(int n) const {
   return result;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::save_screenshot_default
-//       Access: Published
-//  Description: Saves a screenshot of the window to a default
-//               filename, and returns the filename, or empty string
-//               if the screenshot failed.  The default filename is
-//               generated from the supplied prefix and from the
-//               Configrc variable screenshot-filename, which contains
-//               the following strings:
-//
-//                 %~p - the supplied prefix
-//                 %~f - the frame count
-//                 %~e - the value of screenshot-extension
-//                 All other % strings in strftime().
-////////////////////////////////////////////////////////////////////
-Filename GraphicsOutput::
-save_screenshot_default(const string &prefix) {
-  time_t now = time(NULL);
-  struct tm *ttm = localtime(&now);
-  int frame_count = ClockObject::get_global_clock()->get_frame_count();
-
-  static const int buffer_size = 1024;
-  char buffer[buffer_size];
-
-  ostringstream filename_strm;
-
-  size_t i = 0;
-  while (i < screenshot_filename.length()) {
-    char ch1 = screenshot_filename[i++];
-    if (ch1 == '%' && i < screenshot_filename.length()) {
-      char ch2 = screenshot_filename[i++];
-      if (ch2 == '~' && i < screenshot_filename.length()) {
-        char ch3 = screenshot_filename[i++];
-        switch (ch3) {
-        case 'p':
-          filename_strm << prefix;
-          break;
-
-        case 'f':
-          filename_strm << frame_count;
-          break;
-
-        case 'e':
-          filename_strm << screenshot_extension;
-          break;
-        }
-
-      } else {
-        // Use strftime() to decode the percent code.
-        char format[3] = {'%', ch2, '\0'};
-        if (strftime(buffer, buffer_size, format, ttm)) {
-          for (char *b = buffer; *b != '\0'; b++) {
-            switch (*b) {
-            case ' ':
-            case ':':
-            case '/':
-              filename_strm << '-';
-              break;
-
-            case '\n':
-              break;
-
-            default:
-              filename_strm << *b;
-            }
-          }
-        }
-      }
-    } else {
-      filename_strm << ch1;
-    }
-  }
-
-  Filename filename = filename_strm.str();
-  if (save_screenshot(filename)) {
-    return filename;
-  }
-  return Filename();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::save_screenshot
-//       Access: Published
-//  Description: Saves a screenshot of the window to the indicated
-//               filename.  Returns true on success, false on failure.
-////////////////////////////////////////////////////////////////////
-bool GraphicsOutput::
-save_screenshot(const Filename &filename) {
-  PNMImage image;
-  if (!get_screenshot(image)) {
-    return false;
-  }
-
-  if (!image.write(filename)) {
-    return false;
-  }
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::get_screenshot
-//       Access: Published
-//  Description: Captures the most-recently rendered image from the
-//               framebuffer into the indicated PNMImage.  Returns
-//               true on success, false on failure.
-////////////////////////////////////////////////////////////////////
-bool GraphicsOutput::
-get_screenshot(PNMImage &image) {
-  if (_gsg == (GraphicsStateGuardian *)NULL) {
-    return false;
-  }
-
-  if (!is_valid()) {
-    return false;
-  }
-
-  make_current();
-
-  PixelBuffer p(_x_size, _y_size, 3, 1, PixelBuffer::T_unsigned_byte,
-                PixelBuffer::F_rgb);
-
-  DisplayRegion dr(_x_size, _y_size);
-  if (!p.copy(_gsg, &dr, get_screenshot_buffer())) {
-    return false;
-  }
-
-  if (!p.store(image)) {
-    return false;
-  }
-
-  return true;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::make_scratch_display_region
 //       Access: Public
@@ -435,7 +309,7 @@ get_screenshot(PNMImage &image) {
 //               the interface provided in GraphicsLayer.
 ////////////////////////////////////////////////////////////////////
 PT(DisplayRegion) GraphicsOutput::
-make_scratch_display_region(int x_size, int y_size) const {
+make_scratch_display_region(int x_size, int y_size) {
 #ifndef NDEBUG
   if (x_size > _x_size || y_size > _y_size) {
     display_cat.error()
@@ -447,7 +321,7 @@ make_scratch_display_region(int x_size, int y_size) const {
   }
 #endif
 
-  PT(DisplayRegion) region = new DisplayRegion(x_size, y_size);
+  PT(DisplayRegion) region = new DisplayRegion(this, x_size, y_size);
   region->copy_clear_settings(*this);
   return region;
 }
@@ -526,7 +400,7 @@ begin_frame() {
 //  Description: Clears the entire framebuffer before rendering,
 //               according to the settings of get_color_clear_active()
 //               and get_depth_clear_active() (inherited from
-//               ClearableRegion).
+//               DrawableRegion).
 //
 //               This function is called only within the draw thread.
 ////////////////////////////////////////////////////////////////////
@@ -561,8 +435,9 @@ end_frame() {
   // _copy_texture to false.
   if (_copy_texture) {
     nassertv(has_texture());
-    DisplayRegion dr(_x_size, _y_size);
-    get_texture()->copy(_gsg, &dr, _gsg->get_render_buffer(RenderBuffer::T_back));
+    DisplayRegion dr(this, _x_size, _y_size);
+    RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
+    get_texture()->copy(_gsg, &dr, buffer);
   }
 }
 
@@ -660,19 +535,6 @@ declare_channel(int index, GraphicsChannel *chan) {
   _channels[index] = chan;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::get_screenshot_buffer
-//       Access: Protected, Virtual
-//  Description: Returns the RenderBuffer that should be used for
-//               capturing screenshots from this particular
-//               GraphicsOutput.
-////////////////////////////////////////////////////////////////////
-RenderBuffer GraphicsOutput::
-get_screenshot_buffer() {
-  // By default, this is the front buffer.
-  return _gsg->get_render_buffer(RenderBuffer::T_front);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::do_determine_display_regions
 //       Access: Private

+ 6 - 7
panda/src/display/graphicsOutput.h

@@ -25,7 +25,7 @@
 #include "graphicsPipe.h"
 #include "displayRegion.h"
 #include "graphicsStateGuardian.h"
-#include "clearableRegion.h"
+#include "drawableRegion.h"
 #include "renderBuffer.h"
 
 #include "typedWritableReferenceCount.h"
@@ -57,7 +57,7 @@ class PNMImage;
 //               TypedWritableReferenceCount instead of
 //               TypedReferenceCount for that convenience.
 ////////////////////////////////////////////////////////////////////
-class EXPCL_PANDA GraphicsOutput : public TypedWritableReferenceCount, public ClearableRegion {
+class EXPCL_PANDA GraphicsOutput : public TypedWritableReferenceCount, public DrawableRegion {
 protected:
   GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
                  const string &name);
@@ -95,13 +95,13 @@ PUBLISHED:
   int get_num_display_regions() const;
   DisplayRegion *get_display_region(int n) const;
 
-  Filename save_screenshot_default(const string &prefix = "screenshot");
-  bool save_screenshot(const Filename &filename);
-  bool get_screenshot(PNMImage &image);
+  INLINE Filename save_screenshot_default(const string &prefix = "screenshot");
+  INLINE bool save_screenshot(const Filename &filename);
+  INLINE bool get_screenshot(PNMImage &image);
 
 public:
   // No need to publish these.
-  PT(DisplayRegion) make_scratch_display_region(int x_size, int y_size) const;
+  PT(DisplayRegion) make_scratch_display_region(int x_size, int y_size);
 
 public:
   // These are not intended to be called directly by the user.
@@ -138,7 +138,6 @@ public:
 
 protected:
   void declare_channel(int index, GraphicsChannel *chan);
-  virtual RenderBuffer get_screenshot_buffer();
   
 protected:
   PT(GraphicsStateGuardian) _gsg;

+ 16 - 2
panda/src/display/graphicsStateGuardian.I

@@ -138,7 +138,7 @@ get_scene() const {
 //  Description: Clears the framebuffer within the indicated
 //               DisplayRegion, according to the flags indicated by
 //               the DisplayRegion object (inheriting from
-//               ClearableRegion).  Note that by default, a
+//               DrawableRegion).  Note that by default, a
 //               DisplayRegion does not have any clear flags set, in
 //               which case this function will do nothing.
 ////////////////////////////////////////////////////////////////////
@@ -146,7 +146,7 @@ INLINE void GraphicsStateGuardian::
 clear(DisplayRegion *dr) {
   DisplayRegionStack old_dr = push_display_region(dr);
   prepare_display_region();
-  clear((ClearableRegion *)dr);
+  clear((DrawableRegion *)dr);
   pop_display_region(old_dr);
 }
 
@@ -437,3 +437,17 @@ get_clip_plane(int plane_id) const {
   nassertr(plane_id >= 0 && plane_id < (int)_clip_plane_info.size(), (PlaneNode *)NULL);
   return _clip_plane_info[plane_id]._plane;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::set_properties
+//       Access: Protected
+//  Description: Changes the frame buffer properties structure
+//               representing this GSG's capabilities.  Normally, this
+//               should only be called by a derived class wishing to
+//               indicate that the originally-requested capabilities
+//               could not be granted.
+////////////////////////////////////////////////////////////////////
+INLINE void GraphicsStateGuardian::
+set_properties(const FrameBufferProperties &properties) {
+  _properties = properties;
+}

+ 3 - 4
panda/src/display/graphicsStateGuardian.cxx

@@ -112,7 +112,6 @@ reset() {
   _depth_clear_value = 1.0f;
   _stencil_clear_value = 0.0f;
   _accum_clear_value.set(0.0f, 0.0f, 0.0f, 0.0f);
-  _clear_buffer_type = RenderBuffer::T_back | RenderBuffer::T_depth;
   _normals_enabled = false;
 
   //Color and alpha transform variables
@@ -343,15 +342,15 @@ set_depth_clear_value(const float value) {
 //       Access: Public
 //  Description: Clears the framebuffer within the current
 //               DisplayRegion, according to the flags indicated by
-//               the given ClearableRegion object.
+//               the given DrawableRegion object.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-clear(ClearableRegion *clearable) {
+clear(DrawableRegion *clearable) {
   PStatTimer timer(_clear_pcollector);
 
   int clear_buffer_type = 0;
   if (clearable->get_clear_color_active()) {
-    clear_buffer_type |= RenderBuffer::T_back;
+    clear_buffer_type |= clearable->get_draw_buffer_type();
     set_color_clear_value(clearable->get_clear_color());
   }
   if (clearable->get_clear_depth_active()) {

+ 4 - 3
panda/src/display/graphicsStateGuardian.h

@@ -47,7 +47,7 @@
 #include "notify.h"
 #include "pvector.h"
 
-class ClearableRegion;
+class DrawableRegion;
 class GraphicsEngine;
 
 ////////////////////////////////////////////////////////////////////
@@ -103,7 +103,7 @@ public:
   virtual void set_depth_clear_value(const float value);
   virtual void do_clear(const RenderBuffer &buffer)=0;
 
-  void clear(ClearableRegion *clearable);
+  void clear(DrawableRegion *clearable);
   INLINE void clear(DisplayRegion *dr);
 
   virtual void prepare_display_region()=0;
@@ -204,6 +204,8 @@ protected:
   virtual void close_gsg();
   void panic_deactivate();
 
+  INLINE void set_properties(const FrameBufferProperties &properties);
+
 #ifdef DO_PSTATS
   // These functions are used to update the active texture memory
   // usage record (and other frame-based measurements) in Pstats.
@@ -238,7 +240,6 @@ protected:
   float _depth_clear_value;
   bool _stencil_clear_value;
   Colorf _accum_clear_value;
-  int _clear_buffer_type;
 
   int _display_region_stack_level;
   int _frame_buffer_stack_level;

+ 0 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -443,7 +443,6 @@ reset() {
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 do_clear(const RenderBuffer &buffer) {
-  //  DO_PSTATS_STUFF(PStatTimer timer(_win->_clear_pcollector);)
   nassertv(buffer._gsg == this);
   int buffer_type = buffer._buffer_type;
   GLbitfield mask = 0;

+ 4 - 14
panda/src/glxdisplay/glxGraphicsBuffer.cxx

@@ -42,6 +42,10 @@ glxGraphicsBuffer(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
   DCAST_INTO_V(glx_pipe, _pipe);
   _display = glx_pipe->get_display();
   _pbuffer = None;
+
+  // Since the pbuffer never gets flipped, we get screenshots from the
+  // same buffer we draw into.
+  _screenshot_buffer_type = _draw_buffer_type;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -155,17 +159,3 @@ open_buffer() {
   _is_valid = true;
   return true;
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: glxGraphicsBuffer::get_screenshot_buffer
-//       Access: Protected, Virtual
-//  Description: Returns the RenderBuffer that should be used for
-//               capturing screenshots from this particular
-//               GraphicsOutput.
-////////////////////////////////////////////////////////////////////
-RenderBuffer glxGraphicsBuffer::
-get_screenshot_buffer() {
-  // Since the pbuffer never gets flipped, we get screenshots from the
-  // back buffer only.
-  return _gsg->get_render_buffer(RenderBuffer::T_back);
-}

+ 0 - 2
panda/src/glxdisplay/glxGraphicsBuffer.h

@@ -44,8 +44,6 @@ protected:
   virtual void close_buffer();
   virtual bool open_buffer();
 
-  virtual RenderBuffer get_screenshot_buffer();
-
 private:
   Display *_display;
   GLXPbuffer _pbuffer;

+ 18 - 1
panda/src/glxdisplay/glxGraphicsPipe.cxx

@@ -469,26 +469,43 @@ try_for_fbconfig(int framebuffer_mode,
     attrib_list[n++] = GLX_ALPHA_SIZE;
     attrib_list[n++] = want_color_component_bits;
   }
-  if (framebuffer_mode & FrameBufferProperties::FM_double_buffer) {
+
+  switch (framebuffer_mode & FrameBufferProperties::FM_buffer) {
+  case FrameBufferProperties::FM_single_buffer:
+    glxdisplay_cat.debug(false) << " SINGLEBUFFER";
+    attrib_list[n++] = GLX_DOUBLEBUFFER;
+    attrib_list[n++] = false;
+    break;
+
+  case FrameBufferProperties::FM_double_buffer:
+  case FrameBufferProperties::FM_triple_buffer:
     glxdisplay_cat.debug(false) << " DOUBLEBUFFER";
     attrib_list[n++] = GLX_DOUBLEBUFFER;
     attrib_list[n++] = true;
+    break;
   }
+
   if (framebuffer_mode & FrameBufferProperties::FM_stereo) {
     glxdisplay_cat.debug(false) << " STEREO";
     attrib_list[n++] = GLX_STEREO;
     attrib_list[n++] = true;
+  } else {
+    attrib_list[n++] = GLX_STEREO;
+    attrib_list[n++] = false;
   }
+
   if (framebuffer_mode & FrameBufferProperties::FM_depth) {
     glxdisplay_cat.debug(false) << " DEPTH(" << want_depth_bits << ")";
     attrib_list[n++] = GLX_DEPTH_SIZE;
     attrib_list[n++] = want_depth_bits;
   }
+
   if (framebuffer_mode & FrameBufferProperties::FM_stencil) {
     glxdisplay_cat.debug(false) << " STENCIL";
     attrib_list[n++] = GLX_STENCIL_SIZE;
     attrib_list[n++] = 1;
   }
+
   if (framebuffer_mode & FrameBufferProperties::FM_accum) {
     glxdisplay_cat.debug(false) << " ACCUM";
     attrib_list[n++] = GLX_ACCUM_RED_SIZE;

+ 0 - 26
panda/src/mesadisplay/osMesaGraphicsBuffer.cxx

@@ -63,32 +63,6 @@ make_current() {
   mesagsg->reset_if_new();
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: OsMesaGraphicsBuffer::begin_flip
-//       Access: Public, Virtual
-//  Description: This function will be called within the draw thread
-//               after end_frame() has been called on all windows, to
-//               initiate the exchange of the front and back buffers.
-//
-//               This should instruct the window to prepare for the
-//               flip at the next video sync, but it should not wait.
-//
-//               We have the two separate functions, begin_flip() and
-//               end_flip(), to make it easier to flip all of the
-//               windows at the same time.
-////////////////////////////////////////////////////////////////////
-void OsMesaGraphicsBuffer::
-begin_flip() {
-  if (has_texture()) {
-    // Use glCopyTexImage2D to copy the framebuffer to the texture.
-    // This appears to be the only way to "render to a texture" in
-    // OpenGL; there's no interface to make the offscreen buffer
-    // itself be a texture.
-    DisplayRegion dr(_x_size, _y_size);
-    get_texture()->copy(_gsg, &dr, _gsg->get_render_buffer(RenderBuffer::T_back));
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: OsMesaGraphicsBuffer::close_buffer
 //       Access: Protected, Virtual

+ 0 - 2
panda/src/mesadisplay/osMesaGraphicsBuffer.h

@@ -40,8 +40,6 @@ public:
 
   virtual void make_current();
 
-  virtual void begin_flip();
-
 protected:
   virtual void close_buffer();
   virtual bool open_buffer();

+ 9 - 0
panda/src/mesadisplay/osMesaGraphicsStateGuardian.cxx

@@ -30,6 +30,15 @@ OSMesaGraphicsStateGuardian(const FrameBufferProperties &properties) :
   MesaGraphicsStateGuardian(properties)
 {
   _context = OSMesaCreateContext(OSMESA_RGBA, NULL);
+
+  // I think OSMesa always creates single-buffered contexts.  I don't
+  // see any documentation to this effect, but it seems to be the
+  // case.
+  FrameBufferProperties props = get_properties();
+  int mode = props.get_frame_buffer_mode();
+  mode = (mode & ~FrameBufferProperties::FM_buffer) | FrameBufferProperties::FM_single_buffer;
+  props.set_frame_buffer_mode(mode);
+  set_properties(props);
 }
 
 ////////////////////////////////////////////////////////////////////