diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index 3bf0c9e9b9..f7650d04c0 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -285,16 +285,6 @@ get_scene() const { return _scene_setup; } -//////////////////////////////////////////////////////////////////// -// Function: GraphicsStateGuardian::get_geom_munger -// Access: Public -// Description: Returns the current GeomMunger object. -//////////////////////////////////////////////////////////////////// -INLINE const qpGeomMunger *GraphicsStateGuardian:: -get_geom_munger() const { - return _geom_munger; -} - //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::clear // Access: Public @@ -383,7 +373,6 @@ modify_state(const RenderState *state) { _state_pcollector.add_level(1); _state = _state->issue_delta_modify(state, this); finish_modify_state(); - setup_geom_munger(NULL); } } @@ -411,7 +400,6 @@ set_state(const RenderState *state) { _state_pcollector.add_level(1); _state = _state->issue_delta_set(state, this); finish_modify_state(); - setup_geom_munger(NULL); } } diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index f0c6d5baf7..c459854a06 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -267,6 +267,21 @@ void GraphicsStateGuardian:: release_geom(GeomContext *) { } +//////////////////////////////////////////////////////////////////// +// Function: GraphicsStateGuardian::get_geom_munger +// Access: Public, Virtual +// Description: Creates a new GeomMunger object to munge vertices +// appropriate to this GSG for the indicated state. +//////////////////////////////////////////////////////////////////// +CPT(qpGeomMunger) GraphicsStateGuardian:: +get_geom_munger(const RenderState *) { + // The default implementation returns a munger that does nothing, + // but presumably, every kind of GSG needs some special munging + // action, so real GSG's will override this to return something more + // useful. + return qpGeomMunger::register_munger(new qpGeomMunger); +} + //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::set_state_and_transform // Access: Public, Virtual @@ -607,7 +622,7 @@ finish_decal() { //////////////////////////////////////////////////////////////////// bool GraphicsStateGuardian:: begin_draw_primitives(const qpGeomVertexData *data) { - _vertex_data = get_geom_munger()->munge_data(data); + _vertex_data = data; return true; } @@ -627,7 +642,10 @@ draw_triangles(qpGeomTriangles *) { //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_tristrips(qpGeomTristrips *primitive) { - primitive->decompose(_vertex_data)->draw(this); + PT(qpGeomPrimitive) new_prim = primitive->decompose(); + if (!new_prim->is_of_type(qpGeomTristrips::get_class_type())) { + new_prim->draw(this); + } } //////////////////////////////////////////////////////////////////// @@ -637,7 +655,10 @@ draw_tristrips(qpGeomTristrips *primitive) { //////////////////////////////////////////////////////////////////// void GraphicsStateGuardian:: draw_trifans(qpGeomTrifans *primitive) { - primitive->decompose(_vertex_data)->draw(this); + PT(qpGeomPrimitive) new_prim = primitive->decompose(); + if (!new_prim->is_of_type(qpGeomTrifans::get_class_type())) { + new_prim->draw(this); + } } //////////////////////////////////////////////////////////////////// @@ -1329,21 +1350,6 @@ finish_modify_state() { } } -//////////////////////////////////////////////////////////////////// -// Function: GraphicsStateGuardian::setup_geom_munger -// Access: Protected, Virtual -// Description: Called after finish_modify_state has completed, this -// method sets up the GeomMunger for rendering with the -// current state. -//////////////////////////////////////////////////////////////////// -void GraphicsStateGuardian:: -setup_geom_munger(PT(qpGeomMunger) munger) { - if (munger == (qpGeomMunger *)NULL) { - munger = new qpGeomMunger; - } - _geom_munger = qpGeomMunger::register_munger(munger); -} - //////////////////////////////////////////////////////////////////// // Function: GraphicsStateGuardian::free_pointers // Access: Protected, Virtual diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 7df94d3598..b8ce577c0e 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -97,7 +97,6 @@ PUBLISHED: public: INLINE bool set_scene(SceneSetup *scene_setup); INLINE SceneSetup *get_scene() const; - INLINE const qpGeomMunger *get_geom_munger() const; virtual PreparedGraphicsObjects *get_prepared_objects(); @@ -108,6 +107,8 @@ public: virtual GeomContext *prepare_geom(Geom *geom); virtual void release_geom(GeomContext *gc); + virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state); + virtual void set_state_and_transform(const RenderState *state, const TransformState *transform); @@ -209,7 +210,6 @@ protected: virtual void set_blend_mode(); virtual void finish_modify_state(); - virtual void setup_geom_munger(PT(qpGeomMunger) munger); virtual void free_pointers(); virtual void close_gsg(); @@ -242,7 +242,6 @@ protected: CPT(RenderState) _state; CPT(TransformState) _transform; - CPT(qpGeomMunger) _geom_munger; CPT(qpGeomVertexData) _vertex_data; int _buffer_mask; diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index c6243a83db..5a9c494b60 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -2893,6 +2893,18 @@ release_texture(TextureContext *tc) { delete gtc; } +//////////////////////////////////////////////////////////////////// +// Function: DXGraphicsStateGuardian8::get_geom_munger +// Access: Public, Virtual +// Description: Creates a new GeomMunger object to munge vertices +// appropriate to this GSG for the indicated state. +//////////////////////////////////////////////////////////////////// +CPT(qpGeomMunger) DXGraphicsStateGuardian8:: +get_geom_munger(const RenderState *) { + PT(DXGeomMunger8) munger = new DXGeomMunger8; + return qpGeomMunger::register_munger(munger); +} + // copies current display region in framebuffer to the texture // usually its more efficient to do SetRenderTgt void DXGraphicsStateGuardian8:: @@ -4052,19 +4064,6 @@ set_blend_mode() { enable_blend(false); } -//////////////////////////////////////////////////////////////////// -// Function: DXGraphicsStateGuardian8::setup_geom_munger -// Access: Protected, Virtual -// Description: Called after finish_modify_state has completed, this -// method sets up the GeomMunger for rendering with the -// current state. -//////////////////////////////////////////////////////////////////// -void DXGraphicsStateGuardian8:: -setup_geom_munger(PT(qpGeomMunger) munger) { - munger = new DXGeomMunger8; - GraphicsStateGuardian::setup_geom_munger(munger); -} - TypeHandle DXGraphicsStateGuardian8::get_type(void) const { return get_class_type(); } diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h index c989872950..adfb916d73 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.h +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.h @@ -97,6 +97,8 @@ public: virtual void apply_texture(TextureContext *tc); virtual void release_texture(TextureContext *tc); + virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state); + virtual void framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb); virtual bool framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr, @@ -152,7 +154,6 @@ protected: virtual void bind_clip_plane(PlaneNode *plane, int plane_id); virtual void set_blend_mode(); - virtual void setup_geom_munger(PT(qpGeomMunger) munger); void free_nondx_resources(); // free local internal buffers void free_d3d_device(void); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 53d80b54bc..d6e969fd89 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -2326,6 +2326,18 @@ static int binary_log_cap(const int x) { } #endif +//////////////////////////////////////////////////////////////////// +// Function: CLP(GraphicsStateGuardian)::get_geom_munger +// Access: Public, Virtual +// Description: Creates a new GeomMunger object to munge vertices +// appropriate to this GSG for the indicated state. +//////////////////////////////////////////////////////////////////// +CPT(qpGeomMunger) CLP(GraphicsStateGuardian):: +get_geom_munger(const RenderState *) { + PT(CLP(GeomMunger)) munger = new CLP(GeomMunger); + return qpGeomMunger::register_munger(munger); +} + //////////////////////////////////////////////////////////////////// // Function: CLP(GraphicsStateGuardian)::framebuffer_copy_to_texture // Access: Public, Virtual @@ -5036,19 +5048,6 @@ finish_modify_state() { } } -//////////////////////////////////////////////////////////////////// -// Function: CLP(GraphicsStateGuardian)::setup_geom_munger -// Access: Protected, Virtual -// Description: Called after finish_modify_state has completed, this -// method sets up the GeomMunger for rendering with the -// current state. -//////////////////////////////////////////////////////////////////// -void CLP(GraphicsStateGuardian):: -setup_geom_munger(PT(qpGeomMunger) munger) { - munger = new CLP(GeomMunger); - GraphicsStateGuardian::setup_geom_munger(munger); -} - //////////////////////////////////////////////////////////////////// // Function: CLP(GraphicsStateGuardian)::free_pointers // Access: Protected, Virtual diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index c19e5ddd99..4bdc0e8d77 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -102,6 +102,8 @@ public: virtual GeomContext *prepare_geom(Geom *geom); virtual void release_geom(GeomContext *gc); + virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state); + virtual void framebuffer_copy_to_texture (Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb); virtual bool framebuffer_copy_to_ram @@ -180,7 +182,6 @@ protected: virtual void set_blend_mode(); virtual void finish_modify_state(); - virtual void setup_geom_munger(PT(qpGeomMunger) munger); virtual void free_pointers(); diff --git a/panda/src/gobj/Sources.pp b/panda/src/gobj/Sources.pp index bf10d88fc1..4cbdd624fa 100644 --- a/panda/src/gobj/Sources.pp +++ b/panda/src/gobj/Sources.pp @@ -24,6 +24,7 @@ qpgeomTristrips.h \ qpgeomTrifans.h \ qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \ + qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \ qpgeomVertexData.h qpgeomVertexData.I \ qpgeomVertexDataType.h qpgeomVertexDataType.I \ qpgeomVertexFormat.h qpgeomVertexFormat.I \ @@ -55,6 +56,7 @@ qpgeomTristrips.cxx \ qpgeomTrifans.cxx \ qpgeomVertexArrayFormat.cxx \ + qpgeomVertexCacheManager.cxx \ qpgeomVertexData.cxx \ qpgeomVertexDataType.cxx \ qpgeomVertexFormat.cxx \ @@ -84,6 +86,7 @@ qpgeomTristrips.h \ qpgeomTrifans.h \ qpgeomVertexArrayFormat.h qpgeomVertexArrayFormat.I \ + qpgeomVertexCacheManager.h qpgeomVertexCacheManager.I \ qpgeomVertexData.h qpgeomVertexData.I \ qpgeomVertexDataType.h qpgeomVertexDataType.I \ qpgeomVertexFormat.h qpgeomVertexFormat.I \ diff --git a/panda/src/gobj/config_gobj.cxx b/panda/src/gobj/config_gobj.cxx index 90adae9c72..32c49c0e00 100644 --- a/panda/src/gobj/config_gobj.cxx +++ b/panda/src/gobj/config_gobj.cxx @@ -116,6 +116,15 @@ ConfigVariableString fake_texture_image "the same texture file, which will presumably only be loaded " "once.")); +ConfigVariableInt vertex_convert_cache +("vertex-convert-cache", 4194304, // 4 MB + PRC_DESC("This is the amount of memory, in bytes, that should be set " + "aside for storing pre-processed data for rendering vertices. " + "This is not a limit on the actual vertex data, which is " + "determined by the model; it is also not a limit on the " + "amount of memory used by the video driver or the system " + "graphics interface, which Panda has no control over.")); + ConfigVariableDouble default_near ("default-near", 1.0, PRC_DESC("The default near clipping distance for all cameras.")); diff --git a/panda/src/gobj/config_gobj.h b/panda/src/gobj/config_gobj.h index 10662f7274..4a6146b2d7 100644 --- a/panda/src/gobj/config_gobj.h +++ b/panda/src/gobj/config_gobj.h @@ -59,6 +59,8 @@ extern EXPCL_PANDA ConfigVariableEnum textures_power_2; extern EXPCL_PANDA ConfigVariableEnum textures_square; extern EXPCL_PANDA ConfigVariableString fake_texture_image; +extern EXPCL_PANDA ConfigVariableInt vertex_convert_cache; + extern ConfigVariableDouble default_near; extern ConfigVariableDouble default_far; extern ConfigVariableDouble default_fov; diff --git a/panda/src/gobj/drawable.cxx b/panda/src/gobj/drawable.cxx index da3f8e7541..d888de8d8f 100644 --- a/panda/src/gobj/drawable.cxx +++ b/panda/src/gobj/drawable.cxx @@ -48,7 +48,7 @@ dDrawable:: // At this level, this doesn't do very much. //////////////////////////////////////////////////////////////////// void dDrawable:: -draw(GraphicsStateGuardianBase *) const { +draw(GraphicsStateGuardianBase *, const qpGeomVertexData *) const { if (is_dirty()) { ((dDrawable *)this)->config(); } diff --git a/panda/src/gobj/drawable.h b/panda/src/gobj/drawable.h index 6078553ff5..569dd90068 100644 --- a/panda/src/gobj/drawable.h +++ b/panda/src/gobj/drawable.h @@ -34,6 +34,7 @@ class Datagram; class DatagramIterator; class BamReader; class BamWriter; +class qpGeomVertexData; //////////////////////////////////////////////////////////////////// // Class : Drawable @@ -50,7 +51,8 @@ public: dDrawable(); virtual ~dDrawable(); - virtual void draw(GraphicsStateGuardianBase *) const; + virtual void draw(GraphicsStateGuardianBase *gsg, + const qpGeomVertexData *vertex_data) const; virtual bool is_dynamic() const; protected: diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 68b7672635..534102072f 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -915,7 +915,7 @@ release_all() { // Description: Actually draws the Geom with the indicated GSG. //////////////////////////////////////////////////////////////////// void Geom:: -draw(GraphicsStateGuardianBase *gsg) const { +draw(GraphicsStateGuardianBase *gsg, const qpGeomVertexData *) const { PreparedGraphicsObjects *prepared_objects = gsg->get_prepared_objects(); if (is_dirty()) { ((Geom *)this)->config(); diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 3c67f7bc00..f09028a471 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -43,6 +43,7 @@ class DatagramIterator; class BamReader; class BamWriter; class GeomContext; +class qpGeomVertexData; class PreparedGraphicsObjects; //////////////////////////////////////////////////////////////////// @@ -239,7 +240,8 @@ public: int release_all(); // From parent dDrawable - virtual void draw(GraphicsStateGuardianBase *gsg) const; + virtual void draw(GraphicsStateGuardianBase *gsg, + const qpGeomVertexData *vertex_data) const; // From parent Configurable virtual void config(); diff --git a/panda/src/gobj/gobj_composite1.cxx b/panda/src/gobj/gobj_composite1.cxx index 8586ecb5dd..c087c14987 100644 --- a/panda/src/gobj/gobj_composite1.cxx +++ b/panda/src/gobj/gobj_composite1.cxx @@ -18,6 +18,7 @@ #include "qpgeomTristrips.cxx" #include "qpgeomTrifans.cxx" #include "qpgeomVertexArrayFormat.cxx" +#include "qpgeomVertexCacheManager.cxx" #include "qpgeomVertexData.cxx" #include "qpgeomVertexDataType.cxx" #include "qpgeomVertexFormat.cxx" diff --git a/panda/src/gobj/qpgeom.cxx b/panda/src/gobj/qpgeom.cxx index 62d7c704ff..ee937aace2 100644 --- a/panda/src/gobj/qpgeom.cxx +++ b/panda/src/gobj/qpgeom.cxx @@ -203,13 +203,15 @@ write(ostream &out, int indent_level) const { //////////////////////////////////////////////////////////////////// // Function: qpGeom::draw // Access: Public -// Description: Actually draws the Geom with the indicated GSG. +// Description: Actually draws the Geom with the indicated GSG, using +// the indicated vertex data (which might have been +// pre-munged to support the GSG's needs). //////////////////////////////////////////////////////////////////// void qpGeom:: -draw(GraphicsStateGuardianBase *gsg) const { +draw(GraphicsStateGuardianBase *gsg, const qpGeomVertexData *vertex_data) const { CDReader cdata(_cycler); - if (gsg->begin_draw_primitives(cdata->_data)) { + if (gsg->begin_draw_primitives(vertex_data)) { Primitives::const_iterator pi; for (pi = cdata->_primitives.begin(); pi != cdata->_primitives.end(); diff --git a/panda/src/gobj/qpgeom.h b/panda/src/gobj/qpgeom.h index ee75758b7f..e9d28c4f5c 100644 --- a/panda/src/gobj/qpgeom.h +++ b/panda/src/gobj/qpgeom.h @@ -76,7 +76,8 @@ PUBLISHED: void write(ostream &out, int indent_level = 0) const; public: - void draw(GraphicsStateGuardianBase *gsg) const; + void draw(GraphicsStateGuardianBase *gsg, + const qpGeomVertexData *vertex_data) const; protected: virtual BoundingVolume *recompute_bound(); diff --git a/panda/src/gobj/qpgeomMunger.cxx b/panda/src/gobj/qpgeomMunger.cxx index 223bbb2a14..a77778b1d3 100644 --- a/panda/src/gobj/qpgeomMunger.cxx +++ b/panda/src/gobj/qpgeomMunger.cxx @@ -17,10 +17,15 @@ //////////////////////////////////////////////////////////////////// #include "qpgeomMunger.h" +#include "qpgeomVertexCacheManager.h" +#include "mutexHolder.h" +#include "pStatTimer.h" qpGeomMunger::Registry *qpGeomMunger::_registry = NULL; TypeHandle qpGeomMunger::_type_handle; +PStatCollector qpGeomMunger::_munge_pcollector("Cull:Munge"); + //////////////////////////////////////////////////////////////////// // Function: qpGeomMunger::Constructor // Access: Public @@ -92,6 +97,7 @@ remove_data(const qpGeomVertexData *data) { CPT(qpGeomVertexData) qpGeomMunger:: do_munge_data(const qpGeomVertexData *data) { nassertr(_is_registered, NULL); + PStatTimer timer(_munge_pcollector); CPT(qpGeomVertexFormat) orig_format = data->get_format(); CPT(qpGeomVertexFormat) new_format = munge_format(orig_format); @@ -132,8 +138,12 @@ do_munge_format(const qpGeomVertexFormat *format) { nassertr(inserted, NULL); // Tell the source format that we have its pointer. - inserted = ((qpGeomVertexFormat *)format)->_mungers.insert(this).second; - nassertr(inserted, NULL); + { + qpGeomVertexFormat *f = (qpGeomVertexFormat *)format; + MutexHolder holder(f->_cache_lock); + inserted = f->_mungers.insert(this).second; + nassertr(inserted, NULL); + } // We also want to keep a reference count on the derived format, but // only if it is actually a different pointer from the original @@ -189,9 +199,20 @@ make_registry() { //////////////////////////////////////////////////////////////////// void qpGeomMunger:: do_register() { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "GeomMunger::do_register(): " << (void *)this << "\n"; + } nassertv(!_is_registered); nassertv(_formats.empty()); + // Tell the cache manager to hang on to this new GeomMunger, so we + // don't waste our time re-registering the same GeomMunger over and + // over again. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + cache_mgr->record_munger(this); + _is_registered = true; } @@ -202,17 +223,24 @@ do_register() { //////////////////////////////////////////////////////////////////// void qpGeomMunger:: do_unregister() { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "GeomMunger::do_unregister(): " << (void *)this << "\n"; + } nassertv(_is_registered); _is_registered = false; // Unregistering means we should blow away the cache. Formats::iterator fi; for (fi = _formats.begin(); fi != _formats.end(); ++fi) { - const qpGeomVertexFormat *format = (*fi).first; + qpGeomVertexFormat *format = (qpGeomVertexFormat *)(*fi).first; CPT(qpGeomVertexFormat) derived_format = (*fi).second; - size_t num_erased = ((qpGeomVertexFormat *)format)->_mungers.erase(this); - nassertv(num_erased == 1); + { + MutexHolder holder(format->_cache_lock); + size_t num_erased = format->_mungers.erase(this); + nassertv(num_erased == 1); + } if (derived_format != format) { derived_format->unref(); diff --git a/panda/src/gobj/qpgeomMunger.h b/panda/src/gobj/qpgeomMunger.h index 943ff6393e..b28571ff27 100644 --- a/panda/src/gobj/qpgeomMunger.h +++ b/panda/src/gobj/qpgeomMunger.h @@ -23,6 +23,7 @@ #include "typedReferenceCount.h" #include "qpgeomVertexFormat.h" #include "indirectCompareTo.h" +#include "pStatCollector.h" #include "pmap.h" #include "pset.h" @@ -97,6 +98,8 @@ private: static Registry *_registry; + static PStatCollector _munge_pcollector; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/gobj/qpgeomPrimitive.cxx b/panda/src/gobj/qpgeomPrimitive.cxx index 6c50341ed0..1c51c863b8 100644 --- a/panda/src/gobj/qpgeomPrimitive.cxx +++ b/panda/src/gobj/qpgeomPrimitive.cxx @@ -20,6 +20,7 @@ #include "qpgeomVertexData.h" #include "qpgeomVertexArrayFormat.h" #include "qpgeomVertexDataType.h" +#include "qpgeomVertexCacheManager.h" #include "internalName.h" #include "bamReader.h" #include "bamWriter.h" @@ -55,6 +56,23 @@ qpGeomPrimitive(const qpGeomPrimitive ©) : //////////////////////////////////////////////////////////////////// qpGeomPrimitive:: ~qpGeomPrimitive() { + // When we destruct, we should ensure that all of our cached + // entries, across all pipeline stages, are properly removed from + // the cache manager. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + + int num_stages = _cycler.get_num_stages(); + for (int i = 0; i < num_stages; i++) { + if (_cycler.is_stage_unique(i)) { + CData *cdata = _cycler.write_stage(i); + if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { + cache_mgr->remove_decompose(this); + cdata->_decomposed = NULL; + } + _cycler.release_write_stage(i, cdata); + } + } } //////////////////////////////////////////////////////////////////// @@ -209,6 +227,19 @@ set_lengths(PTA_int lengths) { cdata->_lengths = lengths; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::get_num_bytes +// Access: Published +// Description: Records the number of bytes consumed by the primitive +// and its index table(s). +//////////////////////////////////////////////////////////////////// +int qpGeomPrimitive:: +get_num_bytes() const { + CDReader cdata(_cycler); + return cdata->_vertices.size() * sizeof(short) + + cdata->_lengths.size() * sizeof(int); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::get_num_vertices_per_primitive // Access: Published, Virtual @@ -310,7 +341,7 @@ get_primitive_num_vertices(int i) const { //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::decompose -// Access: Published, Virtual +// Access: Published // Description: Decomposes a complex primitive type into a simpler // primitive type, for instance triangle strips to // triangles, and returns a pointer to the new primitive @@ -323,8 +354,48 @@ get_primitive_num_vertices(int i) const { // possible kind of primitive type. //////////////////////////////////////////////////////////////////// PT(qpGeomPrimitive) qpGeomPrimitive:: -decompose(const qpGeomVertexData *vertex_data) { - return this; +decompose() { + PT(qpGeomPrimitive) result; + { + // Use read() and release_read() instead of CDReader, because the + // call to record_decompose() might recursively call back into + // this object, and require a write. + const CData *cdata = _cycler.read(); + if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { + result = cdata->_decomposed; + _cycler.release_read(cdata); + // Record a cache hit, so this element will stay in the cache a + // while longer. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + cache_mgr->record_decompose(this, result->get_num_bytes()); + + return result; + } + _cycler.release_read(cdata); + } + + result = decompose_impl(); + if (result == this) { + // decomposing this primitive has no effect. + return this; + } + + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Decomposing " << get_type() << ": " << (void *)this << "\n"; + } + + // Record the result for the future. + CDWriter cdata(_cycler); + cdata->_decomposed = result; + + // And add *this* object to the cache manager. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + cache_mgr->record_decompose(this, result->get_num_bytes()); + + return result; } //////////////////////////////////////////////////////////////////// @@ -361,6 +432,26 @@ write(ostream &out, const qpGeomVertexData *vertex_data, } } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::clear_cache +// Access: Published +// Description: Removes all of the previously-cached results of +// convert_to(). +//////////////////////////////////////////////////////////////////// +void qpGeomPrimitive:: +clear_cache() { + // Probably we shouldn't do anything at all here unless we are + // running in pipeline stage 0. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + + CData *cdata = CDWriter(_cycler); + if (cdata->_decomposed != (qpGeomPrimitive *)NULL) { + cache_mgr->remove_decompose(this); + cdata->_decomposed = NULL; + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::calc_tight_bounds // Access: Public, Virtual @@ -417,6 +508,42 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, } } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::decompose_impl +// Access: Protected +// Description: Decomposes a complex primitive type into a simpler +// primitive type, for instance triangle strips to +// triangles, and returns a pointer to the new primitive +// definition. If the decomposition cannot be +// performed, this might return the original object. +// +// This method is useful for application code that wants +// to iterate through the set of triangles on the +// primitive without having to write handlers for each +// possible kind of primitive type. +//////////////////////////////////////////////////////////////////// +PT(qpGeomPrimitive) qpGeomPrimitive:: +decompose_impl() { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomPrimitive::remove_cache_entry +// Access: Private +// Description: Removes a particular entry from the local cache; it +// has already been removed from the cache manager. +// This is only called from GeomVertexCacheManager. +//////////////////////////////////////////////////////////////////// +void qpGeomPrimitive:: +remove_cache_entry() const { + // We have to operate on stage 0 of the pipeline, since that's where + // the cache really counts. Because of the multistage pipeline, we + // might not actually have a cache entry there (it might have been + // added to stage 1 instead). No big deal if we don't. + CData *cdata = ((qpGeomPrimitive *)this)->_cycler.write_stage(0); + cdata->_decomposed = NULL; +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomPrimitive::recompute_minmax // Access: Private diff --git a/panda/src/gobj/qpgeomPrimitive.h b/panda/src/gobj/qpgeomPrimitive.h index f7a78975b4..2b46690544 100644 --- a/panda/src/gobj/qpgeomPrimitive.h +++ b/panda/src/gobj/qpgeomPrimitive.h @@ -79,6 +79,8 @@ PUBLISHED: PTA_int modify_lengths(); void set_lengths(PTA_int lengths); + int get_num_bytes() const; + INLINE int get_min_vertex() const; INLINE int get_max_vertex() const; @@ -87,12 +89,14 @@ PUBLISHED: int get_primitive_start(int i) const; int get_primitive_num_vertices(int i) const; - virtual PT(qpGeomPrimitive) decompose(const qpGeomVertexData *vertex_data); + PT(qpGeomPrimitive) decompose(); virtual void output(ostream &out, const qpGeomVertexData *vertex_data) const; virtual void write(ostream &out, const qpGeomVertexData *vertex_data, int indent_level) const; + void clear_cache(); + public: virtual void draw(GraphicsStateGuardianBase *gsg)=0; @@ -100,6 +104,12 @@ public: bool &found_any, const qpGeomVertexData *vertex_data) const; +protected: + virtual PT(qpGeomPrimitive) decompose_impl(); + +private: + void remove_cache_entry() const; + private: // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { @@ -117,6 +127,8 @@ private: bool _got_minmax; unsigned short _min_vertex; unsigned short _max_vertex; + + PT(qpGeomPrimitive) _decomposed; }; PipelineCycler _cycler; @@ -149,6 +161,7 @@ private: static TypeHandle _type_handle; friend class qpGeom; + friend class qpGeomVertexCacheManager; }; #include "qpgeomPrimitive.I" diff --git a/panda/src/gobj/qpgeomTrifans.cxx b/panda/src/gobj/qpgeomTrifans.cxx index 9a19bedaad..e367012b61 100644 --- a/panda/src/gobj/qpgeomTrifans.cxx +++ b/panda/src/gobj/qpgeomTrifans.cxx @@ -53,7 +53,18 @@ qpGeomTrifans:: } //////////////////////////////////////////////////////////////////// -// Function: qpGeomTrifans::decompose +// Function: qpGeomTrifans::draw +// Access: Public, Virtual +// Description: Calls the appropriate method on the GSG to draw the +// primitive. +//////////////////////////////////////////////////////////////////// +void qpGeomTrifans:: +draw(GraphicsStateGuardianBase *gsg) { + gsg->draw_trifans(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTrifans::decompose_impl // Access: Published, Virtual // Description: Decomposes a complex primitive type into a simpler // primitive type, for instance triangle strips to @@ -67,7 +78,7 @@ qpGeomTrifans:: // possible kind of primitive type. //////////////////////////////////////////////////////////////////// PT(qpGeomPrimitive) qpGeomTrifans:: -decompose(const qpGeomVertexData *) { +decompose_impl() { PT(qpGeomTriangles) triangles = new qpGeomTriangles; CPTA_ushort vertices = get_vertices(); CPTA_int lengths = get_lengths(); @@ -95,17 +106,6 @@ decompose(const qpGeomVertexData *) { return triangles.p(); } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomTrifans::draw -// Access: Public, Virtual -// Description: Calls the appropriate method on the GSG to draw the -// primitive. -//////////////////////////////////////////////////////////////////// -void qpGeomTrifans:: -draw(GraphicsStateGuardianBase *gsg) { - gsg->draw_trifans(this); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomTrifans::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeomTrifans.h b/panda/src/gobj/qpgeomTrifans.h index 1cb707cc7c..187fc14496 100644 --- a/panda/src/gobj/qpgeomTrifans.h +++ b/panda/src/gobj/qpgeomTrifans.h @@ -34,11 +34,12 @@ PUBLISHED: qpGeomTrifans(const qpGeomTrifans ©); virtual ~qpGeomTrifans(); - virtual PT(qpGeomPrimitive) decompose(const qpGeomVertexData *vertex_data); - public: virtual void draw(GraphicsStateGuardianBase *gsg); +protected: + virtual PT(qpGeomPrimitive) decompose_impl(); + public: static void register_with_read_factory(); diff --git a/panda/src/gobj/qpgeomTristrips.cxx b/panda/src/gobj/qpgeomTristrips.cxx index 964085d607..688a67beec 100644 --- a/panda/src/gobj/qpgeomTristrips.cxx +++ b/panda/src/gobj/qpgeomTristrips.cxx @@ -53,7 +53,18 @@ qpGeomTristrips:: } //////////////////////////////////////////////////////////////////// -// Function: qpGeomTristrips::decompose +// Function: qpGeomTristrips::draw +// Access: Public, Virtual +// Description: Calls the appropriate method on the GSG to draw the +// primitive. +//////////////////////////////////////////////////////////////////// +void qpGeomTristrips:: +draw(GraphicsStateGuardianBase *gsg) { + gsg->draw_tristrips(this); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomTristrips::decompose_impl // Access: Published, Virtual // Description: Decomposes a complex primitive type into a simpler // primitive type, for instance triangle strips to @@ -67,7 +78,7 @@ qpGeomTristrips:: // possible kind of primitive type. //////////////////////////////////////////////////////////////////// PT(qpGeomPrimitive) qpGeomTristrips:: -decompose(const qpGeomVertexData *) { +decompose_impl() { PT(qpGeomTriangles) triangles = new qpGeomTriangles; CPTA_ushort vertices = get_vertices(); CPTA_int lengths = get_lengths(); @@ -105,17 +116,6 @@ decompose(const qpGeomVertexData *) { return triangles.p(); } -//////////////////////////////////////////////////////////////////// -// Function: qpGeomTristrips::draw -// Access: Public, Virtual -// Description: Calls the appropriate method on the GSG to draw the -// primitive. -//////////////////////////////////////////////////////////////////// -void qpGeomTristrips:: -draw(GraphicsStateGuardianBase *gsg) { - gsg->draw_tristrips(this); -} - //////////////////////////////////////////////////////////////////// // Function: qpGeomTristrips::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeomTristrips.h b/panda/src/gobj/qpgeomTristrips.h index a532c0c8b4..bb788026ec 100644 --- a/panda/src/gobj/qpgeomTristrips.h +++ b/panda/src/gobj/qpgeomTristrips.h @@ -34,11 +34,12 @@ PUBLISHED: qpGeomTristrips(const qpGeomTristrips ©); virtual ~qpGeomTristrips(); - virtual PT(qpGeomPrimitive) decompose(const qpGeomVertexData *vertex_data); - public: virtual void draw(GraphicsStateGuardianBase *gsg); +protected: + virtual PT(qpGeomPrimitive) decompose_impl(); + public: static void register_with_read_factory(); diff --git a/panda/src/gobj/qpgeomVertexCacheManager.I b/panda/src/gobj/qpgeomVertexCacheManager.I new file mode 100644 index 0000000000..bacc34a164 --- /dev/null +++ b/panda/src/gobj/qpgeomVertexCacheManager.I @@ -0,0 +1,179 @@ +// Filename: qpgeomVertexCacheManager.I +// Created by: drose (11Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::set_max_size +// Access: Published +// Description: Specifies the amount of memory, in bytes, that should +// be set aside for storing pre-processed data for +// rendering vertices. This is not a limit on the +// actual vertex data, which is what it is; it is also +// not a limit on the amount of memory used by the video +// driver or the system graphics interface, which Panda +// has no control over. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +set_max_size(int max_size) const { + // We directly change the config variable. + vertex_convert_cache = max_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::get_max_size +// Access: Published +// Description: Returns the amount of memory, in bytes, that should +// be set aside for storing pre-processed data for +// rendering vertices. See set_max_size(). +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomVertexCacheManager:: +get_max_size() const { + return vertex_convert_cache; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::get_total_size +// Access: Published +// Description: Returns the amount of memory, in bytes, currently +// consumed by the cache of pre-processed vertex data. +//////////////////////////////////////////////////////////////////// +INLINE int qpGeomVertexCacheManager:: +get_total_size() const { + return _total_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::record_munger +// Access: Private +// Description: Records a new GeomMunger in the cache, or marks a +// cache hit for a previously-recorded munger. This +// should only be called by GeomMunger. +// +// The cache manager will hold a reference on the +// GeomMunger pointer until it expires from the cache. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +record_munger(const qpGeomMunger *munger) { + Entry entry; + entry._munger = munger; + entry._primitive = NULL; + entry._source = NULL; + entry._format = NULL; + entry._result_size = 100; // Make up a nominal number. + + record_entry(entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::record_decompose +// Access: Private +// Description: Records a new entry in the cache, or marks a cache +// hit for a previous entry in the cache. This should +// only be called by GeomPrimitive. +// +// The cache manager will not hold a reference on the +// GeomPrimitive pointer; if it destructs, it should +// call remove_decompose() to remove itself from the cache. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +record_decompose(const qpGeomPrimitive *primitive, int result_size) { + Entry entry; + entry._primitive = primitive; + entry._source = NULL; + entry._format = NULL; + entry._result_size = result_size; + + record_entry(entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::remove_decompose +// Access: Private +// Description: Removes an entry from the cache, if it is there. +// Quietly ignores it if it is not. This should only be +// called by GeomPrimitive. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +remove_decompose(const qpGeomPrimitive *primitive) { + Entry entry; + entry._primitive = primitive; + entry._source = NULL; + entry._format = NULL; + + remove_entry(entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::record_data +// Access: Private +// Description: Records a new entry in the cache, or marks a cache +// hit for a previous entry in the cache. This should +// only be called by GeomVertexData. +// +// The cache manager will not hold a reference on the +// GeomVertexData pointer; if it destructs, it should +// call remove_data() to remove itself from the cache. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +record_data(const qpGeomVertexData *source, const qpGeomVertexFormat *format, + int result_size) { + Entry entry; + entry._primitive = NULL; + entry._source = source; + entry._format = format; + entry._result_size = result_size; + + record_entry(entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::remove_data +// Access: Private +// Description: Removes an entry from the cache, if it is there. +// Quietly ignores it if it is not. This should only be +// called by GeomVertexData. +//////////////////////////////////////////////////////////////////// +INLINE void qpGeomVertexCacheManager:: +remove_data(const qpGeomVertexData *source, + const qpGeomVertexFormat *format) { + Entry entry; + entry._primitive = NULL; + entry._source = source; + entry._format = format; + + remove_entry(entry); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Entry::operator < +// Access: Public +// Description: Provides a unique ordering for EntriesIndex. +//////////////////////////////////////////////////////////////////// +INLINE bool qpGeomVertexCacheManager::Entry:: +operator < (const qpGeomVertexCacheManager::Entry &other) const { + if (_munger != other._munger) { + return _munger < other._munger; + } + if (_primitive != other._primitive) { + return _primitive < other._primitive; + } + if (_source != other._source) { + return _source < other._source; + } + return _format < other._format; +} diff --git a/panda/src/gobj/qpgeomVertexCacheManager.cxx b/panda/src/gobj/qpgeomVertexCacheManager.cxx new file mode 100644 index 0000000000..b48fb6a0c8 --- /dev/null +++ b/panda/src/gobj/qpgeomVertexCacheManager.cxx @@ -0,0 +1,148 @@ +// Filename: qpgeomVertexCacheManager.cxx +// Created by: drose (11Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "qpgeomVertexCacheManager.h" +#include "mutexHolder.h" + +qpGeomVertexCacheManager *qpGeomVertexCacheManager::_global_ptr = NULL; + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Constructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +qpGeomVertexCacheManager:: +qpGeomVertexCacheManager() : + _total_size(0) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::Destructor +// Access: Protected +// Description: +//////////////////////////////////////////////////////////////////// +qpGeomVertexCacheManager:: +~qpGeomVertexCacheManager() { +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::get_global_ptr +// Access: Published, Static +// Description: Returns the global cache manager pointer. +//////////////////////////////////////////////////////////////////// +qpGeomVertexCacheManager *qpGeomVertexCacheManager:: +get_global_ptr() { + if (_global_ptr == (qpGeomVertexCacheManager *)NULL) { + _global_ptr = new qpGeomVertexCacheManager; + } + return _global_ptr; +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::record_data +// Access: Private +// Description: Records a new generic entry in the cache, or marks a +// cache hit for a previous entry in the cache. +//////////////////////////////////////////////////////////////////// +void qpGeomVertexCacheManager:: +record_entry(const qpGeomVertexCacheManager::Entry &entry) { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "record_entry(" << entry._munger << ", " + << entry._primitive << ", " << entry._source << ", " + << entry._format << ", " << entry._result_size << ")\n"; + } + + MutexHolder holder(_lock); + EntriesIndex::iterator ii = _entries_index.find(&entry); + if (ii != _entries_index.end()) { + // Remove the previous cache entry. + Entries::iterator ei = (*ii).second; + _total_size -= (*ei)._result_size; + _entries_index.erase(ii); + _entries.erase(ei); + } + + // Add the new entry at the end of the list, so it will be the last + // to be removed. + Entries::iterator ei = + _entries.insert(_entries.end(), entry); + Entry *entry_pointer = &(*ei); + + // Also record an index entry. + bool inserted = _entries_index.insert + (EntriesIndex::value_type(entry_pointer, ei)).second; + nassertv(inserted); + + _total_size += entry_pointer->_result_size; + + // Now remove any old entries if our cache is over the limit. This may + // also remove the entry we just added, especially if our cache size + // is set to 0. + int max_size = get_max_size(); + while (_total_size > max_size) { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "total_size = " << _total_size << ", max_size = " + << max_size << ", flushing\n"; + } + nassertv(!_entries.empty()); + ei = _entries.begin(); + entry_pointer = &(*ei); + + ii = _entries_index.find(entry_pointer); + nassertv(ii != _entries_index.end()); + + if (entry_pointer->_source != (qpGeomVertexData *)NULL) { + entry_pointer->_source->remove_cache_entry(entry_pointer->_format); + } + _total_size -= entry_pointer->_result_size; + _entries_index.erase(ii); + _entries.erase(ei); + } + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "total_size = " << _total_size << ", max_size = " + << max_size << ", done\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexCacheManager::remove_entry +// Access: Private +// Description: Removes an entry from the cache, if it is there. +// Quietly ignores it if it is not. +//////////////////////////////////////////////////////////////////// +void qpGeomVertexCacheManager:: +remove_entry(const qpGeomVertexCacheManager::Entry &entry) { + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "remove_entry(" << entry._munger << ", " + << entry._source << ", " << entry._format << ")\n"; + } + MutexHolder holder(_lock); + + EntriesIndex::iterator ii = _entries_index.find(&entry); + if (ii != _entries_index.end()) { + Entries::iterator ei = (*ii).second; + _total_size -= (*ei)._result_size; + _entries_index.erase(ii); + _entries.erase(ei); + } +} diff --git a/panda/src/gobj/qpgeomVertexCacheManager.h b/panda/src/gobj/qpgeomVertexCacheManager.h new file mode 100644 index 0000000000..8b29113fb8 --- /dev/null +++ b/panda/src/gobj/qpgeomVertexCacheManager.h @@ -0,0 +1,113 @@ +// Filename: qpgeomVertexCacheManager.h +// Created by: drose (11Mar05) +// +//////////////////////////////////////////////////////////////////// +// +// 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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef qpGEOMVERTEXCACHEMANAGER_H +#define qpGEOMVERTEXCACHEMANAGER_H + +#include "pandabase.h" +#include "qpgeomMunger.h" +#include "config_gobj.h" +#include "pointerTo.h" +#include "pmutex.h" +#include "indirectLess.h" +#include "plist.h" +#include "pmap.h" + +class qpGeomPrimitive; +class qpGeomVertexData; +class qpGeomVertexFormat; + +//////////////////////////////////////////////////////////////////// +// Class : qpGeomVertexCacheManager +// Description : This is used to keep track of, and limit the size of, +// the cache of munged vertices, which would otherwise +// be distributed through all of the GeomVertexData +// objects in the system. +// +// The actual data in the cache is not stored here, but +// rather it is distributed among the various +// GeomVertexData source objects. This allows the cache +// data to propagate through the multiprocess pipeline. +// +// This is part of the experimental Geom rewrite. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA qpGeomVertexCacheManager { +protected: + qpGeomVertexCacheManager(); + ~qpGeomVertexCacheManager(); + +PUBLISHED: + INLINE void set_max_size(int max_size) const; + INLINE int get_max_size() const; + + INLINE int get_total_size() const; + + static qpGeomVertexCacheManager *get_global_ptr(); + +private: + INLINE void record_munger(const qpGeomMunger *munger); + INLINE void record_decompose(const qpGeomPrimitive *primitive, + int result_size); + INLINE void remove_decompose(const qpGeomPrimitive *primitive); + INLINE void record_data(const qpGeomVertexData *source, + const qpGeomVertexFormat *format, + int result_size); + INLINE void remove_data(const qpGeomVertexData *source, + const qpGeomVertexFormat *format); + + class Entry; + + void record_entry(const Entry &entry); + void remove_entry(const Entry &entry); + +private: + // This mutex protects all operations on this object. + Mutex _lock; + + int _total_size; + + class Entry { + public: + INLINE bool operator < (const Entry &other) const; + + CPT(qpGeomMunger) _munger; + const qpGeomPrimitive *_primitive; + const qpGeomVertexData *_source; + const qpGeomVertexFormat *_format; + int _result_size; + }; + + // This list keeps the cache entries in least-recently-used order: + // the items at the head of the list are ready to be flushed. + typedef plist Entries; + Entries _entries; + + // And this indexes into the above list, for fast lookup. + typedef pmap > EntriesIndex; + EntriesIndex _entries_index; + + static qpGeomVertexCacheManager *_global_ptr; + + friend class qpGeomMunger; + friend class qpGeomPrimitive; + friend class qpGeomVertexData; +}; + +#include "qpgeomVertexCacheManager.I" + +#endif diff --git a/panda/src/gobj/qpgeomVertexData.cxx b/panda/src/gobj/qpgeomVertexData.cxx index 2a08851232..060de66918 100644 --- a/panda/src/gobj/qpgeomVertexData.cxx +++ b/panda/src/gobj/qpgeomVertexData.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "qpgeomVertexData.h" +#include "qpgeomVertexCacheManager.h" #include "bamReader.h" #include "bamWriter.h" #include "pset.h" @@ -82,6 +83,25 @@ operator = (const qpGeomVertexData ©) { //////////////////////////////////////////////////////////////////// qpGeomVertexData:: ~qpGeomVertexData() { + // When we destruct, we should ensure that all of our cached + // entries, across all pipeline stages, are properly removed from + // the cache manager. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + + int num_stages = _cycler.get_num_stages(); + for (int i = 0; i < num_stages; i++) { + if (_cycler.is_stage_unique(i)) { + CData *cdata = _cycler.write_stage(i); + for (ConvertedCache::iterator ci = cdata->_converted_cache.begin(); + ci != cdata->_converted_cache.end(); + ++ci) { + cache_mgr->remove_data(this, (*ci).first); + } + cdata->_converted_cache.clear(); + _cycler.release_write_stage(i, cdata); + } + } } //////////////////////////////////////////////////////////////////// @@ -117,11 +137,14 @@ set_num_vertices(int n) { CDWriter cdata(_cycler); nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size()); + bool any_changed = false; + for (size_t i = 0; i < cdata->_arrays.size(); i++) { int stride = _format->get_array(i)->get_stride(); int delta = n - (cdata->_arrays[i].size() / stride); if (delta != 0) { + any_changed = true; if (cdata->_arrays[i].get_ref_count() > 1) { // Copy-on-write: the array is already reffed somewhere else, // so we're just going to make a copy. @@ -145,6 +168,10 @@ set_num_vertices(int n) { } } } + + if (any_changed) { + clear_cache(); + } } //////////////////////////////////////////////////////////////////// @@ -190,6 +217,8 @@ modify_array_data(int array) { cdata->_arrays[array] = PTA_uchar(); cdata->_arrays[array].v() = orig_data.v(); } + + clear_cache(); return cdata->_arrays[array]; } @@ -209,105 +238,23 @@ set_array_data(int array, PTA_uchar array_data) { } //////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexData::set_data +// Function: qpGeomVertexData::get_num_bytes // Access: Published -// Description: Sets the nth vertex to a particular value. Query the -// format to get the array index and data_type -// parameters for the particular data type you want to -// set. -// -// This flavor of set_data() accepts a generic float -// array and a specific number of dimensions. The new -// data will be copied from the num_values elements -// of data. +// Description: Returns the total number of bytes consumed by the +// different arrays of the vertex data. //////////////////////////////////////////////////////////////////// -void qpGeomVertexData:: -set_data(int array, const qpGeomVertexDataType *data_type, - int vertex, const float *data, int num_values) { - int stride = _format->get_array(array)->get_stride(); - int element = vertex * stride + data_type->get_start(); +int qpGeomVertexData:: +get_num_bytes() const { + CDReader cdata(_cycler); + + int num_bytes = 0; - { - CDReader cdata(_cycler); - int array_size = (int)cdata->_arrays[array].size(); - if (element + data_type->get_total_bytes() > array_size) { - // Whoops, we need more vertices! - set_num_vertices(vertex + 1); - } + Arrays::const_iterator ai; + for (ai = cdata->_arrays.begin(); ai != cdata->_arrays.end(); ++ai) { + num_bytes += (*ai).size(); } - PTA_uchar array_data = modify_array_data(array); - nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size()); - - switch (data_type->get_numeric_type()) { - case qpGeomVertexDataType::NT_uint8: - { - nassertv(num_values <= data_type->get_num_values()); - for (int i = 0; i < num_values; i++) { - int value = (int)(data[i] * 255.0f); - *(unsigned char *)&array_data[element] = value; - element += 1; - } - } - break; - - case qpGeomVertexDataType::NT_packed_argb: - { - nassertv(num_values == 4); - *(PN_uint32 *)&array_data[element] = pack_argb(data); - } - break; - - case qpGeomVertexDataType::NT_float: - nassertv(num_values == data_type->get_num_values()); - memcpy(&array_data[element], data, data_type->get_total_bytes()); - break; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: qpGeomVertexData::get_data -// Access: Published -// Description: Returns the data associated with the nth vertex for a -// particular value. Query the format to get the array -// index and data_type parameters for the particular -// data type you want to get. -// -// This flavor of get_data() copies its data into a -// generic float array. -//////////////////////////////////////////////////////////////////// -void qpGeomVertexData:: -get_data(int array, const qpGeomVertexDataType *data_type, - int vertex, float *data, int num_values) const { - CPTA_uchar array_data = get_array_data(array); - int stride = _format->get_array(array)->get_stride(); - int element = vertex * stride + data_type->get_start(); - nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size()); - - switch (data_type->get_numeric_type()) { - case qpGeomVertexDataType::NT_uint8: - { - nassertv(num_values <= data_type->get_num_values()); - for (int i = 0; i < num_values; i++) { - int value = *(unsigned char *)&array_data[element]; - element += 1; - data[i] = (float)value / 255.0f; - } - } - break; - - case qpGeomVertexDataType::NT_packed_argb: - { - nassertv(num_values == 4); - unpack_argb(data, *(PN_uint32 *)&array_data[element]); - } - break; - - case qpGeomVertexDataType::NT_float: - nassertv(num_values <= data_type->get_num_values()); - memcpy(data, &array_data[element], num_values * sizeof(PN_float32)); - break; - } + return num_bytes; } //////////////////////////////////////////////////////////////////// @@ -318,10 +265,43 @@ get_data(int array, const qpGeomVertexDataType *data_type, // the data vertex-by-vertex to a new set of data arrays // in the new format. //////////////////////////////////////////////////////////////////// -PT(qpGeomVertexData) qpGeomVertexData:: +CPT(qpGeomVertexData) qpGeomVertexData:: convert_to(const qpGeomVertexFormat *new_format) const { + if (new_format == _format) { + // Trivial case: no change is needed. + return this; + } + + // Look up the format in our cache--maybe we've recently converted + // to the requested format. + { + // Use read() and release_read() instead of CDReader, because the + // call to record_data() might recursively call back into this + // object, and require a write. + const CData *cdata = _cycler.read(); + ConvertedCache::const_iterator ci = + cdata->_converted_cache.find(new_format); + if (ci != cdata->_converted_cache.end()) { + _cycler.release_read(cdata); + // Record a cache hit, so this element will stay in the cache a + // while longer. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + cache_mgr->record_data(this, new_format, (*ci).second->get_num_bytes()); + + return (*ci).second; + } + _cycler.release_read(cdata); + } + + // Okay, convert the data to the new format. int num_vertices = get_num_vertices(); + if (gobj_cat.is_debug()) { + gobj_cat.debug() + << "Converting " << num_vertices << " vertices.\n"; + } + PT(qpGeomVertexData) new_data = new qpGeomVertexData(new_format); pset done_arrays; @@ -383,6 +363,19 @@ convert_to(const qpGeomVertexFormat *new_format) const { } } + // Record the new result in the cache. + { + CDWriter cdata(((qpGeomVertexData *)this)->_cycler); + cdata->_converted_cache[new_format] = new_data; + } + + // And tell the cache manager about the new entry. (It might + // immediately request a delete from the cache of the thing we just + // added.) + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + cache_mgr->record_data(this, new_format, new_data->get_num_bytes()); + return new_data; } @@ -406,6 +399,130 @@ write(ostream &out, int indent_level) const { _format->write_with_data(out, indent_level, this); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::clear_cache +// Access: Published +// Description: Removes all of the previously-cached results of +// convert_to(). +//////////////////////////////////////////////////////////////////// +void qpGeomVertexData:: +clear_cache() { + // Probably we shouldn't do anything at all here unless we are + // running in pipeline stage 0. + qpGeomVertexCacheManager *cache_mgr = + qpGeomVertexCacheManager::get_global_ptr(); + + CData *cdata = CDWriter(_cycler); + for (ConvertedCache::iterator ci = cdata->_converted_cache.begin(); + ci != cdata->_converted_cache.end(); + ++ci) { + cache_mgr->remove_data(this, (*ci).first); + } + cdata->_converted_cache.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::set_data +// Access: Public +// Description: Sets the nth vertex to a particular value. Query the +// format to get the array index and data_type +// parameters for the particular data type you want to +// set. +// +// This flavor of set_data() accepts a generic float +// array and a specific number of dimensions. The new +// data will be copied from the num_values elements +// of data. +//////////////////////////////////////////////////////////////////// +void qpGeomVertexData:: +set_data(int array, const qpGeomVertexDataType *data_type, + int vertex, const float *data, int num_values) { + int stride = _format->get_array(array)->get_stride(); + int element = vertex * stride + data_type->get_start(); + + { + CDReader cdata(_cycler); + int array_size = (int)cdata->_arrays[array].size(); + if (element + data_type->get_total_bytes() > array_size) { + // Whoops, we need more vertices! + set_num_vertices(vertex + 1); + } + } + + PTA_uchar array_data = modify_array_data(array); + nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size()); + + switch (data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + { + nassertv(num_values <= data_type->get_num_values()); + for (int i = 0; i < num_values; i++) { + int value = (int)(data[i] * 255.0f); + *(unsigned char *)&array_data[element] = value; + element += 1; + } + } + break; + + case qpGeomVertexDataType::NT_packed_argb: + { + nassertv(num_values == 4); + *(PN_uint32 *)&array_data[element] = pack_argb(data); + } + break; + + case qpGeomVertexDataType::NT_float: + nassertv(num_values == data_type->get_num_values()); + memcpy(&array_data[element], data, data_type->get_total_bytes()); + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::get_data +// Access: Public +// Description: Returns the data associated with the nth vertex for a +// particular value. Query the format to get the array +// index and data_type parameters for the particular +// data type you want to get. +// +// This flavor of get_data() copies its data into a +// generic float array. +//////////////////////////////////////////////////////////////////// +void qpGeomVertexData:: +get_data(int array, const qpGeomVertexDataType *data_type, + int vertex, float *data, int num_values) const { + CPTA_uchar array_data = get_array_data(array); + int stride = _format->get_array(array)->get_stride(); + int element = vertex * stride + data_type->get_start(); + nassertv(element >= 0 && element + data_type->get_total_bytes() <= (int)array_data.size()); + + switch (data_type->get_numeric_type()) { + case qpGeomVertexDataType::NT_uint8: + { + nassertv(num_values <= data_type->get_num_values()); + for (int i = 0; i < num_values; i++) { + int value = *(unsigned char *)&array_data[element]; + element += 1; + data[i] = (float)value / 255.0f; + } + } + break; + + case qpGeomVertexDataType::NT_packed_argb: + { + nassertv(num_values == 4); + unpack_argb(data, *(PN_uint32 *)&array_data[element]); + } + break; + + case qpGeomVertexDataType::NT_float: + nassertv(num_values <= data_type->get_num_values()); + memcpy(data, &array_data[element], num_values * sizeof(PN_float32)); + break; + } +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::get_array_info // Access: Public @@ -546,6 +663,27 @@ unpack_argb(float data[4], unsigned int packed_argb) { data[3] = (float)((packed_argb >> 24) & 0xff) / 255.0f; } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomVertexData::remove_cache_entry +// Access: Private +// Description: Removes a particular entry from the local cache; it +// has already been removed from the cache manager. +// This is only called from GeomVertexCacheManager. +//////////////////////////////////////////////////////////////////// +void qpGeomVertexData:: +remove_cache_entry(const qpGeomVertexFormat *format) const { + // We have to operate on stage 0 of the pipeline, since that's where + // the cache really counts. Because of the multistage pipeline, we + // might not actually have a cache entry there (it might have been + // added to stage 1 instead). No big deal if we don't. + CData *cdata = ((qpGeomVertexData *)this)->_cycler.write_stage(0); + ConvertedCache::iterator ci = cdata->_converted_cache.find(format); + if (ci != cdata->_converted_cache.end()) { + cdata->_converted_cache.erase(ci); + } + ((qpGeomVertexData *)this)->_cycler.release_write_stage(0, cdata); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomVertexData::register_with_read_factory // Access: Public, Static diff --git a/panda/src/gobj/qpgeomVertexData.h b/panda/src/gobj/qpgeomVertexData.h index 48336dc112..5936a9c954 100644 --- a/panda/src/gobj/qpgeomVertexData.h +++ b/panda/src/gobj/qpgeomVertexData.h @@ -77,17 +77,21 @@ PUBLISHED: PTA_uchar modify_array_data(int array); void set_array_data(int array, PTA_uchar array_data); + int get_num_bytes() const; + + CPT(qpGeomVertexData) convert_to(const qpGeomVertexFormat *new_format) const; + + void output(ostream &out) const; + void write(ostream &out, int indent_level = 0) const; + + void clear_cache(); + +public: void set_data(int array, const qpGeomVertexDataType *data_type, int vertex, const float *data, int num_values); void get_data(int array, const qpGeomVertexDataType *data_type, int vertex, float *data, int num_values) const; - PT(qpGeomVertexData) convert_to(const qpGeomVertexFormat *new_format) const; - - void output(ostream &out) const; - void write(ostream &out, int indent_level = 0) const; - -public: bool get_array_info(const InternalName *name, CPTA_uchar &array_data, int &num_components, qpGeomVertexDataType::NumericType &numeric_type, @@ -101,9 +105,19 @@ public: static void unpack_argb(float data[4], unsigned int packed_argb); private: + void remove_cache_entry(const qpGeomVertexFormat *format) const; + +private: + CPT(qpGeomVertexFormat) _format; + typedef pvector Arrays; - CPT(qpGeomVertexFormat) _format; + // We have to use reference-counting pointers here instead of having + // explicit cleanup in the GeomVertexFormat destructor, because the + // cache needs to be stored in the CycleData, which makes accurate + // cleanup more difficult. We use the GeomVertexCacheManager class + // to avoid cache bloat. + typedef pmap ConvertedCache; // This is the data that must be cycled between pipeline stages. class EXPCL_PANDA CData : public CycleData { @@ -116,6 +130,7 @@ private: virtual void fillin(DatagramIterator &scan, BamReader *manager); Arrays _arrays; + ConvertedCache _converted_cache; }; PipelineCycler _cycler; @@ -146,6 +161,8 @@ public: private: static TypeHandle _type_handle; + + friend class qpGeomVertexCacheManager; }; INLINE ostream &operator << (ostream &out, const qpGeomVertexData &obj); diff --git a/panda/src/gobj/qpgeomVertexFormat.cxx b/panda/src/gobj/qpgeomVertexFormat.cxx index 19d4d60799..b834d965d2 100644 --- a/panda/src/gobj/qpgeomVertexFormat.cxx +++ b/panda/src/gobj/qpgeomVertexFormat.cxx @@ -19,6 +19,7 @@ #include "qpgeomVertexFormat.h" #include "qpgeomVertexData.h" #include "qpgeomMunger.h" +#include "mutexHolder.h" #include "indent.h" #include "bamReader.h" #include "bamWriter.h" @@ -435,6 +436,7 @@ do_unregister() { _data_types_by_name.clear(); + MutexHolder holder(_cache_lock); Mungers::iterator mi; for (mi = _mungers.begin(); mi != _mungers.end(); ++mi) { (*mi)->remove_format(this); diff --git a/panda/src/gobj/qpgeomVertexFormat.h b/panda/src/gobj/qpgeomVertexFormat.h index c06985642a..f5b000aa4d 100644 --- a/panda/src/gobj/qpgeomVertexFormat.h +++ b/panda/src/gobj/qpgeomVertexFormat.h @@ -30,6 +30,7 @@ #include "pvector.h" #include "pta_float.h" #include "indirectCompareTo.h" +#include "pmutex.h" class FactoryParams; class qpGeomVertexData; @@ -142,9 +143,13 @@ private: typedef pmap DataTypesByName; DataTypesByName _data_types_by_name; + // This set keeps track of things that need to be told when we + // destruct, and it is protected by the mutex. + Mutex _cache_lock; typedef pset Mungers; Mungers _mungers; + // This is the global registry of all currently-in-use formats. typedef pset > Formats; class Registry { public: diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index 48807ebef5..f1678e4c3e 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -47,6 +47,7 @@ class qpGeomVertexData; class qpGeomTriangles; class qpGeomTristrips; class qpGeomTrifans; +class qpGeomMunger; class PreparedGraphicsObjects; class GraphicsOutput; @@ -130,6 +131,8 @@ public: virtual GeomContext *prepare_geom(Geom *geom)=0; virtual void release_geom(GeomContext *gc)=0; + virtual CPT(qpGeomMunger) get_geom_munger(const RenderState *state)=0; + virtual void set_state_and_transform(const RenderState *state, const TransformState *transform)=0; diff --git a/panda/src/pgraph/cullHandler.I b/panda/src/pgraph/cullHandler.I index 61de5ad3c6..7473b0d39d 100644 --- a/panda/src/pgraph/cullHandler.I +++ b/panda/src/pgraph/cullHandler.I @@ -30,6 +30,6 @@ draw(CullableObject *object, GraphicsStateGuardianBase *gsg) { draw_with_decals(object, gsg); } else { gsg->set_state_and_transform(object->_state, object->_transform); - object->_geom->draw(gsg); + object->draw(gsg); } } diff --git a/panda/src/pgraph/cullHandler.cxx b/panda/src/pgraph/cullHandler.cxx index ec9ac3a336..ab218cc33a 100644 --- a/panda/src/pgraph/cullHandler.cxx +++ b/panda/src/pgraph/cullHandler.cxx @@ -66,7 +66,7 @@ draw_with_decals(CullableObject *object, GraphicsStateGuardianBase *gsg) { CullableObject *base = object; while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) { gsg->set_state_and_transform(base->_state->compose(state), base->_transform); - base->_geom->draw(gsg); + base->draw(gsg); base = base->_next; } @@ -78,7 +78,7 @@ draw_with_decals(CullableObject *object, GraphicsStateGuardianBase *gsg) { CullableObject *decal = base->_next; while (decal != (CullableObject *)NULL) { gsg->set_state_and_transform(decal->_state->compose(state), decal->_transform); - decal->_geom->draw(gsg); + decal->draw(gsg); decal = decal->_next; } } @@ -89,7 +89,7 @@ draw_with_decals(CullableObject *object, GraphicsStateGuardianBase *gsg) { base = object; while (base != (CullableObject *)NULL && base->_geom != (Geom *)NULL) { gsg->set_state_and_transform(base->_state->compose(state), base->_transform); - base->_geom->draw(gsg); + base->draw(gsg); base = base->_next; } diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index 52c1e000f5..07187e4a9f 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -85,6 +85,10 @@ make_next() const { //////////////////////////////////////////////////////////////////// void CullResult:: add_object(CullableObject *object) { + // Munge vertices as needed for the GSG's requirements, and the + // object's current state. + object->munge_vertices(_gsg); + // Check to see if there's a special transparency setting. const RenderState *state = object->_state; nassertv(state != (const RenderState *)NULL); diff --git a/panda/src/pgraph/cullableObject.I b/panda/src/pgraph/cullableObject.I index bd3134c6f1..2ce6ae678b 100644 --- a/panda/src/pgraph/cullableObject.I +++ b/panda/src/pgraph/cullableObject.I @@ -103,6 +103,35 @@ has_decals() const { return (_next != (CullableObject *)NULL); } +//////////////////////////////////////////////////////////////////// +// Function: CullableObject::munge_vertices +// Access: Public +// Description: Gets a GeomMunger from the GSG to transform the +// vertices to meet the GSG's vertex requirements for +// the current state. +//////////////////////////////////////////////////////////////////// +INLINE void CullableObject:: +munge_vertices(GraphicsStateGuardianBase *gsg) { + // Temporary test until the experimental Geom rewrite becomes the + // actual Geom implementation. + if (_geom->is_exact_type(qpGeom::get_class_type())) { + CPT(qpGeomMunger) munger = gsg->get_geom_munger(_state); + _munged_data = munger->munge_data(((const qpGeom *)_geom.p())->get_vertex_data()); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: CullableObject::draw +// Access: Public +// Description: Draws the cullable object on the GSG immediately, in +// the GSG's current state. This should only be called +// from the draw thread. +//////////////////////////////////////////////////////////////////// +INLINE void CullableObject:: +draw(GraphicsStateGuardianBase *gsg) { + _geom->draw(gsg, _munged_data); +} + //////////////////////////////////////////////////////////////////// // Function: CullableObject::operator new // Access: Public diff --git a/panda/src/pgraph/cullableObject.h b/panda/src/pgraph/cullableObject.h index 4b93fe7c1d..b2b02f8a12 100644 --- a/panda/src/pgraph/cullableObject.h +++ b/panda/src/pgraph/cullableObject.h @@ -22,6 +22,9 @@ #include "pandabase.h" #include "geom.h" +#include "qpgeom.h" +#include "qpgeomVertexData.h" +#include "qpgeomMunger.h" #include "renderState.h" #include "transformState.h" #include "pointerTo.h" @@ -51,6 +54,9 @@ public: INLINE bool has_decals() const; + INLINE void munge_vertices(GraphicsStateGuardianBase *gsg); + INLINE void draw(GraphicsStateGuardianBase *gsg); + public: ~CullableObject(); @@ -67,6 +73,7 @@ PUBLISHED: public: CPT(Geom) _geom; + CPT(qpGeomVertexData) _munged_data; CPT(RenderState) _state; CPT(TransformState) _transform; CullableObject *_next; diff --git a/panda/src/pgraph/drawCullHandler.cxx b/panda/src/pgraph/drawCullHandler.cxx index 1191f60f66..1d923afb1a 100644 --- a/panda/src/pgraph/drawCullHandler.cxx +++ b/panda/src/pgraph/drawCullHandler.cxx @@ -33,6 +33,11 @@ //////////////////////////////////////////////////////////////////// void DrawCullHandler:: record_object(CullableObject *object) { + // Munge vertices as needed for the GSG's requirements, and the + // object's current state. + object->munge_vertices(_gsg); + + // And draw the object, then dispense with it. draw(object, _gsg); delete object; } diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index fa51bdc8e7..5973616ded 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -125,6 +125,7 @@ static TimeCollectorProperties time_properties[] = { { 1, "Cull", { 0.0, 1.0, 0.0 }, 1.0 / 30.0 }, { 1, "Cull:Show fps", { 0.5, 0.8, 1.0 } }, { 1, "Cull:Bins", { 0.3, 0.6, 0.3 } }, + { 1, "Cull:Munge", { 0.3, 0.3, 0.9 } }, { 1, "Draw", { 1.0, 0.0, 0.0 }, 1.0 / 30.0 }, { 1, "Draw:Make current", { 0.4, 0.2, 0.6 } }, { 1, "Draw:Copy texture", { 0.2, 0.6, 0.4 } },