Răsfoiți Sursa

*** empty log message ***

Josh Yelon 20 ani în urmă
părinte
comite
efc2a9ce26

+ 45 - 16
panda/src/display/graphicsOutput.I

@@ -60,30 +60,36 @@ get_name() const {
   return _name;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::count_textures
+//       Access: Published
+//  Description: If the GraphicsOutput is set to render into a
+//               texture, returns the number of textures that are
+//               being rendered into.  Normally, the textures would
+//               be associated with different buffers - a color
+//               texture, a depth texture, and a stencil texture.
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsOutput::
+count_textures() const {
+  return _textures.size();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::has_texture
 //       Access: Published
-//  Description: Returns true if the GraphicsOutput is set to render
-//               into a texture, or false otherwise.
-//
-//               Normally, this will only be true for a GraphicsBuffer
-//               object, and only when want_texture is passed in as
-//               true to the GraphicsBuffer constructor.  If
-//               show-buffers is true, this may also be set for a
-//               GraphicsWindow, which is in this case serving in the
-//               place of a GraphicsBuffer.
+//  Description: Returns true if the GraphicsOutput is rendering 
+//               into any textures at all.
 ////////////////////////////////////////////////////////////////////
 INLINE bool GraphicsOutput::
 has_texture() const {
-  return !(_texture.is_null());
+  return (_textures.size() > 0);
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::get_texture
 //       Access: Published
-//  Description: Returns the texture into which the GraphicsOutput
-//               renders, if has_texture() is true, or NULL if
-//               has_texture() is false.
+//  Description: Returns the nth texture into which the GraphicsOutput
+//               renders.  Returns NULL if there is no such texture.
 //
 //               If the texture is non-NULL, it may be applied to
 //               geometry to be rendered for any other windows or
@@ -93,8 +99,26 @@ has_texture() const {
 //               the texture will be invalid.
 ////////////////////////////////////////////////////////////////////
 INLINE Texture *GraphicsOutput::
-get_texture() const {
-  return _texture;
+get_texture(int i) const {
+  if ((i < 0) || (i >= ((int)_textures.size()))) {
+    return (Texture *)NULL;
+  }
+  return _textures[i]._texture;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::get_rtm_mode
+//       Access: Published
+//  Description: Returns the RenderTextureMode associated with the
+//               nth texture.  Returns RTM_none if there is no such
+//               texture.
+////////////////////////////////////////////////////////////////////
+INLINE GraphicsOutput::RenderTextureMode GraphicsOutput::
+get_rtm_mode(int i) const {
+  if ((i < 0) || (i >= ((int)_textures.size()))) {
+    return RTM_none;
+  }
+  return _textures[i]._rtm_mode;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -225,7 +249,12 @@ INLINE bool GraphicsOutput::
 get_delete_flag() const {
   // We only delete the window or buffer automatically when it is
   // no longer associated with a texture.
-  return _delete_flag && !_hold_texture.is_valid_pointer();
+  for (int i=0; i<(int)_hold_textures.size(); i++) {
+    if (_hold_textures[i].is_valid_pointer()) {
+      return false;
+    }
+  }
+  return _delete_flag;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 185 - 112
panda/src/display/graphicsOutput.cxx

@@ -80,7 +80,6 @@ GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
   _y_size = 0;
   _has_size = false;
   _is_valid = false;
-  _rtm_mode = RTM_none;
   _flip_ready = false;
   _needs_context = true;
   _cube_map_index = -1;
@@ -187,12 +186,24 @@ GraphicsOutput::
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsOutput::setup_render_texture
+//     Function: GraphicsOutput::clear_render_textures
+//       Access: Published
+//  Description: If the GraphicsOutput is currently rendering to
+//               a texture, then all textures are dissociated from
+//               the GraphicsOuput.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+clear_render_textures() {
+  MutexHolder holder(_lock);
+  _textures.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::add_render_texture
 //       Access: Published
 //  Description: Creates a new Texture object, suitable for rendering
-//               the contents of this buffer into, and stores it in
-//               _texture.  This also disassociates the previous
-//               texture (if any).
+//               the contents of this buffer into, and appends it to
+//               the list of render textures.
 //
 //               If tex is not NULL, it is the texture that will be
 //               set up for rendering into; otherwise, a new Texture
@@ -200,66 +211,84 @@ GraphicsOutput::
 //               get_texture() to retrieve the new texture pointer
 //               later).
 //
-//               If allow_bind is true, and this GraphicsOutput is an
-//               offscreen graphics buffer that has not yet been
-//               rendered into, it will attempt to set up the buffer
-//               for rendering directly into the texture, avoiding the
-//               cost of the copy-to-texture-memory each frame.  This
-//               is not supported by all graphics hardware, but if it
-//               is not supported, this option is quietly ignored.
-//
-//               If to_ram is true, the texture image will be
-//               downloaded from the framebuffer memory into system
-//               RAM each frame, which is more expensive but allows
-//               the texture to subsequently be applied to any GSG.
-//               Otherwise, the texture image remains in texture
-//               memory only.
-//
 //               Also see make_texture_buffer(), which is a
 //               higher-level interface for preparing
 //               render-to-a-texture mode.
 ////////////////////////////////////////////////////////////////////
 void GraphicsOutput::
-setup_render_texture(Texture *tex, RenderTextureMode mode) {
+add_render_texture(Texture *tex, RenderTextureMode mode) {
   if (mode == RTM_none) {
     return;
   }
   MutexHolder holder(_lock);
-  _rtm_mode = mode;
 
   if (tex == (Texture *)NULL) {
-    _texture = new Texture(get_name());
-    _texture->set_wrap_u(Texture::WM_clamp);
-    _texture->set_wrap_v(Texture::WM_clamp);
+    tex = new Texture(get_name());
+    tex->set_wrap_u(Texture::WM_clamp);
+    tex->set_wrap_v(Texture::WM_clamp);
   } else {
-    _texture = tex;
-    _texture->clear_ram_image();
+    tex->clear_ram_image();
   }
-  _texture->set_match_framebuffer_format(true);
+  tex->set_match_framebuffer_format(true);
   
   // Go ahead and tell the texture our anticipated size, even if it
   // might be inaccurate (particularly if this is a GraphicsWindow,
   // which has system-imposed restrictions on size).
-  _texture->set_x_size(get_x_size());
-  _texture->set_y_size(get_y_size());
-
+  tex->set_x_size(get_x_size());
+  tex->set_y_size(get_y_size());
+  
+  RenderTexture result;
+  result._texture = tex;
+  result._rtm_mode = mode;
+  _textures.push_back(result);
+  
   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
   set_inverted(_gsg->get_copy_texture_inverted());
+
+  // Sanity check that we don't have two textures of the same type.
+  int count_stencil_textures = 0;
+  int count_depth_textures = 0;
+  int count_color_textures = 0;
+  for (int i=0; i<count_textures(); i++) {
+    Texture::Format fmt = get_texture(i)->get_format();
+    if (fmt == Texture::F_depth_component) {
+      count_depth_textures += 1;
+    } else if (fmt == Texture::F_stencil_index) {
+      count_stencil_textures += 1;
+    } else {
+      count_color_textures += 1;
+    }
+  }
+  if ((count_color_textures > 1)||
+      (count_depth_textures > 1)||
+      (count_stencil_textures > 1)) {
+    display_cat.error() <<
+      "Currently, each GraphicsOutput can only render to one color texture, "
+      "one depth texture, and one stencil texture at a time.  RTM aborted.\n";
+    clear_render_textures();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::setup_render_texture
 //       Access: Published
-//  Description:
+//  Description: This is a deprecated interface that made sense back
+//               when GraphicsOutputs could only render into one
+//               texture at a time.  From now on, use 
+//               clear_render_textures and add_render_texture
+//               instead.
 ////////////////////////////////////////////////////////////////////
 void GraphicsOutput::
 setup_render_texture(Texture *tex, bool allow_bind, bool to_ram) {
+  display_cat.warning() <<
+    "Using deprecated setup_render_texture interface.\n";
+  clear_render_textures();
   if (to_ram) {
-    setup_render_texture(tex, RTM_copy_ram);
+    add_render_texture(tex, RTM_copy_ram);
   } else if (allow_bind) {
-    setup_render_texture(tex, RTM_bind_or_copy);
+    add_render_texture(tex, RTM_bind_or_copy);
   } else {
-    setup_render_texture(tex, RTM_copy_texture);
+    add_render_texture(tex, RTM_copy_texture);
   }
 }
 
@@ -577,7 +606,20 @@ get_texture_card() {
   PT(GeomNode) gnode = new GeomNode("texture card");
   gnode->add_geom(_texture_card);
   NodePath path(gnode);
-  path.set_texture(get_texture(), 0);
+
+  // The texture card, by default, is textured with the first
+  // render-to-texture output texture.  Depth and stencil
+  // textures are ignored.  The user can freely alter the
+  // card's texture attrib.
+  for (int i=0; i<count_textures(); i++) {
+    Texture *texture = get_texture(i);
+    if ((texture->get_format() != Texture::F_depth_component) &&
+        (texture->get_format() != Texture::F_stencil_index)) {
+      path.set_texture(texture, 0);
+      break;
+    }
+  }
+  
   return path;
 }
 
@@ -635,7 +677,7 @@ make_texture_buffer(const string &name, int x_size, int y_size,
     // host window size.  If the user requests this, we have to use a
     // parasite buffer.
     buffer = engine->make_parasite(host, name, sort, x_size, y_size);
-    buffer->setup_render_texture(tex, false, to_ram);
+    buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_copy_texture);
     return buffer;
   }
 
@@ -644,7 +686,7 @@ make_texture_buffer(const string &name, int x_size, int y_size,
     // since it all amounts to the same thing anyway--this will
     // actually create a new GraphicsWindow.
     buffer = engine->make_buffer(gsg, name, sort, x_size, y_size);
-    buffer->setup_render_texture(tex, false, to_ram);
+    buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_copy_texture);
     return buffer;
   }
   
@@ -661,7 +703,7 @@ make_texture_buffer(const string &name, int x_size, int y_size,
       (x_size <= host->get_x_size() && y_size <= host->get_y_size())) {
     buffer = engine->make_parasite(host, name, sort, x_size, y_size);
     if (buffer != (GraphicsOutput *)NULL) {
-      buffer->setup_render_texture(tex, false, to_ram);
+      buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_copy_texture);
       return buffer;
     }
   }
@@ -680,7 +722,11 @@ make_texture_buffer(const string &name, int x_size, int y_size,
         buffer = engine->make_buffer(sb_gsg, name, sort, x_size, y_size);
         if (buffer != (GraphicsOutput *)NULL) {
           // Check the buffer for goodness.
-          buffer->setup_render_texture(tex, allow_bind, to_ram);
+          if (allow_bind) {
+            buffer->add_render_texture(tex, RTM_bind_or_copy);
+          } else {
+            buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_copy_texture);
+          }
           engine->open_windows();
           if (buffer->is_valid()) {
             return buffer;
@@ -700,7 +746,11 @@ make_texture_buffer(const string &name, int x_size, int y_size,
   // source window is double-buffered.
   buffer = engine->make_buffer(gsg, name, sort, x_size, y_size);
   if (buffer != (GraphicsOutput *)NULL) {
-    buffer->setup_render_texture(tex, allow_bind, to_ram);
+    if (allow_bind) {
+      buffer->add_render_texture(tex, RTM_bind_or_copy);
+    } else {
+      buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_copy_texture);
+    }
     engine->open_windows();
     if (buffer->is_valid()) {
       return buffer;
@@ -714,7 +764,7 @@ make_texture_buffer(const string &name, int x_size, int y_size,
   // Looks like we have to settle for a parasite buffer.
   if (x_size <= host->get_x_size() && y_size <= host->get_y_size()) {
     buffer = engine->make_parasite(host, name, sort, x_size, y_size);
-    buffer->setup_render_texture(tex, false, to_ram);
+    buffer->add_render_texture(tex, to_ram ? RTM_copy_ram : RTM_copy_texture);
     return buffer;
   }
 
@@ -926,10 +976,7 @@ begin_frame() {
   // Okay, we already have a GSG, so activate it.
   make_current();
   
-  if (_rtm_mode == RTM_bind_or_copy) {
-    // Release the texture so we can render into the frame buffer.
-    _gsg->framebuffer_release_texture(this, get_texture());
-  }
+  begin_render_texture();
   
   _cube_map_index = -1;
   _cube_map_dr = NULL;
@@ -982,33 +1029,24 @@ end_frame() {
   nassertv(_gsg != (GraphicsStateGuardian *)NULL);
   _gsg->end_frame();
 
-  // If _rtm_mode isn't RTM_none, it means we should copy or lock the
-  // framebuffer to the GraphicsOutput's associated texture after the
-  // frame has rendered.
-  if (_rtm_mode != RTM_none) {
+  // Handle all render-to-texture operations that use bind-to-texture
+  end_render_texture();
+  
+  // Handle all render-to-texture operations that use copy-to-texture
+  for (int i=0; i<count_textures(); i++) {
+    RenderTextureMode rtm_mode = get_rtm_mode(i);
+    if ((rtm_mode == RTM_none)||(rtm_mode == RTM_bind_or_copy)) {
+      continue;
+    }
+
+    Texture *texture = get_texture(i);
     PStatTimer timer(_copy_texture_pcollector);
     nassertv(has_texture());
-
-    // If _rtm_mode is one of the bind-modes, it means we should attempt
-    // to lock the framebuffer directly to the texture memory, avoiding
-    // the copy.
-    if (_rtm_mode == RTM_bind_or_copy) {
-      if (display_cat.is_debug()) {
-        display_cat.debug()
-          << "Locking texture for " << get_name() << " at frame end.\n";
-      }
-      if (!_gsg->framebuffer_bind_to_texture(this, get_texture())) {
-        display_cat.warning()
-          << "Lock-to-texture failed, resorting to copy.\n";
-        _rtm_mode = RTM_copy_texture;
-      }
-    }
     
-    if ((_rtm_mode == RTM_copy_texture)||
-        (_rtm_mode == RTM_copy_ram)||
-        ((_rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
-        ((_rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
-      _trigger_copy = false;
+    if ((rtm_mode == RTM_copy_texture)||
+        (rtm_mode == RTM_copy_ram)||
+        ((rtm_mode == RTM_triggered_copy_texture)&&(_trigger_copy))||
+        ((rtm_mode == RTM_triggered_copy_ram)&&(_trigger_copy))) {
       if (display_cat.is_debug()) {
         display_cat.debug()
           << "Copying texture for " << get_name() << " at frame end.\n";
@@ -1017,30 +1055,31 @@ end_frame() {
       }
       RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
       if (_cube_map_dr != (DisplayRegion *)NULL) {
-        if ((_rtm_mode == RTM_copy_ram)||(_rtm_mode == RTM_triggered_copy_ram)) {
-          _gsg->framebuffer_copy_to_ram(get_texture(), _cube_map_index,
+        if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
+          _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
                                         _cube_map_dr, buffer);
         } else {
-          _gsg->framebuffer_copy_to_texture(get_texture(), _cube_map_index,
+          _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
                                             _cube_map_dr, buffer);
         }
       } else {
-        if ((_rtm_mode == RTM_copy_ram)||(_rtm_mode == RTM_triggered_copy_ram)) {
-          _gsg->framebuffer_copy_to_ram(get_texture(), _cube_map_index,
+        if ((rtm_mode == RTM_copy_ram)||(rtm_mode == RTM_triggered_copy_ram)) {
+          _gsg->framebuffer_copy_to_ram(texture, _cube_map_index,
                                         _default_display_region, buffer);
         } else {
-          _gsg->framebuffer_copy_to_texture(get_texture(), _cube_map_index,
+          _gsg->framebuffer_copy_to_texture(texture, _cube_map_index,
                                             _default_display_region, buffer);
         }
       }
     }
   }
-
+  _trigger_copy = false;
+  
   // If we're not single-buffered, we're now ready to flip.
   if (!_gsg->get_properties().is_single_buffered()) {
     _flip_ready = true;
   }
-
+  
   // In one-shot mode, we request the GraphicsEngine to delete the
   // window after we have rendered a frame.
   if (_one_shot) {
@@ -1059,23 +1098,53 @@ end_frame() {
       
       // If we were rendering directly to texture, we can't delete the
       // buffer until the texture is gone too.
-      if (_rtm_mode == RTM_bind_or_copy) {
-        _hold_texture = _texture;
+      for (int i=0; i<count_textures(); i++) {
+        if (get_rtm_mode(i) == RTM_bind_or_copy) {
+          _hold_textures.push_back(get_texture(i));
+        }
       }
     }
 
-    // We have to be sure to clear the _texture pointer, though, or
-    // we'll end up holding a reference to it forever.
-    _texture = NULL;
-    
-    // And we need to stop trying to copy to the texture.
-    _rtm_mode = RTM_none;
+    // We have to be sure to clear the _textures pointers, though, or
+    // we'll end up holding a reference to the textures forever.
+    clear_render_textures();
   }
-
+  
   _cube_map_index = -1;
   _cube_map_dr = NULL;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::begin_render_texture
+//       Access: Public, Virtual
+//  Description: If the GraphicsOutput supports direct render-to-texture,
+//               and if any setup needs to be done during begin_frame,
+//               then the setup code should go here.  Any textures that
+//               can not be rendered to directly should be reflagged
+//               as RTM_copy_texture.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+begin_render_texture() {
+  for (int i=0; i<count_textures(); i++) {
+    if (get_rtm_mode(i) == RTM_bind_or_copy) {
+      _textures[i]._rtm_mode = RTM_copy_texture;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::end_render_texture
+//       Access: Public, Virtual
+//  Description: If the GraphicsOutput supports direct render-to-texture,
+//               and if any setup needs to be done during end_frame,
+//               then the setup code should go here.  Any textures that
+//               could not be rendered to directly should be reflagged
+//               as RTM_copy_texture.
+////////////////////////////////////////////////////////////////////
+void GraphicsOutput::
+end_render_texture() {
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsOutput::change_scenes
 //       Access: Public
@@ -1095,31 +1164,35 @@ change_scenes(DisplayRegion *new_dr) {
     _cube_map_index = new_cube_map_index;
     _cube_map_dr = new_dr;
 
-    if (_rtm_mode != RTM_none) {
-      if (_rtm_mode == RTM_bind_or_copy) {
-        // In render-to-texture mode, switch the rendering backend to
-        // the new cube map face, so that the subsequent frame will be
-        // rendered to the new face.
-
-        select_cube_map(new_cube_map_index);
-        
-      } else if (old_cube_map_index != -1) {
-        // In copy-to-texture mode, copy the just-rendered framebuffer
-        // to the old cube map face.
-        nassertv(old_cube_map_dr != (DisplayRegion *)NULL);
-        if (display_cat.is_debug()) {
-          display_cat.debug()
-            << "Copying texture for " << get_name() << " at scene change.\n";
-          display_cat.debug()
-            << "cube_map_index = " << old_cube_map_index << "\n";
-        }
-        RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
-        if (_rtm_mode == RTM_copy_ram) {
-          _gsg->framebuffer_copy_to_ram(get_texture(), old_cube_map_index, 
-                                        old_cube_map_dr, buffer);
-        } else {
-          _gsg->framebuffer_copy_to_texture(get_texture(), old_cube_map_index, 
-                                            old_cube_map_dr, buffer);
+    for (int i=0; i<count_textures(); i++) {
+      Texture *texture = get_texture(i);
+      RenderTextureMode rtm_mode = get_rtm_mode(i);
+      if (rtm_mode != RTM_none) {
+        if (rtm_mode == RTM_bind_or_copy) {
+          // In render-to-texture mode, switch the rendering backend to
+          // the new cube map face, so that the subsequent frame will be
+          // rendered to the new face.
+          
+          select_cube_map(new_cube_map_index);
+          
+        } else if (old_cube_map_index != -1) {
+          // In copy-to-texture mode, copy the just-rendered framebuffer
+          // to the old cube map face.
+          nassertv(old_cube_map_dr != (DisplayRegion *)NULL);
+          if (display_cat.is_debug()) {
+            display_cat.debug()
+              << "Copying texture for " << get_name() << " at scene change.\n";
+            display_cat.debug()
+              << "cube_map_index = " << old_cube_map_index << "\n";
+          }
+          RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
+          if (rtm_mode == RTM_copy_ram) {
+            _gsg->framebuffer_copy_to_ram(texture, old_cube_map_index, 
+                                          old_cube_map_dr, buffer);
+          } else {
+            _gsg->framebuffer_copy_to_texture(texture, old_cube_map_index, 
+                                              old_cube_map_dr, buffer);
+          }
         }
       }
     }

+ 23 - 13
panda/src/display/graphicsOutput.h

@@ -84,11 +84,14 @@ PUBLISHED:
   INLINE GraphicsPipe *get_pipe() const;
   INLINE const string &get_name() const;
 
-  INLINE bool has_texture() const;  
-  INLINE Texture *get_texture() const;  
-  void setup_render_texture(Texture *tex, RenderTextureMode mode);
+  INLINE int count_textures() const;
+  INLINE bool has_texture() const;
+  INLINE Texture *get_texture(int i=0) const;
+  INLINE RenderTextureMode get_rtm_mode(int i=0) const;
+  void clear_render_textures();
+  void add_render_texture(Texture *tex, RenderTextureMode mode);
   void setup_render_texture(Texture *tex, bool allow_bind, bool to_ram);
-  
+
   INLINE int get_x_size() const;
   INLINE int get_y_size() const;
   INLINE bool has_size() const;
@@ -157,10 +160,13 @@ public:
   // It is an error to call any of the following methods from any
   // thread other than the draw thread.  These methods are normally
   // called by the GraphicsEngine.
-  virtual bool begin_frame();
   void clear();
+  virtual bool begin_frame();
   virtual void end_frame();
 
+  virtual void begin_render_texture();
+  virtual void end_render_texture();
+
   void change_scenes(DisplayRegion *new_dr);
   virtual void select_cube_map(int cube_map_index);
 
@@ -182,11 +188,15 @@ public:
   
 protected:
 
+  class RenderTexture {
+  public:
+    PT(Texture) _texture;
+    RenderTextureMode _rtm_mode;
+  };
   PT(GraphicsStateGuardian) _gsg;
   PT(GraphicsPipe) _pipe;
   string _name;
-  PT(Texture) _texture;
-  RenderTextureMode _rtm_mode;
+  pvector<RenderTexture> _textures;
   bool _flip_ready;
   bool _needs_context;
   int _cube_map_index;
@@ -211,12 +221,12 @@ protected:
   bool _inverted;
   bool _delete_flag;
 
-  // This weak pointer is used to keep track of whether the buffer's
-  // bound Texture has been deleted or not.  Until it has, we don't
-  // auto-close the buffer (since that would deallocate the memory
-  // associated with the texture).
-  WPT(Texture) _hold_texture;
-
+  // These weak pointers are used to keep track of whether the
+  // buffer's bound Texture has been deleted or not.  Until they have,
+  // we don't auto-close the buffer (since that would deallocate the
+  // memory associated with the texture).
+  pvector<WPT(Texture)> _hold_textures;
+  
 protected:
   Mutex _lock; 
   // protects _display_regions.

+ 0 - 33
panda/src/display/graphicsStateGuardian.cxx

@@ -971,39 +971,6 @@ end_draw_primitives() {
   _vertex_data = NULL;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::framebuffer_bind_to_texture
-//       Access: Public, Virtual
-//  Description: Works in lieu of copy_texture() to bind the primary
-//               render buffer of the framebuffer to the indicated
-//               texture (which must have been created via
-//               GraphicsOutput::setup_render_texture()).
-//
-//               If supported by the graphics backend, this will make
-//               the framebuffer memory directly accessible within the
-//               texture, but the frame cannot be rendered again until
-//               framebuffer_release_texture() is called.
-//
-//               The return value is true if successful, false on
-//               failure.
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-framebuffer_bind_to_texture(GraphicsOutput *, Texture *) {
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::framebuffer_release_texture
-//       Access: Public, Virtual
-//  Description: Undoes a previous call to
-//               framebuffer_bind_to_texture().  The framebuffer may
-//               again be rendered into, and the contents of the
-//               texture is undefined.
-////////////////////////////////////////////////////////////////////
-void GraphicsStateGuardian::
-framebuffer_release_texture(GraphicsOutput *, Texture *) {
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::do_issue_color_scale
 //       Access: Public, Virtual

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

@@ -191,9 +191,6 @@ public:
   virtual void draw_points(const GeomPoints *primitive);
   virtual void end_draw_primitives();
 
-  virtual bool framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex);
-  virtual void framebuffer_release_texture(GraphicsOutput *win, Texture *tex);
-
   INLINE bool reset_if_new();
   INLINE void mark_new();
   virtual void reset();

+ 1 - 4
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -202,10 +202,7 @@ public:
   (Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0;
   virtual bool framebuffer_copy_to_ram
   (Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0;
-
-  virtual bool framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex)=0;
-  virtual void framebuffer_release_texture(GraphicsOutput *win, Texture *tex)=0;
-
+  
   virtual CoordinateSystem get_internal_coordinate_system() const=0;
 
   virtual void bind_light(PointLight *light_obj, const NodePath &light, 

+ 99 - 32
panda/src/wgldisplay/wglGraphicsBuffer.cxx

@@ -89,6 +89,75 @@ begin_frame() {
   return GraphicsBuffer::begin_frame();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: wglGraphicsStateGuardian::begin_render_texture
+//       Access: Public, Virtual
+//  Description: If the GraphicsOutput supports direct render-to-texture,
+//               and if any setup needs to be done during begin_frame,
+//               then the setup code should go here.  Any textures that
+//               can not be rendered to directly should be reflagged
+//               as RTM_copy_texture.
+////////////////////////////////////////////////////////////////////
+void wglGraphicsBuffer::
+begin_render_texture() {
+  wglGraphicsStateGuardian *wglgsg;
+  DCAST_INTO_V(wglgsg, _gsg);
+  
+  if (_gsg->get_properties().is_single_buffered()) {
+    wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
+  } else {
+    wglgsg->_wglReleaseTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsOutput::end_render_texture
+//       Access: Public, Virtual
+//  Description: If the GraphicsOutput supports direct render-to-texture,
+//               and if any setup needs to be done during end_frame,
+//               then the setup code should go here.  Any textures that
+//               could not be rendered to directly should be reflagged
+//               as RTM_copy_texture.
+////////////////////////////////////////////////////////////////////
+void wglGraphicsBuffer::
+end_render_texture() {
+  wglGraphicsStateGuardian *wglgsg;
+  DCAST_INTO_V(wglgsg, _gsg);
+
+  // Find the color texture, if there is one. That one can be bound to
+  // the framebuffer.  All others must be marked RTM_copy_to_texture.
+  int tex_index = -1;
+  for (int i=0; i<count_textures(); i++) {
+    if (get_rtm_mode(i) == RTM_bind_or_copy) {
+      if ((get_texture(i)->get_format() != Texture::F_depth_component)&&
+          (get_texture(i)->get_format() != Texture::F_stencil_index)&&
+          (tex_index < 0)) {
+        tex_index = i;
+      } else {
+        _textures[i]._rtm_mode = RTM_copy_texture;
+      }
+    }
+  }
+  
+  if (tex_index >= 0) {
+    Texture *tex = get_texture(tex_index);
+    TextureContext *tc = tex->prepare_now(_gsg->get_prepared_objects(), _gsg);
+    nassertv(tc != (TextureContext *)NULL);
+    CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
+    GLenum target = wglgsg->get_texture_target(tex->get_texture_type());
+    if (target == GL_NONE) {
+      _textures[tex_index]._rtm_mode = RTM_copy_texture;
+      return;
+    }
+    GLP(BindTexture)(target, gtc->_index);
+    if (_gsg->get_properties().is_single_buffered()) {
+      wglgsg->_wglBindTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
+    } else {
+      wglgsg->_wglBindTexImageARB(_pbuffer, WGL_BACK_LEFT_ARB);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: wglGraphicsBuffer::select_cube_map
 //       Access: Public, Virtual
@@ -231,29 +300,6 @@ open_buffer() {
   }
 
   _pbuffer_dc = wglgsg->_wglGetPbufferDCARB(_pbuffer);
-  if (wgldisplay_cat.is_debug()) {
-    wgldisplay_cat.debug()
-      << "Created PBuffer " << _pbuffer << ", DC " << _pbuffer_dc << "\n";
-    switch (_rtm_mode) {
-    case RTM_bind_or_copy:
-      wgldisplay_cat.debug()
-        << "pbuffer renders directly to texture.\n";
-      break;
-
-    case RTM_copy_texture:
-      wgldisplay_cat.debug()
-        << "pbuffer copies indirectly into texture.\n";
-      break;
-
-    case RTM_copy_ram:
-      wgldisplay_cat.debug()
-        << "pbuffer copies indirectly into system RAM.\n";
-      break;
-
-    default:
-      break;
-    }
-  }
   
   wglMakeCurrent(_pbuffer_dc, wglgsg->get_context(_pbuffer_dc));
   wglgsg->report_my_gl_errors();
@@ -286,9 +332,15 @@ make_pbuffer(HDC twindow_dc) {
 
   if (wglgsg->_supports_pixel_format) {
     bool got_pbuffer_format = false;
+    bool any_binds = false;
+    
+    for (int i=0; i<count_textures(); i++) {
+      if (get_rtm_mode(i) == RTM_bind_or_copy) {
+        any_binds = true;
+      }
+    }
 
-    if ((_rtm_mode == RTM_bind_or_copy) &&
-        wglgsg->_supports_render_texture) {
+    if (any_binds && wglgsg->_supports_render_texture) {
       // First, try to get a pbuffer format that supports
       // render-to-texture.
       int new_pbformat = choose_pbuffer_format(twindow_dc, true);
@@ -299,8 +351,13 @@ make_pbuffer(HDC twindow_dc) {
     }
 
     if (!got_pbuffer_format) {
-      // Failing that, just get a matching pbuffer format.
-      _rtm_mode = RTM_copy_texture;
+      // Failing that, just get a matching pbuffer format,
+      // and disable RTM_bind_or_copy.
+      for (int i=0; i<count_textures(); i++) {
+        if (get_rtm_mode(i) == RTM_bind_or_copy) {
+          _textures[i]._rtm_mode = RTM_copy_texture;
+        }
+      }
       int new_pbformat = choose_pbuffer_format(twindow_dc, false);
       if (new_pbformat != 0) {
         pbformat = new_pbformat;
@@ -322,8 +379,18 @@ make_pbuffer(HDC twindow_dc) {
   int iattrib_list[max_attrib_list];
   int ni = 0;
 
-  if (_rtm_mode == RTM_bind_or_copy) {
-    nassertr(_texture != (Texture *)NULL, false);
+  // Find the texture to bind to the color buffer.
+  Texture *bindtexture = NULL;
+  for (int i=0; i<count_textures(); i++) {
+    if ((get_rtm_mode(i) == RTM_bind_or_copy)&&
+        (get_texture(i)->get_format() != Texture::F_depth_component)&&
+        (get_texture(i)->get_format() != Texture::F_stencil_index)) {
+      bindtexture = get_texture(i);
+      break;
+    }
+  }
+  
+  if (bindtexture != 0) {
 
     if (_gsg->get_properties().get_frame_buffer_mode() & FrameBufferProperties::FM_alpha) {
       iattrib_list[ni++] = WGL_TEXTURE_FORMAT_ARB;
@@ -333,12 +400,12 @@ make_pbuffer(HDC twindow_dc) {
       iattrib_list[ni++] = WGL_TEXTURE_RGB_ARB;
     }
 
-    if (_texture->uses_mipmaps()) {
+    if (bindtexture->uses_mipmaps()) {
       iattrib_list[ni++] = WGL_MIPMAP_TEXTURE_ARB;
       iattrib_list[ni++] = 1;
     }
 
-    switch (_texture->get_texture_type()) {
+    switch (bindtexture->get_texture_type()) {
     case Texture::TT_cube_map:
       iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
       iattrib_list[ni++] = WGL_TEXTURE_CUBE_MAP_ARB;
@@ -353,7 +420,7 @@ make_pbuffer(HDC twindow_dc) {
       iattrib_list[ni++] = WGL_TEXTURE_TARGET_ARB;
       iattrib_list[ni++] = WGL_TEXTURE_2D_ARB;
     }
-  }    
+  }  
 
   // Terminate the list.
   nassertr(ni <= max_attrib_list, false);

+ 3 - 0
panda/src/wgldisplay/wglGraphicsBuffer.h

@@ -52,6 +52,9 @@ public:
   virtual void make_current();
   virtual void release_gsg();
 
+  virtual void begin_render_texture();
+  virtual void end_render_texture();
+  
   virtual void process_events();
 
 protected:

+ 0 - 60
panda/src/wgldisplay/wglGraphicsStateGuardian.cxx

@@ -63,66 +63,6 @@ wglGraphicsStateGuardian::
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: wglGraphicsStateGuardian::framebuffer_bind_to_texture
-//       Access: Public, Virtual
-//  Description: Works in lieu of copy_texture() to bind the primary
-//               render buffer of the framebuffer to the indicated
-//               texture (which must have been created via
-//               GraphicsOutput::setup_render_texture()).
-//
-//               If supported by the graphics backend, this will make
-//               the framebuffer memory directly accessible within the
-//               texture, but the frame cannot be rendered again until
-//               framebuffer_release_texture() is called.
-//
-//               The return value is true if successful, false on
-//               failure.
-////////////////////////////////////////////////////////////////////
-bool wglGraphicsStateGuardian::
-framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex) {
-  wglGraphicsBuffer *buffer;
-  DCAST_INTO_R(buffer, win, false);
-
-  TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
-  nassertr(tc != (TextureContext *)NULL, false);
-  CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
-  GLenum target = get_texture_target(tex->get_texture_type());
-  if (target == GL_NONE) {
-    // Invalid texture, can't bind it.
-    return false;
-  }
-  GLP(BindTexture)(target, gtc->_index);
-
-  if (get_properties().is_single_buffered()) {
-    _wglBindTexImageARB(buffer->_pbuffer, WGL_FRONT_LEFT_ARB);
-  } else {
-    _wglBindTexImageARB(buffer->_pbuffer, WGL_BACK_LEFT_ARB);
-  }
-
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: wglGraphicsStateGuardian::framebuffer_release_texture
-//       Access: Public, Virtual
-//  Description: Undoes a previous call to
-//               framebuffer_bind_to_texture().  The framebuffer may
-//               again be rendered into, and the contents of the
-//               texture is undefined.
-////////////////////////////////////////////////////////////////////
-void wglGraphicsStateGuardian::
-framebuffer_release_texture(GraphicsOutput *win, Texture *) {
-  wglGraphicsBuffer *buffer;
-  DCAST_INTO_V(buffer, win);
-
-  if (get_properties().is_single_buffered()) {
-    _wglReleaseTexImageARB(buffer->_pbuffer, WGL_FRONT_LEFT_ARB);
-  } else {
-    _wglReleaseTexImageARB(buffer->_pbuffer, WGL_BACK_LEFT_ARB);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: wglGraphicsStateGuardian::reset
 //       Access: Public, Virtual

+ 0 - 3
panda/src/wgldisplay/wglGraphicsStateGuardian.h

@@ -43,9 +43,6 @@ public:
   INLINE bool made_context() const;
   INLINE HGLRC get_context(HDC hdc);
 
-  virtual bool framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex);
-  virtual void framebuffer_release_texture(GraphicsOutput *win, Texture *tex);
-
   virtual void reset();
 
   INLINE HDC get_twindow_dc();