Переглянути джерело

add retained mode rendering infrastructure

David Rose 24 роки тому
батько
коміт
e14e69dce0

+ 4 - 0
panda/src/display/config_display.cxx

@@ -27,6 +27,8 @@
 #include "graphicsChannel.h"
 #include "hardwareChannel.h"
 #include "textureContext.h"
+#include "geomNodeContext.h"
+#include "geomContext.h"
 
 Configure(config_display);
 NotifyCategoryDef(display, "");
@@ -130,6 +132,8 @@ init_libdisplay() {
   GraphicsChannel::init_type();
   HardwareChannel::init_type();
   TextureContext::init_type();
+  GeomNodeContext::init_type();
+  GeomContext::init_type();
 
   disp = new Config::ConfigTable::Symbol;
   guard = new Config::ConfigTable::Symbol;

+ 35 - 9
panda/src/display/graphicsStateGuardian.cxx

@@ -414,6 +414,24 @@ release_all_textures() {
 ////////////////////////////////////////////////////////////////////
 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());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -723,9 +741,11 @@ unmark_prepared_geom_node(GeomNodeContext *gnc) {
 void GraphicsStateGuardian::
 init_frame_pstats() {
   _current_textures.clear();
+  _current_geoms.clear();
+  _current_geom_nodes.clear();
   _active_texusage_pcollector.clear_level();
-  _total_geom_pcollector.clear_level();
-  _total_geom_node_pcollector.clear_level();
+  _active_geom_pcollector.clear_level();
+  _active_geom_node_pcollector.clear_level();
 
   // Also clear out our other counters while we're here.
   _vertices_tristrip_pcollector.clear_level();
@@ -752,8 +772,10 @@ init_frame_pstats() {
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
 add_to_texture_record(TextureContext *tc) {
-  if (_current_textures.insert(tc).second) {
-    _active_texusage_pcollector.add_level(tc->estimate_texture_memory());
+  if (PStatClient::is_connected()) {
+    if (_current_textures.insert(tc).second) {
+      _active_texusage_pcollector.add_level(tc->estimate_texture_memory());
+    }
   }
 }
 
@@ -767,8 +789,10 @@ add_to_texture_record(TextureContext *tc) {
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
 add_to_geom_record(GeomContext *gc) {
-  if (gc != (GeomContext *)NULL && _current_geoms.insert(gc).second) {
-    _active_geom_pcollector.add_level(1);
+  if (PStatClient::is_connected()) {
+    if (gc != (GeomContext *)NULL && _current_geoms.insert(gc).second) {
+      _active_geom_pcollector.add_level(1);
+    }
   }
 }
 
@@ -782,9 +806,11 @@ add_to_geom_record(GeomContext *gc) {
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
 add_to_geom_node_record(GeomNodeContext *gnc) {
-  if (gnc != (GeomNodeContext *)NULL && 
-      _current_geom_nodes.insert(gnc).second) {
-    _active_geom_node_pcollector.add_level(1);
+  if (PStatClient::is_connected()) {
+    if (gnc != (GeomNodeContext *)NULL && 
+        _current_geom_nodes.insert(gnc).second) {
+      _active_geom_node_pcollector.add_level(1);
+    }
   }
 }
 

+ 12 - 0
panda/src/gobj/config_gobj.cxx

@@ -75,6 +75,18 @@ bool textures_down_square = false;
 // wastefully.
 bool keep_texture_ram = config_gobj.GetBool("keep-texture-ram", false);
 
+// Ditto for Geom's.  This is a little more dangerous, because if
+// anyone calls release_all_geoms() on the GSG, we won't be able to
+// restore them automatically.
+bool keep_geom_ram = config_gobj.GetBool("keep-geom-ram", false);
+
+// Set this true to allow the use of retained mode rendering, which
+// creates specific cache information (like display lists or vertex
+// buffers) with the GSG for static geometry, when supported by the
+// GSG.  Set it false to use only immediate mode, which sends the
+// vertices to the GSG every frame.
+bool retained_mode = config_gobj.GetBool("retained-mode", true);
+
 
 // Set this to specify how textures should be written into Bam files.
 // Currently, the options are:

+ 2 - 0
panda/src/gobj/config_gobj.h

@@ -31,6 +31,8 @@ extern EXPCL_PANDA bool textures_down_power_2;
 extern EXPCL_PANDA bool textures_up_square;
 extern EXPCL_PANDA bool textures_down_square;
 extern EXPCL_PANDA bool keep_texture_ram;
+extern EXPCL_PANDA bool keep_geom_ram;
+extern EXPCL_PANDA bool retained_mode;
 
 enum BamTextureMode {
   BTM_fullpath,

+ 13 - 0
panda/src/gobj/drawable.cxx

@@ -52,6 +52,19 @@ draw(GraphicsStateGuardianBase *) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Drawable::draw
+//       Access: Public, Virtual
+//  Description: Returns true if the Drawable has any dynamic
+//               properties that are expected to change from one frame
+//               to the next, or false if the Drawable is largely
+//               static.
+////////////////////////////////////////////////////////////////////
+bool dDrawable::
+is_dynamic() const {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Drawable::propagate_stale_bound
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/gobj/drawable.h

@@ -53,6 +53,7 @@ public:
   virtual ~dDrawable();
 
   virtual void draw(GraphicsStateGuardianBase *);
+  virtual bool is_dynamic() const;
 
 protected:
   virtual void propagate_stale_bound();

+ 14 - 0
panda/src/gobj/geom.cxx

@@ -324,6 +324,20 @@ get_texcoords(PTA_TexCoordf &texcoords, GeomBindType &bind,
   tindex = _tindex;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::is_dynamic
+//       Access: Public, Virtual
+//  Description: Returns true if the Geom has any dynamic properties
+//               that are expected to change from one frame to the
+//               next, or false if the Geom is largely static.  For
+//               now, this is the same thing as asking whether its
+//               vertices are indexed.
+////////////////////////////////////////////////////////////////////
+bool Geom::
+is_dynamic() const {
+  return (_vindex != (ushort*)0L);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::get_num_vertices
 //       Access: Public

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

@@ -171,6 +171,8 @@ public:
                      GeomBindType &bind,
                      PTA_ushort &tindex) const;
 
+  virtual bool is_dynamic() const;
+
 PUBLISHED:
   INLINE GeomBindType get_binding(int attr) const;
   INLINE const PTA_Vertexf &get_coords_array() const;

+ 2 - 2
panda/src/graph/dftraverser.h

@@ -42,8 +42,8 @@ public:
   typedef TYPENAME Visitor::AttributeWrapper AttributeWrapper;
 
   INLINE_GRAPH DFTraverser(Visitor &visitor,
-                     const AttributeWrapper &initial_render_state,
-                     TypeHandle graph_type);
+                           const AttributeWrapper &initial_render_state,
+                           TypeHandle graph_type);
 
   INLINE_GRAPH void start(NodeRelation *arc, const LevelState &initial_level_state);
   INLINE_GRAPH void start(Node *root, const LevelState &initial_level_state);

+ 6 - 6
panda/src/graph/traverserVisitor.h

@@ -31,19 +31,19 @@ public:
   typedef TYPENAME TransitionWrapper::AttributeWrapper AttributeWrapper;
 
   INLINE_GRAPH bool reached_node(Node *node,
-                           AttributeWrapper &render_state,
-                           LevelState &level_state);
+                                 AttributeWrapper &render_state,
+                                 LevelState &level_state);
 
   // Some traversers (notably DFTraverser) will also call the
   // following two functions to mark the crossing of arcs.  This will
   // allow the Visitor to maintain its own internal state as needed.
 
   INLINE_GRAPH bool forward_arc(NodeRelation *arc, TransitionWrapper &trans,
-                          AttributeWrapper &pre, AttributeWrapper &post,
-                          LevelState &level_state);
+                                AttributeWrapper &pre, AttributeWrapper &post,
+                                LevelState &level_state);
   INLINE_GRAPH void backward_arc(NodeRelation *arc, TransitionWrapper &trans,
-                           AttributeWrapper &pre, AttributeWrapper &post,
-                           const LevelState &level_state);
+                                 AttributeWrapper &pre, AttributeWrapper &post,
+                                 const LevelState &level_state);
 };
 
 #include "traverserVisitor.T"

+ 16 - 6
panda/src/pstatclient/pStatClient.cxx

@@ -581,8 +581,7 @@ stop(int collector_index, int thread_index, float as_of) {
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 clear_level(int collector_index, int thread_index) {
-  if (_collectors[collector_index]._def->_is_active &&
-      _threads[thread_index]._is_active) {
+  if (_collectors[collector_index]._def->_is_active) {
     _collectors[collector_index]._per_thread[thread_index]._has_level = false;
     _collectors[collector_index]._per_thread[thread_index]._level = 0.0;
   }
@@ -599,8 +598,7 @@ clear_level(int collector_index, int thread_index) {
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 set_level(int collector_index, int thread_index, float level) {
-  if (_collectors[collector_index]._def->_is_active &&
-      _threads[thread_index]._is_active) {
+  if (_collectors[collector_index]._def->_is_active) {
     level *= _collectors[collector_index]._def->_factor;
     _collectors[collector_index]._per_thread[thread_index]._has_level = true;
     _collectors[collector_index]._per_thread[thread_index]._level = level;
@@ -620,14 +618,26 @@ set_level(int collector_index, int thread_index, float level) {
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 add_level(int collector_index, int thread_index, float increment) {
-  if (_collectors[collector_index]._def->_is_active &&
-      _threads[thread_index]._is_active) {
+  if (_collectors[collector_index]._def->_is_active) {
     increment *= _collectors[collector_index]._def->_factor;
     _collectors[collector_index]._per_thread[thread_index]._has_level = true;
     _collectors[collector_index]._per_thread[thread_index]._level += increment;
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_level
+//       Access: Private
+//  Description: Returns the current level value of the given collector.
+//
+//               Normally you would not use this interface directly;
+//               instead, call PStatCollector::get_level().
+////////////////////////////////////////////////////////////////////
+float PStatClient::
+get_level(int collector_index, int thread_index) const {
+  return _collectors[collector_index]._per_thread[thread_index]._level;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::new_frame
 //       Access: Private

+ 1 - 0
panda/src/pstatclient/pStatClient.h

@@ -106,6 +106,7 @@ private:
   void clear_level(int collector_index, int thread_index);
   void set_level(int collector_index, int thread_index, float level);
   void add_level(int collector_index, int thread_index, float increment);
+  float get_level(int collector_index, int thread_index) const;
 
   void new_frame(int thread_index);
   void transmit_frame_data(int thread_index);

+ 20 - 0
panda/src/pstatclient/pStatCollector.I

@@ -212,6 +212,16 @@ sub_level(float decrement) {
   _client->add_level(_index, 0, -decrement);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatCollector::get_level
+//       Access: Published
+//  Description: Returns the current level value of the given collector.
+////////////////////////////////////////////////////////////////////
+INLINE float PStatCollector::
+get_level() {
+  return _client->get_level(_index, 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatCollector::is_active
 //       Access: Public
@@ -325,4 +335,14 @@ sub_level(const PStatThread &thread, float decrement) {
   _client->add_level(_index, thread._index, -decrement);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatCollector::get_level
+//       Access: Public
+//  Description: Returns the current level value of the given collector.
+////////////////////////////////////////////////////////////////////
+INLINE float PStatCollector::
+get_level(const PStatThread &thread) {
+  return _client->get_level(_index, thread._index);
+}
+
 #endif  // DO_PSTATS

+ 4 - 0
panda/src/pstatclient/pStatCollector.h

@@ -74,6 +74,7 @@ PUBLISHED:
   INLINE void set_level(float level);
   INLINE void add_level(float increment);
   INLINE void sub_level(float decrement);
+  INLINE float get_level();
 
 public:
   INLINE bool is_active(const PStatThread &thread);
@@ -86,6 +87,7 @@ public:
   INLINE void set_level(const PStatThread &thread, float level);
   INLINE void add_level(const PStatThread &thread, float increment);
   INLINE void sub_level(const PStatThread &thread, float decrement);
+  INLINE float get_level(const PStatThread &thread);
 
 private:
   PStatClient *_client;
@@ -108,6 +110,7 @@ PUBLISHED:
   INLINE void set_level(float) { }
   INLINE void add_level(float) { }
   INLINE void sub_level(float) { }
+  INLINE float get_level() { return 0.0; }
 
 public:
   INLINE bool is_active(const PStatThread &) { return false; }
@@ -120,6 +123,7 @@ public:
   INLINE void set_level(const PStatThread &, float) { }
   INLINE void add_level(const PStatThread &, float) { }
   INLINE void sub_level(const PStatThread &, float) { }
+  INLINE float get_level(const PStatThread &) { return 0.0; }
 
 #endif  // DO_PSTATS
 };

+ 20 - 2
panda/src/sgmanip/nodePath.cxx

@@ -40,6 +40,7 @@
 #include <bamFile.h>
 #include <materialPool.h>
 #include <pt_NodeRelation.h>
+#include <config_gobj.h>
 
 #include "plist.h"
 
@@ -47,7 +48,7 @@ TypeHandle NodePath::_type_handle;
 
 
 // This class is used in prepare_scene() to traverse the scene graph
-// and register textures with the gsg.
+// and register textures and geoms with the gsg.
 class ScenePrepareVisitor : public TraverserVisitor<NodeTransitionWrapper, NullLevelState> {
 public:
   bool forward_arc(NodeRelation *, NodeTransitionWrapper &trans,
@@ -62,7 +63,17 @@ public:
     return true;
   }
 
+  bool reached_node(Node *node, NodeAttributeWrapper &,
+                    NullLevelState &) {
+    if (_retained_mode && node->is_of_type(GeomNode::get_class_type())) {
+      GeomNode *gnode = DCAST(GeomNode, node);
+      gnode->prepare(_gsg);
+    }
+    return true;
+  }
+
   GraphicsStateGuardianBase *_gsg;
+  bool _retained_mode;
 };
 
 ////////////////////////////////////////////////////////////////////
@@ -2760,9 +2771,15 @@ get_stashed_ancestor() const {
 //               will initialize itself when the scene is rendered,
 //               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.
 ////////////////////////////////////////////////////////////////////
 void NodePath::
-prepare_scene(GraphicsStateGuardianBase *gsg) {
+prepare_scene(GraphicsStateGuardianBase *gsg, bool force_retained_mode) {
   nassertv_always(!is_empty());
 
   // Use the ScenePrepareVisitor and fire off a traversal of the scene
@@ -2771,6 +2788,7 @@ prepare_scene(GraphicsStateGuardianBase *gsg) {
   // graph at this point and below.
   ScenePrepareVisitor visitor;
   visitor._gsg = gsg;
+  visitor._retained_mode = retained_mode || force_retained_mode;
 
   NodeAttributeWrapper initial(TextureTransition::get_class_type());
   df_traverse(node(), visitor, initial, NullLevelState(),

+ 1 - 1
panda/src/sgmanip/nodePath.h

@@ -509,7 +509,7 @@ PUBLISHED:
   INLINE bool is_stashed() const;
   NodePath get_stashed_ancestor() const;
 
-  void prepare_scene(GraphicsStateGuardianBase *gsg);
+  void prepare_scene(GraphicsStateGuardianBase *gsg, bool force_retained_mode = false);
   INLINE void clear_wrt_cache();
 
   void show_bounds();

+ 22 - 0
panda/src/sgraph/geomNode.cxx

@@ -15,12 +15,15 @@
 // [email protected] .
 //
 ////////////////////////////////////////////////////////////////////
+
 #include "geomNode.h"
 #include "geomTransformer.h"
+#include "config_sgraph.h"
 
 #include <geom.h>
 #include <allTransitionsWrapper.h>
 #include <indent.h>
+#include <config_gobj.h>
 
 ////////////////////////////////////////////////////////////////////
 // Static variables
@@ -182,6 +185,25 @@ prepare(GraphicsStateGuardianBase *gsg) {
       unprepare();
       _prepared_context = gc;
       _prepared_gsg = gsg;
+
+      if (!keep_geom_ram) {
+        // Once we have prepared the GeomNode, we can generally safely
+        // remove the vertices from main RAM.  The GSG is now
+        // responsible for remembering what it looks like.
+        
+        if (sgraph_cat.is_debug()) {
+          sgraph_cat.debug()
+            << "Dumping RAM for " << *this << "\n";
+        }
+
+        // First, change our bounding volume to a static volume, so it
+        // won't try to recompute itself once we clear out the Geoms.
+        set_bound(get_bound());
+
+        // Now remove the set of Geoms, since we don't need them any
+        // more.
+        _geoms.clear();
+      }
     }
     return gc;
   }