cache new geom munging

This commit is contained in:
David Rose 2005-03-12 01:17:56 +00:00
parent 0768cdd95c
commit 219f16c5de
40 changed files with 1053 additions and 214 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;

View File

@ -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();
}

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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 \

View File

@ -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."));

View File

@ -59,6 +59,8 @@ extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> textures_power_2;
extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> 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;

View File

@ -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();
}

View File

@ -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:

View File

@ -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();

View File

@ -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();

View File

@ -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"

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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 &copy) :
////////////////////////////////////////////////////////////////////
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

View File

@ -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<CData> _cycler;
@ -149,6 +161,7 @@ private:
static TypeHandle _type_handle;
friend class qpGeom;
friend class qpGeomVertexCacheManager;
};
#include "qpgeomPrimitive.I"

View File

@ -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

View File

@ -34,11 +34,12 @@ PUBLISHED:
qpGeomTrifans(const qpGeomTrifans &copy);
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();

View File

@ -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

View File

@ -34,11 +34,12 @@ PUBLISHED:
qpGeomTristrips(const qpGeomTristrips &copy);
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();

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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<Entry> Entries;
Entries _entries;
// And this indexes into the above list, for fast lookup.
typedef pmap<const Entry *, Entries::iterator, IndirectLess<Entry> > EntriesIndex;
EntriesIndex _entries_index;
static qpGeomVertexCacheManager *_global_ptr;
friend class qpGeomMunger;
friend class qpGeomPrimitive;
friend class qpGeomVertexData;
};
#include "qpgeomVertexCacheManager.I"
#endif

View File

@ -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 &copy) {
////////////////////////////////////////////////////////////////////
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<int> 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

View File

@ -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<PTA_uchar> 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<CPT(qpGeomVertexFormat), CPT(qpGeomVertexData) > 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<CData> _cycler;
@ -146,6 +161,8 @@ public:
private:
static TypeHandle _type_handle;
friend class qpGeomVertexCacheManager;
};
INLINE ostream &operator << (ostream &out, const qpGeomVertexData &obj);

View File

@ -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);

View File

@ -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<const InternalName *, DataTypeRecord> 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<qpGeomMunger *> Mungers;
Mungers _mungers;
// This is the global registry of all currently-in-use formats.
typedef pset<qpGeomVertexFormat *, IndirectCompareTo<qpGeomVertexFormat> > Formats;
class Registry {
public:

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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 } },