Browse Source

split out PreparedGraphicsObjects from GraphicsStateGuardians

David Rose 22 years ago
parent
commit
232f9395bf

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

@@ -551,7 +551,7 @@ get_screenshot(PNMImage &image) {
                 PixelBuffer::F_rgb);
 
   RenderBuffer buffer = gsg->get_render_buffer(get_screenshot_buffer_type());
-  if (!p.copy(gsg, this, buffer)) {
+  if (!gsg->copy_pixel_buffer(&p, this, buffer)) {
     return false;
   }
 

+ 1 - 1
panda/src/display/graphicsBuffer.h

@@ -1,5 +1,5 @@
 // Filename: graphicsBuffer.h
-// Created by:  mike (09Jan97)
+// Created by:  drose (06Feb04)
 //
 ////////////////////////////////////////////////////////////////////
 //

+ 2 - 1
panda/src/display/graphicsOutput.cxx

@@ -437,7 +437,8 @@ end_frame() {
     nassertv(has_texture());
     DisplayRegion dr(this, _x_size, _y_size);
     RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
-    get_texture()->copy(_gsg, &dr, buffer);
+    TextureContext *tc = get_texture()->prepare_now(_gsg->get_prepared_objects(), _gsg);
+    _gsg->copy_texture(tc, &dr, buffer);
   }
 }
 

+ 11 - 0
panda/src/display/graphicsStateGuardian.I

@@ -40,6 +40,17 @@ ClipPlaneInfo() {
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::release_all_textures
+//       Access: Public
+//  Description: Frees the resources for all textures associated with
+//               this GSG.
+////////////////////////////////////////////////////////////////////
+INLINE int GraphicsStateGuardian::
+release_all_textures() {
+  return _prepared_objects->release_all_textures();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::set_active
 //       Access: Published

+ 30 - 174
panda/src/display/graphicsStateGuardian.cxx

@@ -79,6 +79,7 @@ GraphicsStateGuardian(const FrameBufferProperties &properties) {
   _needs_reset = true;
   _closing_gsg = false;
   _active = true;
+  _prepared_objects = new PreparedGraphicsObjects;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -146,58 +147,15 @@ get_render_buffer(int buffer_type) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::release_all_textures
-//       Access: Public
-//  Description: Frees the resources for all textures associated with
-//               this GSG.
-////////////////////////////////////////////////////////////////////
-void GraphicsStateGuardian::
-release_all_textures() {
-  // We must get a copy of the _prepared_textures list first, because
-  // each call to release_texture() will remove that texture from the
-  // list, and we don't want to traverse a list while we're modifying
-  // it!
-
-  Textures temp = _prepared_textures;
-  for (Textures::const_iterator ti = temp.begin();
-       ti != temp.end();
-       ++ti) {
-    release_texture(*ti);
-  }
-
-  // Now that we've released all of the textures, the
-  // _prepared_textures list should have completely emptied itself.
-  nassertv(_prepared_textures.empty());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::release_all_geoms
-//       Access: Public
-//  Description: Frees the resources for all Geoms and GeomNodes
-//               associated with this GSG.  Warning!  This may make
-//               the Geoms unrenderable, if the Panda-level
-//               information has been deleted.
+//     Function: GraphicsStateGuardian::get_prepared_objects
+//       Access: Public, Virtual
+//  Description: Returns the set of texture and geom objects that have
+//               been prepared with this GSG (and possibly other GSG's
+//               that share objects).
 ////////////////////////////////////////////////////////////////////
-void GraphicsStateGuardian::
-release_all_geoms() {
-  // As above, for both Geoms and GeomNodes.
-
-  Geoms temp_geoms = _prepared_geoms;
-  for (Geoms::const_iterator gi = temp_geoms.begin();
-       gi != temp_geoms.end();
-       ++gi) {
-    release_geom(*gi);
-  }
-
-  GeomNodes temp_geom_nodes = _prepared_geom_nodes;
-  for (GeomNodes::const_iterator gni = temp_geom_nodes.begin();
-       gni != temp_geom_nodes.end();
-       ++gni) {
-    release_geom_node(*gni);
-  }
-
-  nassertv(_prepared_geoms.empty());
-  nassertv(_prepared_geom_nodes.empty());
+PreparedGraphicsObjects *GraphicsStateGuardian::
+get_prepared_objects() {
+  return _prepared_objects;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -397,6 +355,12 @@ prepare_lens() {
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
 begin_frame() {
+  // Now we know the GSG is the currently active context, so this is a
+  // good time to release any textures or geoms that had been queued
+  // up to release in the past frame, and load up any newly requested
+  // textures.
+  _prepared_objects->update(this);
+  
   // Undo any lighting we had enabled last frame, to force the lights
   // to be reissued, in case their parameters or positions have
   // changed between frames.
@@ -1229,126 +1193,6 @@ set_blend_mode(ColorWriteAttrib::Mode, ColorBlendAttrib::Mode,
                TransparencyAttrib::Mode) {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::mark_prepared_texture
-//       Access: Protected
-//  Description: This is intended to be called from within
-//               prepare_texture().  It adds the indicated
-//               TextureContext pointer to the _prepared_textures set,
-//               and returns true if it was successfully added
-//               (i.e. it was not already in the set).
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-mark_prepared_texture(TextureContext *tc) {
-  bool prepared = _prepared_textures.insert(tc).second;
-#ifdef DO_PSTATS
-  if (prepared) {
-    _total_texusage_pcollector.add_level(tc->estimate_texture_memory());
-  }
-#endif
-  return prepared;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::unmark_prepared_texture
-//       Access: Protected
-//  Description: This is intended to be called from within
-//               release_texture().  It removes the indicated
-//               TextureContext pointer from the _prepared_textures
-//               set, and returns true if it was successfully removed
-//               (i.e. it had been in the set).
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-unmark_prepared_texture(TextureContext *tc) {
-  bool removed = (_prepared_textures.erase(tc) != 0);
-#ifdef DO_PSTATS
-  if (removed) {
-    _total_texusage_pcollector.sub_level(tc->estimate_texture_memory());
-  }
-#endif
-  return removed;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::mark_prepared_geom
-//       Access: Protected
-//  Description: This is intended to be called from within
-//               prepare_geom().  It adds the indicated GeomContext
-//               pointer to the _prepared_geoms set, and returns true
-//               if it was successfully added (i.e. it was not already
-//               in the set).
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-mark_prepared_geom(GeomContext *gc) {
-  bool prepared = _prepared_geoms.insert(gc).second;
-#ifdef DO_PSTATS
-  if (prepared) {
-    _total_geom_pcollector.add_level(1);
-  }
-#endif
-  return prepared;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::unmark_prepared_geom
-//       Access: Protected
-//  Description: This is intended to be called from within
-//               release_geom().  It removes the indicated GeomContext
-//               pointer from the _prepared_geoms set, and returns
-//               true if it was successfully removed (i.e. it had been
-//               in the set).
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-unmark_prepared_geom(GeomContext *gc) {
-  bool removed = (_prepared_geoms.erase(gc) != 0);
-#ifdef DO_PSTATS
-  if (removed) {
-    _total_geom_pcollector.sub_level(1);
-  }
-#endif
-  return removed;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::mark_prepared_geom_node
-//       Access: Protected
-//  Description: This is intended to be called from within
-//               prepare_geom_node().  It adds the indicated
-//               GeomNodeContext pointer to the _prepared_geom_nodes
-//               set, and returns true if it was successfully added
-//               (i.e. it was not already in the set).
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-mark_prepared_geom_node(GeomNodeContext *gnc) {
-  bool prepared = _prepared_geom_nodes.insert(gnc).second;
-#ifdef DO_PSTATS
-  if (prepared) {
-    _total_geom_node_pcollector.add_level(1);
-  }
-#endif
-  return prepared;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::unmark_prepared_geom_node
-//       Access: Protected
-//  Description: This is intended to be called from within
-//               release_geom_node().  It removes the indicated
-//               GeomNodeContext pointer from the _prepared_geom_nodes
-//               set, and returns true if it was successfully removed
-//               (i.e. it had been in the set).
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-unmark_prepared_geom_node(GeomNodeContext *gnc) {
-  bool removed = (_prepared_geom_nodes.erase(gnc) != 0);
-#ifdef DO_PSTATS
-  if (removed) {
-    _total_geom_node_pcollector.sub_level(1);
-  }
-#endif
-  return removed;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::free_pointers
 //       Access: Protected, Virtual
@@ -1371,8 +1215,18 @@ void GraphicsStateGuardian::
 close_gsg() {
   _closing_gsg = true;
   free_pointers();
-  release_all_textures();
-  release_all_geoms();
+
+  // If we're not sharing the prepared objects list with any other
+  // GSG, go ahread and release all the textures and geoms now.  This
+  // isn't really a reliable test of whether we are sharing this list,
+  // but it's not too important if we get this wrong since this ought
+  // to be an optional cleanup anyway.  (Presumably, the underlying
+  // graphics API will properly clean up outstanding textures and
+  // geoms when the last context using them is released.)
+  if (_prepared_objects->get_ref_count() == 1) {
+    release_all_textures();
+    //release_all_geoms();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1522,7 +1376,9 @@ get_untextured_state() {
 
 void GraphicsStateGuardian::
 traverse_prepared_textures(bool (*pertex_callbackfn)(TextureContext *,void *),void *callback_arg) {
-  for (Textures::const_iterator ti = _prepared_textures.begin(); ti != _prepared_textures.end();
+  PreparedGraphicsObjects::Textures::const_iterator ti;
+  for (ti = _prepared_objects->_prepared_textures.begin(); 
+       ti != _prepared_objects->_prepared_textures.end();
        ++ti) {
     bool bResult=(*pertex_callbackfn)(*ti,callback_arg);
     if(!bResult)

+ 7 - 18
panda/src/display/graphicsStateGuardian.h

@@ -26,6 +26,7 @@
 #include "frameBufferProperties.h"
 #include "displayRegionStack.h"
 #include "lensStack.h"
+#include "preparedGraphicsObjects.h"
 
 #include "graphicsStateGuardianBase.h"
 #include "graphicsThreadingModel.h"
@@ -69,8 +70,8 @@ public:
   virtual ~GraphicsStateGuardian();
 
 PUBLISHED:
-  void release_all_textures();
-  void release_all_geoms();
+  INLINE int release_all_textures();
+  //  int release_all_geoms();
 
   INLINE void set_active(bool active);
   INLINE bool is_active() const;
@@ -84,6 +85,8 @@ public:
   INLINE void set_scene(SceneSetup *scene_setup);
   INLINE SceneSetup *get_scene() const;
 
+  virtual PreparedGraphicsObjects *get_prepared_objects();
+
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual void apply_texture(TextureContext *tc);
   virtual void release_texture(TextureContext *tc);
@@ -193,13 +196,6 @@ protected:
                                                  CPT(DisplayRegion) dr)=0;
   virtual void restore_frame_buffer(SavedFrameBuffer *frame_buffer)=0;
 
-  bool mark_prepared_texture(TextureContext *tc);
-  bool unmark_prepared_texture(TextureContext *tc);
-  bool mark_prepared_geom(GeomContext *gc);
-  bool unmark_prepared_geom(GeomContext *gc);
-  bool mark_prepared_geom_node(GeomNodeContext *gnc);
-  bool unmark_prepared_geom_node(GeomNodeContext *gnc);
-
   virtual void free_pointers();
   virtual void close_gsg();
   void panic_deactivate();
@@ -276,6 +272,8 @@ protected:
   bool _closing_gsg;
   bool _active;
 
+  PT(PreparedGraphicsObjects) _prepared_objects;
+
 public:
   // Statistics
   static PStatCollector _total_texusage_pcollector;
@@ -321,15 +319,6 @@ private:
   pvector<ClipPlaneInfo> _clip_plane_info;
   bool _clip_planes_enabled_this_frame;
 
-  // NOTE: on win32 another DLL (e.g. libpandadx.dll) cannot access
-  // these sets directly due to exported template issue
-  typedef pset<TextureContext *> Textures;
-  Textures _prepared_textures;  
-  typedef pset<GeomContext *> Geoms;
-  Geoms _prepared_geoms;  
-  typedef pset<GeomNodeContext *> GeomNodes;
-  GeomNodes _prepared_geom_nodes;  
-
   FrameBufferProperties _properties;
   PT(GraphicsPipe) _pipe;
   GraphicsEngine *_engine;

+ 3 - 2
panda/src/distort/nonlinearImager.cxx

@@ -608,8 +608,9 @@ render_screen(GraphicsEngine *engine, NonlinearImager::Screen &screen) {
 
   // Copy the results of the render from the frame buffer into the
   // screen's texture.
-  screen._texture->copy(_gsg, scratch_region, 
-                        _gsg->get_render_buffer(RenderBuffer::T_back));
+  TextureContext *tc = screen._texture->prepare_now(_gsg->get_prepared_objects(), _gsg);
+  _gsg->copy_texture(tc, scratch_region,
+                     _gsg->get_render_buffer(RenderBuffer::T_back));
 
   // It might be nice if we didn't throw away the scratch region every
   // time, which prevents us from preserving cull state from one frame

+ 18 - 32
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -1643,6 +1643,9 @@ draw_sphere(GeomSphere *geom, GeomContext *) {
 //               responsibility of the calling function to later
 //               call release_texture() with this same pointer (which
 //               will also delete the pointer).
+//
+//               This function should not be called directly to
+//               prepare a texture.  Instead, call Texture::prepare().
 ////////////////////////////////////////////////////////////////////
 TextureContext *CLP(GraphicsStateGuardian)::
 prepare_texture(Texture *tex) {
@@ -1654,13 +1657,6 @@ prepare_texture(Texture *tex) {
   specify_texture(tex);
   apply_texture_immediate(tex);
 
-  bool inserted = mark_prepared_texture(gtc);
-
-  // If this assertion fails, the same texture was prepared twice,
-  // which shouldn't be possible, since the texture itself should
-  // detect this.
-  nassertr(inserted, gtc);
-
   report_my_gl_errors();
   return gtc;
 }
@@ -1695,32 +1691,19 @@ apply_texture(TextureContext *tc) {
 //     Function: CLP(GraphicsStateGuardian)::release_texture
 //       Access: Public, Virtual
 //  Description: Frees the GL resources previously allocated for the
-//               texture.
+//               texture.  This function should never be called
+//               directly; instead, call Texture::release() (or simply
+//               let the Texture destruct).
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsStateGuardian)::
 release_texture(TextureContext *tc) {
   CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
-  Texture *tex = tc->_texture;
-
-  if (!_closing_gsg) {
-    // Don't bother to delete the GL texture if we're about to destroy
-    // the context anyway.
-    GLP(DeleteTextures)(1, &gtc->_index);
-  }
-  gtc->_index = 0;
 
-  bool erased = unmark_prepared_texture(gtc);
-
-  // If this assertion fails, a texture was released that hadn't been
-  // prepared (or a texture was released twice).
-  nassertv(erased);
-
-  tex->clear_gsg(this);
+  GLP(DeleteTextures)(1, &gtc->_index);
+  report_my_gl_errors();
 
+  gtc->_index = 0;
   delete gtc;
-  if (!_closing_gsg) {
-    report_my_gl_errors();
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1811,7 +1794,7 @@ prepare_geom_node(GeomNode *node) {
   _texturing_enabled = old_texturing_enabled;
   _vertex_colors_enabled = old_vertex_colors_enabled;
 
-  bool inserted = mark_prepared_geom_node(ggnc);
+  bool inserted = _prepared_objects->mark_prepared_geom_node(ggnc);
 
   // If this assertion fails, the same GeomNode was prepared twice,
   // which shouldn't be possible, since the GeomNode itself should
@@ -1875,7 +1858,7 @@ release_geom_node(GeomNodeContext *gnc) {
     CLP(GeomNodeContext) *ggnc = DCAST(CLP(GeomNodeContext), gnc);
     GLP(DeleteLists)(ggnc->_index, 1);
 
-    bool erased = unmark_prepared_geom_node(ggnc);
+    bool erased = _prepared_objects->unmark_prepared_geom_node(ggnc);
 
     // If this assertion fails, a GeomNode was released that hadn't
     // been prepared (or a GeomNode was released twice).
@@ -2220,7 +2203,9 @@ issue_texture(const TextureAttrib *attrib) {
     enable_texturing(true);
     Texture *tex = attrib->get_texture();
     nassertv(tex != (Texture *)NULL);
-    tex->apply(this);
+
+    TextureContext *tc = tex->prepare_now(_prepared_objects, this);
+    apply_texture(tc);
   }
   report_my_gl_errors();
 }
@@ -3916,7 +3901,8 @@ save_frame_buffer(const RenderBuffer &buffer,
   if (buffer._buffer_type & RenderBuffer::T_back) {
     // Save the color buffer.
     sfb->_back_rgba = new Texture;
-    copy_texture(sfb->_back_rgba->prepare(this), dr, buffer);
+    TextureContext *tc = sfb->_back_rgba->prepare_now(_prepared_objects, this);
+    copy_texture(tc, dr, buffer);
   }
 
   return sfb;
@@ -3934,8 +3920,8 @@ restore_frame_buffer(SavedFrameBuffer *frame_buffer) {
   if (sfb->_back_rgba != (Texture *)NULL &&
       (sfb->_buffer._buffer_type & RenderBuffer::T_back) != 0) {
     // Restore the color buffer.
-    draw_texture(sfb->_back_rgba->prepare(this),
-                 sfb->_display_region, sfb->_buffer);
+    TextureContext *tc = sfb->_back_rgba->prepare_now(_prepared_objects, this);
+    draw_texture(tc, sfb->_display_region, sfb->_buffer);
   }
 
   if (sfb->_depth != (PixelBuffer *)NULL &&

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

@@ -183,7 +183,7 @@ make_gsg(const FrameBufferProperties &properties) {
 
   // Now we can make a GSG.
   PT(glxGraphicsStateGuardian) gsg = 
-    new glxGraphicsStateGuardian(new_properties, context, fbconfig,
+    new glxGraphicsStateGuardian(new_properties, NULL, context, fbconfig,
                                  _display, _screen);
   return gsg.p();
 }

+ 4 - 0
panda/src/glxdisplay/glxGraphicsStateGuardian.cxx

@@ -28,6 +28,7 @@ TypeHandle glxGraphicsStateGuardian::_type_handle;
 ////////////////////////////////////////////////////////////////////
 glxGraphicsStateGuardian::
 glxGraphicsStateGuardian(const FrameBufferProperties &properties,
+                         glxGraphicsStateGuardian *share_with,
                          GLXContext context, GLXFBConfig fbconfig,
                          Display *display, int screen) :
   GLGraphicsStateGuardian(properties),
@@ -36,6 +37,9 @@ glxGraphicsStateGuardian(const FrameBufferProperties &properties,
   _display(display),
   _screen(screen)
 {
+  if (share_with != (glxGraphicsStateGuardian *)NULL) {
+    _prepared_objects = share_with->get_prepared_objects();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/glxdisplay/glxGraphicsStateGuardian.h

@@ -32,6 +32,7 @@
 class glxGraphicsStateGuardian : public GLGraphicsStateGuardian {
 public:
   glxGraphicsStateGuardian(const FrameBufferProperties &properties,
+                           glxGraphicsStateGuardian *share_with,
                            GLXContext context, GLXFBConfig fbconfig,
                            Display *display, int screen);
   virtual ~glxGraphicsStateGuardian();

+ 8 - 3
panda/src/gobj/Sources.pp

@@ -4,7 +4,7 @@
 #begin lib_target
   #define TARGET gobj
   #define LOCAL_LIBS \
-    event linmath mathutil pnmimage gsgbase putil
+    pstatclient event linmath mathutil pnmimage gsgbase putil
 
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx 
 
@@ -19,7 +19,9 @@
     matrixLens.I matrixLens.h \
     orthographicLens.I orthographicLens.h perspectiveLens.I  \
     perspectiveLens.h pixelBuffer.I pixelBuffer.N  \
-    pixelBuffer.h lens.h lens.I \
+    pixelBuffer.h \
+    preparedGraphicsObjects.I preparedGraphicsObjects.h \
+    lens.h lens.I \
     savedContext.I savedContext.h \
     texture.I texture.N texture.h \
     textureContext.I textureContext.h \
@@ -33,7 +35,9 @@
     geomQuad.cxx geomSphere.cxx geomSprite.cxx geomTri.cxx  \
     geomTrifan.cxx geomTristrip.cxx imageBuffer.cxx material.cxx  \
     materialPool.cxx matrixLens.cxx orthographicLens.cxx  \
-    perspectiveLens.cxx pixelBuffer.cxx lens.cxx  \
+    perspectiveLens.cxx pixelBuffer.cxx \
+    preparedGraphicsObjects.cxx \
+    lens.cxx  \
     savedContext.cxx texture.cxx textureContext.cxx texturePool.cxx
 
   #define INSTALL_HEADERS \
@@ -47,6 +51,7 @@
     materialPool.I materialPool.h matrixLens.I matrixLens.h \
     orthographicLens.I orthographicLens.h perspectiveLens.I \
     perspectiveLens.h pixelBuffer.I pixelBuffer.h \
+    preparedGraphicsObjects.I preparedGraphicsObjects.h \
     lens.h lens.I \
     savedContext.I savedContext.h \
     texture.I texture.h \

+ 10 - 3
panda/src/gobj/geom.cxx

@@ -138,8 +138,10 @@ ostream &operator << (ostream &out, GeomAttrType t) {
 ////////////////////////////////////////////////////////////////////
 Geom::
 Geom(void) : dDrawable() {
+  /*
   _prepared_gsg = (GraphicsStateGuardianBase *)NULL;
   _prepared_context = (GeomContext *)NULL;
+  */
   init();
 }
 
@@ -150,8 +152,10 @@ Geom(void) : dDrawable() {
 ////////////////////////////////////////////////////////////////////
 Geom::
 Geom(const Geom& copy) : dDrawable() {
+  /*
   _prepared_gsg = (GraphicsStateGuardianBase *)NULL;
   _prepared_context = (GeomContext *)NULL;
+  */
   *this = copy;
 }
 
@@ -162,7 +166,7 @@ Geom(const Geom& copy) : dDrawable() {
 ////////////////////////////////////////////////////////////////////
 Geom::
 ~Geom() {
-  unprepare();
+  //  unprepare();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -463,9 +467,10 @@ draw(GraphicsStateGuardianBase *gsg) {
   if (is_dirty()) {
     config(); 
   }
+  /*
   if (_prepared_gsg == gsg) {
     draw_immediate(gsg, _prepared_context);
-  } else {
+    } else */ {
     draw_immediate(gsg, (GeomContext *)NULL);
   }
 }
@@ -513,7 +518,7 @@ config() {
   }
 
   // Mark the Geom as needing to be prepared again.
-  unprepare();
+  //  unprepare();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -547,6 +552,7 @@ output(ostream &out) const {
   */
 }
 
+/*
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::prepare
 //       Access: Public
@@ -620,6 +626,7 @@ clear_gsg(GraphicsStateGuardianBase *gsg) {
     _prepared_context = (GeomContext *)NULL;
   }
 }
+*/
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::init

+ 4 - 0
panda/src/gobj/geom.h

@@ -224,10 +224,12 @@ public:
   INLINE ColorIterator make_color_iterator() const;
   INLINE const Colorf &get_next_color(ColorIterator &citerator) const;
 
+  /*
   GeomContext *prepare(GraphicsStateGuardianBase *gsg);
   void unprepare();
   void unprepare(GraphicsStateGuardianBase *gsg);
   void clear_gsg(GraphicsStateGuardianBase *gsg);
+  */
 
 protected:
   void init();
@@ -261,8 +263,10 @@ protected:
   // intended to reduce memory overhead that would otherwise be
   // required to support a little-used feature (having two
   // simultaneous GSG's).
+  /*
   GraphicsStateGuardianBase *_prepared_gsg;
   GeomContext *_prepared_context;
+  */
 
 public:
   //static void register_with_read_factory(void);

+ 1 - 0
panda/src/gobj/gobj_composite2.cxx

@@ -9,6 +9,7 @@
 #include "matrixLens.cxx"
 #include "perspectiveLens.cxx"
 #include "pixelBuffer.cxx"
+#include "preparedGraphicsObjects.cxx"
 #include "lens.cxx"
 #include "savedContext.cxx"
 #include "texture.cxx"

+ 0 - 4
panda/src/gobj/imageBuffer.h

@@ -43,10 +43,6 @@ PUBLISHED:
 public:
   virtual void config( void ) { WritableConfigurable::config(); }
 
-  virtual bool copy(GraphicsStateGuardianBase *, const DisplayRegion *)=0;
-  virtual bool copy(GraphicsStateGuardianBase *, const DisplayRegion *,
-                    const RenderBuffer &rb)=0;
-
 PUBLISHED:
   INLINE bool has_filename() const;
   INLINE const Filename &get_filename() const;

+ 0 - 10
panda/src/gobj/pixelBuffer.cxx

@@ -421,13 +421,3 @@ copy(const PixelBuffer *pb) {
   if (!pb->_image.empty())
     _image.v() = pb->_image.v();
 }
-
-bool PixelBuffer::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) {
-  return gsg->copy_pixel_buffer(this, dr);
-}
-
-bool PixelBuffer::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr,
-                       const RenderBuffer &rb) {
-  return gsg->copy_pixel_buffer(this, dr, rb);
-}
-

+ 0 - 4
panda/src/gobj/pixelBuffer.h

@@ -105,10 +105,6 @@ public:
   bool store( PNMImage& pnmimage ) const;
 
   void copy(const PixelBuffer *pb);
-  
-  virtual bool copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr);
-  virtual bool copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr,
-                    const RenderBuffer &rb);
 
   INLINE void set_xsize(int size);
   INLINE void set_ysize(int size);

+ 18 - 0
panda/src/gobj/preparedGraphicsObjects.I

@@ -0,0 +1,18 @@
+// Filename: preparedGraphicsObjects.I
+// Created by:  drose (23Feb04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+

+ 243 - 0
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -0,0 +1,243 @@
+// Filename: preparedGraphicsObjects.cxx
+// Created by:  drose (19Feb04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "preparedGraphicsObjects.h"
+#include "textureContext.h"
+#include "mutexHolder.h"
+
+#ifndef CPPPARSER
+PStatCollector PreparedGraphicsObjects::_total_texusage_pcollector("Texture usage");
+#endif  // CPPPARSER
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PreparedGraphicsObjects::
+PreparedGraphicsObjects() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+PreparedGraphicsObjects::
+~PreparedGraphicsObjects() {
+  // There may be texture objects that are still prepared when we
+  // destruct.  If this is so, then all of the GSG's that own them
+  // have already destructed, so we can assume their resources were
+  // internally cleaned up.  Quietly erase these remaining textures.
+
+  MutexHolder holder(_lock);
+
+  Textures::iterator tci;
+  for (tci = _prepared_textures.begin();
+       tci != _prepared_textures.end();
+       ++tci) {
+    TextureContext *tc = (*tci);
+    _total_texusage_pcollector.sub_level(tc->estimate_texture_memory());
+    tc->_texture->clear_prepared(this);
+  }
+
+  _prepared_textures.clear();
+  _released_textures.clear();
+  _enqueued_textures.clear();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::enqueue_texture
+//       Access: Public
+//  Description: Indicates that a texture would like to be put on the
+//               list to be prepared when the GSG is next ready to
+//               do this (presumably at the next frame).
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+enqueue_texture(Texture *tex) {
+  MutexHolder holder(_lock);
+
+  _enqueued_textures.insert(tex);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::dequeue_texture
+//       Access: Public
+//  Description: Removes a texture from the queued list of textures to
+//               be prepared.  Normally it is not necessary to call
+//               this, unless you change your mind about preparing it
+//               at the last minute, since the texture will
+//               automatically be dequeued and prepared at the next
+//               frame.
+//
+//               The return value is true if the texture is
+//               successfully dequeued, false if it had not been
+//               queued.
+////////////////////////////////////////////////////////////////////
+bool PreparedGraphicsObjects::
+dequeue_texture(Texture *tex) {
+  MutexHolder holder(_lock);
+
+  EnqueuedTextures::iterator qi = _enqueued_textures.find(tex);
+  if (qi != _enqueued_textures.end()) {
+    _enqueued_textures.erase(qi);
+    return true;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::release_texture
+//       Access: Public
+//  Description: Indicates that a texture context, created by a
+//               previous call to prepare_texture(), is no longer
+//               needed.  The driver resources will not be freed until
+//               some GSG calls update(), indicating it is at a
+//               stage where it is ready to release textures--this
+//               prevents conflicts from threading or multiple GSG's
+//               sharing textures (we have no way of knowing which
+//               graphics context is currently active, or what state
+//               it's in, at the time release_texture is called).
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+release_texture(TextureContext *tc) {
+  MutexHolder holder(_lock);
+
+  tc->_texture->clear_prepared(this);
+  _total_texusage_pcollector.sub_level(tc->estimate_texture_memory());
+
+  // We have to set the Texture pointer to NULL at this point, since
+  // the Texture itself might destruct at any time after it has been
+  // released.
+  tc->_texture = (Texture *)NULL;
+
+  bool removed = (_prepared_textures.erase(tc) != 0);
+  nassertv(removed);
+
+  _released_textures.insert(tc);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::release_all_textures
+//       Access: Public
+//  Description: Releases all textures at once.  This will force them
+//               to be reloaded into texture memory for all GSG's that
+//               share this object.  Returns the number of textures
+//               released.
+////////////////////////////////////////////////////////////////////
+int PreparedGraphicsObjects::
+release_all_textures() {
+  MutexHolder holder(_lock);
+
+  int num_textures = (int)_prepared_textures.size();
+
+  Textures::iterator tci;
+  for (tci = _prepared_textures.begin();
+       tci != _prepared_textures.end();
+       ++tci) {
+    TextureContext *tc = (*tci);
+    tc->_texture->clear_prepared(this);
+    _total_texusage_pcollector.sub_level(tc->estimate_texture_memory());
+    tc->_texture = (Texture *)NULL;
+
+    _released_textures.insert(tc);
+  }
+
+  _prepared_textures.clear();
+
+  return num_textures;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::prepare_texture_now
+//       Access: Public
+//  Description: Immediately creates a new TextureContext for the
+//               indicated texture and returns it.  This assumes that
+//               the GraphicsStateGuardian is the currently active
+//               rendering context and that it is ready to accept new
+//               textures.  If this is not necessarily the case, you
+//               should use enqueue_texture() instead.
+//
+//               Normally, this function is not called directly.  Call
+//               Texture::prepare_now() instead.
+//
+//               The TextureContext contains all of the pertinent
+//               information needed by the GSG to keep track of this
+//               one particular texture, and will exist as long as the
+//               texture is ready to be rendered.
+//
+//               When either the Texture or the
+//               PreparedGraphicsObjects object destructs, the
+//               TextureContext will be deleted.
+////////////////////////////////////////////////////////////////////
+TextureContext *PreparedGraphicsObjects::
+prepare_texture_now(Texture *tex, GraphicsStateGuardianBase *gsg) {
+  MutexHolder holder(_lock);
+
+  // Ask the GSG to create a brand new TextureContext.  There might
+  // be several GSG's sharing the same set of textures; if so, it
+  // doesn't matter which of them creates the context (since they're
+  // all shared anyway).
+  TextureContext *tc = gsg->prepare_texture(tex);
+
+  bool prepared = _prepared_textures.insert(tc).second;
+  nassertr(prepared, tc);
+
+  _total_texusage_pcollector.add_level(tc->estimate_texture_memory());
+  return tc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PreparedGraphicsObjects::update
+//       Access: Public
+//  Description: This is called by the GraphicsStateGuardian to
+//               indicate that it is in a state to load or release
+//               textures.
+//
+//               Any texture contexts that were previously passed to
+//               release_texture() are actually passed to the GSG to
+//               be freed at this point; textures that were previously
+//               passed to prepare_texture are actually loaded.
+////////////////////////////////////////////////////////////////////
+void PreparedGraphicsObjects::
+update(GraphicsStateGuardianBase *gsg) {
+  MutexHolder holder(_lock);
+
+  // First, release all the textures awaiting release.
+  Textures::iterator tci;
+  for (tci = _released_textures.begin();
+       tci != _released_textures.end();
+       ++tci) {
+    TextureContext *tc = (*tci);
+    gsg->release_texture(tc);
+  }
+
+  _released_textures.clear();
+
+  // Now prepare all the textures awaiting preparation.
+  EnqueuedTextures::iterator qi;
+  for (qi = _enqueued_textures.begin();
+       qi != _enqueued_textures.end();
+       ++qi) {
+    Texture *tex = (*qi);
+    tex->prepare_now(this, gsg);
+  }
+
+  _enqueued_textures.clear();
+}

+ 80 - 0
panda/src/gobj/preparedGraphicsObjects.h

@@ -0,0 +1,80 @@
+// Filename: preparedGraphicsObjects.h
+// Created by:  drose (19Feb04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PREPAREDGRAPHICSOBJECTS_H
+#define PREPAREDGRAPHICSOBJECTS_H
+
+#include "pandabase.h"
+#include "referenceCount.h"
+#include "texture.h"
+#include "pStatCollector.h"
+#include "pset.h"
+#include "pmutex.h"
+
+class TextureContext;
+class GeomContext;
+class GeomNodeContext;
+class GraphicsStateGuardianBase;
+
+////////////////////////////////////////////////////////////////////
+//       Class : PreparedGraphicsObjects
+// Description : A table of objects that are saved within the graphics
+//               context for reference by handle later.  Generally,
+//               this represents things like OpenGL texture objects or
+//               display lists.
+//
+//               This object simply records the pointers to the
+//               context objects created by the individual GSG's;
+//               these context objects will contain enough information
+//               to reference or release the actual object stored
+//               within the graphics context.
+//
+//               These tables may potentially be shared between
+//               related graphics contexts, hence their storage here
+//               in a separate object rather than as a part of the
+//               GraphicsStateGuardian.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PreparedGraphicsObjects : public ReferenceCount {
+public:
+  PreparedGraphicsObjects();
+  ~PreparedGraphicsObjects();
+
+  void enqueue_texture(Texture *tex);
+  bool dequeue_texture(Texture *tex);
+  void release_texture(TextureContext *tc);
+  int release_all_textures();
+
+  TextureContext *prepare_texture_now(Texture *tex, GraphicsStateGuardianBase *gsg);
+  void update(GraphicsStateGuardianBase *gsg);
+
+private:
+  typedef pset<TextureContext *> Textures;
+  typedef pset< PT(Texture) > EnqueuedTextures;
+
+  Mutex _lock;
+  Textures _prepared_textures, _released_textures;  
+  EnqueuedTextures _enqueued_textures;
+
+  static PStatCollector _total_texusage_pcollector;
+
+  friend class GraphicsStateGuardian;
+};
+
+#include "preparedGraphicsObjects.I"
+
+#endif

+ 0 - 10
panda/src/gobj/texture.I

@@ -150,14 +150,4 @@ get_keep_ram_image() const {
   return _keep_ram_image;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::apply
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-INLINE void Texture::
-apply(GraphicsStateGuardianBase *gsg) {
-  gsg->apply_texture(prepare(gsg));
-}
-
 

+ 107 - 81
panda/src/gobj/texture.cxx

@@ -26,6 +26,7 @@
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "string_utils.h"
+#include "preparedGraphicsObjects.h"
 
 #include <stddef.h>
 
@@ -126,7 +127,7 @@ consider_downgrade(PNMImage &pnmimage, int num_channels,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Constructor
+//     Function: Texture::Constructor
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -146,7 +147,7 @@ Texture() : ImageBuffer() {
 
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Constructor
+//     Function: Texture::Constructor
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -166,17 +167,17 @@ Texture(int xsize, int ysize, int components, int component_width, PixelBuffer::
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Destructor
+//     Function: Texture::Destructor
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
 Texture::
 ~Texture() {
-  unprepare();
+  release_all();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: read
+//     Function: Texture::read
 //       Access: Published
 //  Description: Reads the texture from the indicated filename.  If
 //               num_channels is not 0, it specifies the number of
@@ -215,7 +216,7 @@ read(const Filename &fullpath, int primary_file_num_channels) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: read
+//     Function: Texture::read
 //       Access: Published
 //  Description: Combine a 3-component image with a grayscale image
 //               to get a 4-component image
@@ -309,7 +310,7 @@ read(const Filename &fullpath, const Filename &alpha_fullpath,
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: write
+//     Function: Texture::write
 //       Access: Published
 //  Description: Writes the texture to the indicated filename.
 ////////////////////////////////////////////////////////////////////
@@ -330,7 +331,7 @@ write(const Filename &name) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: set_wrapu
+//     Function: Texture::set_wrapu
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -343,7 +344,7 @@ set_wrapu(Texture::WrapMode wrap) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: set_wrapv
+//     Function: Texture::set_wrapv
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -356,7 +357,7 @@ set_wrapv(Texture::WrapMode wrap) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: set_minfilter
+//     Function: Texture::set_minfilter
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -373,7 +374,7 @@ set_minfilter(Texture::FilterType filter) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: set_magfilter
+//     Function: Texture::set_magfilter
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -386,7 +387,7 @@ set_magfilter(Texture::FilterType filter) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: set_anisotropic_degree
+//     Function: Texture::set_anisotropic_degree
 //       Access: Published
 //  Description: Specifies the level of anisotropic filtering to apply
 //               to the texture.  Normally, this is 1, to indicate
@@ -403,7 +404,7 @@ set_anisotropic_degree(int anisotropic_degree) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: set_border_color
+//     Function: Texture::set_border_color
 //       Access: Published
 //  Description:
 ////////////////////////////////////////////////////////////////////
@@ -413,7 +414,24 @@ set_border_color(const Colorf &color) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: load
+//     Function: Texture::prepare
+//       Access: Published
+//  Description: Indicates that the texture should be enqueued to be
+//               prepared in the indicated prepared_objects at the
+//               beginning of the next frame.  This will ensure the
+//               texture is already loaded into texture memory if it
+//               is expected to be rendered soon.
+//
+//               Use this function instead of prepare_now() to preload
+//               textures from a user interface standpoint.
+////////////////////////////////////////////////////////////////////
+void Texture::
+prepare(PreparedGraphicsObjects *prepared_objects) {
+  prepared_objects->enqueue_texture(this);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::load
 //       Access: Public
 //  Description: Creates the texture from the already-read PNMImage.
 ////////////////////////////////////////////////////////////////////
@@ -428,7 +446,7 @@ load(const PNMImage &pnmimage) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: store
+//     Function: Texture::store
 //       Access: Public
 //  Description: Saves the texture to the indicated PNMImage, but does
 //               not write it to disk.
@@ -459,22 +477,32 @@ is_mipmap(FilterType type) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: prepare
+//     Function: Texture::prepare_now
 //       Access: Public
 //  Description: Creates a context for the texture on the particular
 //               GSG, if it does not already exist.  Returns the new
-//               (or old) TextureContext.
+//               (or old) TextureContext.  This assumes that the
+//               GraphicsStateGuardian is the currently active
+//               rendering context and that it is ready to accept new
+//               textures.  If this is not necessarily the case, you
+//               should use prepare_later() instead.
+//
+//               Normally, this is not called directly except by the
+//               GraphicsStateGuardian; a texture does not need to be
+//               explicitly prepared by the user before it may be
+//               rendered.
 ////////////////////////////////////////////////////////////////////
 TextureContext *Texture::
-prepare(GraphicsStateGuardianBase *gsg) {
+prepare_now(PreparedGraphicsObjects *prepared_objects, 
+            GraphicsStateGuardianBase *gsg) {
   Contexts::const_iterator ci;
-  ci = _contexts.find(gsg);
+  ci = _contexts.find(prepared_objects);
   if (ci != _contexts.end()) {
     return (*ci).second;
   }
 
-  TextureContext *tc = gsg->prepare_texture(this);
-  _contexts[gsg] = tc;
+  TextureContext *tc = prepared_objects->prepare_texture_now(this, gsg);
+  _contexts[prepared_objects] = tc;
 
   // Now that we have a new TextureContext with zero dirty flags, our
   // intersection of all dirty flags must be zero.  This doesn't mean
@@ -498,67 +526,54 @@ prepare(GraphicsStateGuardianBase *gsg) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Texture::unprepare
+//     Function: Texture::release
 //       Access: Public
-//  Description: Frees the context allocated on all GSG's for which
-//               the texture has been declared.
+//  Description: Frees the texture context only on the indicated object,
+//               if it exists there.  Returns true if it was released,
+//               false if it had not been prepared.
 ////////////////////////////////////////////////////////////////////
-void Texture::
-unprepare() {
-  // We have to traverse a copy of the _contexts list, because the GSG
-  // will call clear_gsg() in response to each release_texture(), and
-  // we don't want to be modifying the _contexts list while we're
-  // traversing it.
-  Contexts temp = _contexts;
-
-  Contexts::const_iterator ci;
-  for (ci = temp.begin(); ci != temp.end(); ++ci) {
-    GraphicsStateGuardianBase *gsg = (*ci).first;
+bool Texture::
+release(PreparedGraphicsObjects *prepared_objects) {
+  Contexts::iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
     TextureContext *tc = (*ci).second;
-    gsg->release_texture(tc);
+    prepared_objects->release_texture(tc);
+    return true;
   }
 
-  // Now that we've called release_texture() on every known GSG, the
-  // _contexts list should have completely emptied itself.
-  nassertv(_contexts.empty());
+  // Maybe it wasn't prepared yet, but it's about to be.
+  return prepared_objects->dequeue_texture(this);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Texture::unprepare
+//     Function: Texture::release_all
 //       Access: Public
-//  Description: Frees the texture context only on the indicated GSG,
-//               if it exists there.
-////////////////////////////////////////////////////////////////////
-void Texture::
-unprepare(GraphicsStateGuardianBase *gsg) {
-  Contexts::iterator ci;
-  ci = _contexts.find(gsg);
-  if (ci != _contexts.end()) {
+//  Description: Frees the context allocated on all objects for which
+//               the texture has been declared.  Returns the number of
+//               contexts which have been freed.
+////////////////////////////////////////////////////////////////////
+int Texture::
+release_all() {
+  // We have to traverse a copy of the _contexts list, because the
+  // PreparedGraphicsObjects object will call clear_prepared() in response
+  // to each release_texture(), and we don't want to be modifying the
+  // _contexts list while we're traversing it.
+  Contexts temp = _contexts;
+  int num_freed = (int)_contexts.size();
+
+  Contexts::const_iterator ci;
+  for (ci = temp.begin(); ci != temp.end(); ++ci) {
+    PreparedGraphicsObjects *prepared_objects = (*ci).first;
     TextureContext *tc = (*ci).second;
-    gsg->release_texture(tc);
+    prepared_objects->release_texture(tc);
   }
-}
 
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::clear_gsg
-//       Access: Public
-//  Description: Removes the indicated GSG from the Texture's known
-//               GSG's, without actually releasing the texture on that
-//               GSG.  This is intended to be called only from
-//               GSG::release_texture(); it should never be called by
-//               user code.
-////////////////////////////////////////////////////////////////////
-void Texture::
-clear_gsg(GraphicsStateGuardianBase *gsg) {
-  Contexts::iterator ci;
-  ci = _contexts.find(gsg);
-  if (ci != _contexts.end()) {
-    _contexts.erase(ci);
-  } else {
-    // If this assertion fails, clear_gsg() was called on a GSG which
-    // the texture didn't know about.
-    nassertv(false);
-  }
+  // Now that we've called release_texture() on every known context,
+  // the _contexts list should have completely emptied itself.
+  nassertr(_contexts.empty(), num_freed);
+
+  return num_freed;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -606,17 +621,6 @@ get_ram_image() {
   }
 }
 
-bool Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) {
-  gsg->copy_texture(prepare(gsg), dr);
-  return true;
-}
-
-bool Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr,
-                   const RenderBuffer &rb) {
-  gsg->copy_texture(prepare(gsg), dr, rb);
-  return true;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::mark_dirty
 //       Access: Public
@@ -650,6 +654,28 @@ mark_dirty(int flags_to_set) {
   _all_dirty_flags |= flags_to_set;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::clear_prepared
+//       Access: Private
+//  Description: Removes the indicated PreparedGraphicsObjects table
+//               from the Texture's table, without actually releasing
+//               the texture.  This is intended to be called only from
+//               PreparedGraphicsObjects::release_texture(); it should
+//               never be called by user code.
+////////////////////////////////////////////////////////////////////
+void Texture::
+clear_prepared(PreparedGraphicsObjects *prepared_objects) {
+  Contexts::iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    _contexts.erase(ci);
+  } else {
+    // If this assertion fails, clear_prepared() was given a
+    // prepared_objects which the texture didn't know about.
+    nassertv(false);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::string_wrap_mode
 //       Access: Public

+ 16 - 15
panda/src/gobj/texture.h

@@ -28,6 +28,7 @@
 class PNMImage;
 class TextureContext;
 class FactoryParams;
+class PreparedGraphicsObjects;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Texture
@@ -98,28 +99,24 @@ PUBLISHED:
   INLINE int get_anisotropic_degree() const;
   INLINE bool uses_mipmaps() const;
 
+  void prepare(PreparedGraphicsObjects *prepared_objects);
+
 public:
   bool load(const PNMImage &pnmimage);
   bool store(PNMImage &pnmimage) const;
 
   static bool is_mipmap(FilterType type);
 
-  TextureContext *prepare(GraphicsStateGuardianBase *gsg);
-  void unprepare();
-  void unprepare(GraphicsStateGuardianBase *gsg);
-  void clear_gsg(GraphicsStateGuardianBase *gsg);
+  TextureContext *prepare_now(PreparedGraphicsObjects *prepared_objects, 
+                              GraphicsStateGuardianBase *gsg);
+  bool release(PreparedGraphicsObjects *prepared_objects);
+  int release_all();
 
   INLINE bool has_ram_image() const;
   PixelBuffer *get_ram_image();
   INLINE void set_keep_ram_image(bool keep_ram_image);
   INLINE bool get_keep_ram_image() const;
 
-  INLINE void apply(GraphicsStateGuardianBase *gsg);
-
-  virtual bool copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr);
-  virtual bool copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr,
-                    const RenderBuffer &rb);
-
   // These bits are used as parameters to Texture::mark_dirty() and
   // also TextureContext::mark_dirty() (and related functions in
   // TextureContext).
@@ -136,6 +133,8 @@ public:
   static FilterType string_filter_type(const string &string);
 
 private:
+  void clear_prepared(PreparedGraphicsObjects *prepared_objects);
+
   WrapMode _wrapu;
   WrapMode _wrapv;
   FilterType _minfilter;
@@ -144,11 +143,12 @@ private:
   bool _keep_ram_image;
   Colorf _border_color;
 
-  // A Texture keeps a list (actually, a map) of all the GSG's that it
-  // has been prepared into.  Each GSG conversely keeps a list (a set)
-  // of all the Textures that have been prepared there.  When either
-  // destructs, it removes itself from the other's list.
-  typedef pmap<GraphicsStateGuardianBase *, TextureContext *> Contexts;
+  // A Texture keeps a list (actually, a map) of all the
+  // PreparedGraphicsObjects tables that it has been prepared into.
+  // Each PGO conversely keeps a list (a set) of all the Textures that
+  // have been prepared there.  When either destructs, it removes
+  // itself from the other's list.
+  typedef pmap<PreparedGraphicsObjects *, TextureContext *> Contexts;
   Contexts _contexts;
 
   // This value represents the intersection of all the dirty flags of
@@ -201,6 +201,7 @@ private:
   static TypeHandle _type_handle;
 
   friend class TextureContext;
+  friend class PreparedGraphicsObjects;
 };
 
 #include "texture.I"

+ 3 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -44,6 +44,7 @@ class GeomTristrip;
 class GeomTrifan;
 class GeomSphere;
 
+class PreparedGraphicsObjects;
 class TextureContext;
 class Texture;
 class PixelBuffer;
@@ -111,6 +112,8 @@ public:
   // These are some general interface functions; they're defined here
   // mainly to make it easy to call these from code in some directory
   // that display depends on.
+  virtual PreparedGraphicsObjects *get_prepared_objects()=0;
+
   virtual TextureContext *prepare_texture(Texture *tex)=0;
   virtual void apply_texture(TextureContext *tc)=0;
   virtual void release_texture(TextureContext *tc)=0;

+ 12 - 12
panda/src/pgraph/nodePath.cxx

@@ -47,6 +47,7 @@
 #include "globPattern.h"
 #include "config_gobj.h"
 #include "bamFile.h"
+#include "preparedGraphicsObjects.h"
 #include "dcast.h"
 
 // stack seems to overflow on Intel C++ at 7000.  If we need more than 
@@ -3246,19 +3247,18 @@ verify_complete() const {
 //               but this may take some of the overhead away from that
 //               process.
 //
-//               If force_retained_mode is true, retained mode is set
-//               on the geometry encountered, regardless of the
-//               setting of the retained-mode Config variable.
-//               Otherwise, retained mode is set only if the
-//               retained-mode Config variable is true.
+//               In particular, this will ensure that textures within
+//               the scene are loaded in texture memory, and display
+//               lists are built up from static geometry.
 ////////////////////////////////////////////////////////////////////
 void NodePath::
-prepare_scene(GraphicsStateGuardianBase *gsg, bool force_retained_mode) {
+prepare_scene(GraphicsStateGuardianBase *gsg) {
   nassertv_always(!is_empty());
 
+  PreparedGraphicsObjects *prepared_objects = gsg->get_prepared_objects();
+
   CPT(RenderState) net_state = get_net_state();
-  r_prepare_scene(node(), net_state, gsg, 
-                  retained_mode || force_retained_mode);
+  r_prepare_scene(node(), net_state, prepared_objects);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3895,14 +3895,14 @@ r_find_all_textures(PandaNode *node, const RenderState *state,
 ////////////////////////////////////////////////////////////////////
 void NodePath::
 r_prepare_scene(PandaNode *node, const RenderState *state,
-                GraphicsStateGuardianBase *gsg, bool do_retained_mode) {
+                PreparedGraphicsObjects *prepared_objects) {
   if (node->is_geom_node()) {
     GeomNode *gnode;
     DCAST_INTO_V(gnode, node);
 
     /* 
        Not implemented yet in pgraph.  Maybe we don't need this anyway.
-    if (do_retained_mode) {
+    if (retained_mode) {
       gnode->prepare(gsg);
     }
     */
@@ -3917,7 +3917,7 @@ r_prepare_scene(PandaNode *node, const RenderState *state,
         DCAST_INTO_V(ta, attrib);
         Texture *texture = ta->get_texture();
         if (texture != (Texture *)NULL) {
-          texture->prepare(gsg);
+          texture->prepare(prepared_objects);
         }
       }
     }
@@ -3927,6 +3927,6 @@ r_prepare_scene(PandaNode *node, const RenderState *state,
   for (int i = 0; i < num_children; i++) {
     PandaNode *child = node->get_child(i);
     CPT(RenderState) child_state = state->compose(child->get_state());
-    r_prepare_scene(child, child_state, gsg, do_retained_mode);
+    r_prepare_scene(child, child_state, prepared_objects);
   }
 }

+ 3 - 2
panda/src/pgraph/nodePath.h

@@ -39,6 +39,7 @@ class Texture;
 class Material;
 class Fog;
 class GlobPattern;
+class PreparedGraphicsObjects;
 
 //
 // A NodePath is the fundamental unit of high-level interaction with
@@ -577,7 +578,7 @@ PUBLISHED:
   // Miscellaneous
   bool verify_complete() const;
 
-  void prepare_scene(GraphicsStateGuardianBase *gsg, bool force_retained_mode = false);
+  void prepare_scene(GraphicsStateGuardianBase *gsg);
 
   void show_bounds();
   void hide_bounds();
@@ -635,7 +636,7 @@ private:
   void r_find_all_textures(PandaNode *node, const RenderState *state,
                            Textures &textures) const;
   void r_prepare_scene(PandaNode *node, const RenderState *state,
-                       GraphicsStateGuardianBase *gsg, bool do_retained_mode);
+                       PreparedGraphicsObjects *prepared_objects);
 
   PT(NodePathComponent) _head;
   int _backup_key;

+ 3 - 1
panda/src/pgraph/spotlight.cxx

@@ -227,7 +227,9 @@ make_image(Texture *texture, float radius) {
       pb->set_uchar_rgb_texel(C, size - tx2 - 1, size - ty2 - 1, size);
     }
   }
-  texture->unprepare();
+
+  // Force the texture to be reloaded into texture memory.
+  texture->mark_dirty(Texture::DF_image);
 
   return true;
 }