clean up caching a bit, multitex in gl

This commit is contained in:
David Rose 2005-03-21 01:39:24 +00:00
parent 720fddb73b
commit 92a93e8af0
19 changed files with 443 additions and 357 deletions

View File

@ -433,6 +433,8 @@ reset() {
_glActiveTexture = (PFNGLACTIVETEXTUREPROC)
get_extension_func(GLPREFIX_QUOTED, "ActiveTexture");
_glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
get_extension_func(GLPREFIX_QUOTED, "ClientActiveTexture");
_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fv");
@ -441,12 +443,15 @@ reset() {
_glActiveTexture = (PFNGLACTIVETEXTUREPROC)
get_extension_func(GLPREFIX_QUOTED, "ActiveTextureARB");
_glClientActiveTexture = (PFNGLACTIVETEXTUREPROC)
get_extension_func(GLPREFIX_QUOTED, "ClientActiveTextureARB");
_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC)
get_extension_func(GLPREFIX_QUOTED, "MultiTexCoord2fvARB");
}
if (_supports_multitexture) {
if (_glActiveTexture == NULL || _glMultiTexCoord2fv == NULL) {
if (_glActiveTexture == NULL || _glClientActiveTexture == NULL ||
_glMultiTexCoord2fv == NULL) {
GLCAT.warning()
<< "Multitexture advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
_supports_multitexture = false;
@ -454,6 +459,7 @@ reset() {
}
if (!_supports_multitexture) {
_glActiveTexture = null_glActiveTexture;
_glClientActiveTexture = null_glActiveTexture;
}
_supports_buffers = false;
@ -707,6 +713,7 @@ reset() {
_current_tex_gen = DCAST(TexGenAttrib, TexGenAttrib::make());
_needs_tex_gen = false;
_tex_gen_modifies_mat = false;
_last_max_stage_index = 0;
_auto_antialias_mode = false;
_render_mode = RenderModeAttrib::M_filled;
@ -2041,12 +2048,13 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
if (geom->get_usage_hint() == qpGeomUsageHint::UH_static &&
_vertex_data->get_usage_hint() == qpGeomUsageHint::UH_static &&
display_lists) {
// If the geom appears to be totally static, try to build it into
// If the geom claims to be totally static, try to build it into
// a display list.
GeomContext *gc = ((qpGeom *)geom)->prepare_now(get_prepared_objects(), this);
nassertr(gc != (GeomContext *)NULL, false);
CLP(GeomContext) *ggc = DCAST(CLP(GeomContext), gc);
if (ggc->_modified == geom->get_modified()) {
UpdateSeq modified = max(geom->get_modified(), _vertex_data->get_modified());
if (ggc->_modified == modified) {
// If it hasn't been modified, just play the display list again.
GLP(CallList)(ggc->_index);
#ifdef DO_PSTATS
@ -2064,7 +2072,7 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
} else {
GLP(NewList)(ggc->_index, GL_COMPILE);
}
ggc->_modified = geom->get_modified();
ggc->_modified = modified;
#ifdef DO_PSTATS
// Count up the number of vertices used by primitives in the Geom,
@ -2115,17 +2123,53 @@ begin_draw_primitives(const qpGeom *geom, const qpGeomVertexData *vertex_data) {
GLP(DisableClientState)(GL_COLOR_ARRAY);
}
if (_vertex_data->get_array_info(InternalName::get_texcoord(),
array_data, num_components, numeric_type,
start, stride)) {
const unsigned char *client_pointer = setup_array_data(array_data);
GLP(TexCoordPointer)(num_components, get_numeric_type(numeric_type),
stride, client_pointer + start);
GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
} else {
GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
// Now set up each of the active texture coordinate stages--or at
// least those for which we're not generating texture coordinates
// automatically.
const Geom::ActiveTextureStages &active_stages =
_current_texture->get_on_stages();
const Geom::NoTexCoordStages &no_texcoords =
_current_tex_gen->get_no_texcoords();
int max_stage_index = (int)active_stages.size();
int stage_index = 0;
while (stage_index < max_stage_index) {
_glClientActiveTexture(GL_TEXTURE0 + stage_index);
TextureStage *stage = active_stages[stage_index];
if (no_texcoords.find(stage) == no_texcoords.end()) {
// This stage is not one of the stages that doesn't need
// texcoords issued for it.
const InternalName *name = stage->get_texcoord_name();
if (_vertex_data->get_array_info(name, array_data, num_components,
numeric_type, start, stride)) {
// The vertex data does have texcoords for this stage.
const unsigned char *client_pointer = setup_array_data(array_data);
GLP(TexCoordPointer)(num_components, get_numeric_type(numeric_type),
stride, client_pointer + start);
GLP(EnableClientState)(GL_TEXTURE_COORD_ARRAY);
} else {
// The vertex data doesn't have texcoords for this stage (even
// though they're needed).
GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
}
} else {
// No texcoords are needed for this stage.
GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
}
++stage_index;
}
// Be sure also to disable any texture stages we had enabled before.
while (stage_index < _last_max_stage_index) {
_glClientActiveTexture(GL_TEXTURE0 + stage_index);
GLP(DisableClientState)(GL_TEXTURE_COORD_ARRAY);
++stage_index;
}
_last_max_stage_index = max_stage_index;
return true;
}
@ -2528,7 +2572,7 @@ setup_array_data(const qpGeomVertexArrayData *data) {
nassertr(vbc != (VertexBufferContext *)NULL, data->get_data());
apply_vertex_buffer(vbc);
// NULL is the OpenGL convention for the first byte of the buffer.
// NULL is the OpenGL convention for the first byte of the buffer object.
return NULL;
}
@ -2659,7 +2703,7 @@ setup_primitive(const qpGeomPrimitive *data) {
nassertr(ibc != (IndexBufferContext *)NULL, data->get_flat_last_vertices());
apply_index_buffer(ibc);
// NULL is the OpenGL convention for the first byte of the buffer.
// NULL is the OpenGL convention for the first byte of the buffer object.
return NULL;
}

View File

@ -318,7 +318,7 @@ protected:
int _pass_number;
bool _auto_rescale_normal;
CLP(GeomContext) *_geom_display_list;
float _num_display_list_verts_before;
int _last_max_stage_index;
int _error_count;
@ -340,6 +340,7 @@ public:
bool _supports_multitexture;
PFNGLACTIVETEXTUREPROC _glActiveTexture;
PFNGLCLIENTACTIVETEXTUREPROC _glClientActiveTexture;
PFNGLMULTITEXCOORD2FVPROC _glMultiTexCoord2fv;
bool _supports_buffers;

View File

@ -89,6 +89,7 @@ modify_primitive(int i) {
CDWriter cdata(_cycler);
nassertr(i >= 0 && i < (int)cdata->_primitives.size(), NULL);
cdata->_got_usage_hint = false;
cdata->_modified = qpGeom::get_next_modified();
return cdata->_primitives[i];
}
@ -117,6 +118,23 @@ set_primitive(int i, const qpGeomPrimitive *primitive) {
}
}
cdata->_primitives[i] = (qpGeomPrimitive *)primitive;
cdata->_modified = qpGeom::get_next_modified();
}
////////////////////////////////////////////////////////////////////
// Function: qpGeom::get_modified
// Access: Published
// Description: Returns a sequence number which is guaranteed to
// change at least every time any of the primitives in
// the Geom is modified, or the set of primitives is
// modified. However, this does not include
// modifications to the vertex data, which should be
// tested separately.
////////////////////////////////////////////////////////////////////
INLINE UpdateSeq qpGeom::
get_modified() const {
CDReader cdata(_cycler);
return cdata->_modified;
}
////////////////////////////////////////////////////////////////////
@ -141,7 +159,8 @@ CData(const qpGeom::CData &copy) :
_data(copy._data),
_primitives(copy._primitives),
_usage_hint(copy._usage_hint),
_got_usage_hint(copy._got_usage_hint)
_got_usage_hint(copy._got_usage_hint),
_modified(copy._modified)
{
}

View File

@ -157,6 +157,7 @@ add_primitive(const qpGeomPrimitive *primitive) {
if (cdata->_got_usage_hint) {
cdata->_usage_hint = min(cdata->_usage_hint, primitive->get_usage_hint());
}
cdata->_modified = qpGeom::get_next_modified();
}
////////////////////////////////////////////////////////////////////
@ -176,6 +177,7 @@ remove_primitive(int i) {
cdata->_got_usage_hint = false;
}
cdata->_primitives.erase(cdata->_primitives.begin() + i);
cdata->_modified = qpGeom::get_next_modified();
}
////////////////////////////////////////////////////////////////////
@ -214,40 +216,6 @@ get_num_bytes() const {
return num_bytes;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeom::get_modified
// Access: Published
// Description: Returns the maximum UpdateSeq of all the Geom's
// individual primitives and vertex arrays. This,
// therefore, will change only when any part of the Geom
// changes.
////////////////////////////////////////////////////////////////////
UpdateSeq qpGeom::
get_modified() const {
CDReader cdata(_cycler);
UpdateSeq seq;
Primitives::const_iterator pi;
for (pi = cdata->_primitives.begin();
pi != cdata->_primitives.end();
++pi) {
UpdateSeq pseq = (*pi)->get_modified();
if (seq < pseq) {
seq = pseq;
}
}
int num_arrays = cdata->_data->get_num_arrays();
for (int i = 0; i < num_arrays; ++i) {
UpdateSeq aseq = cdata->_data->get_array(i)->get_modified();
if (seq < aseq) {
seq = aseq;
}
}
return seq;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeom::munge_geom
// Access: Published

View File

@ -78,7 +78,7 @@ PUBLISHED:
void clear_primitives();
int get_num_bytes() const;
UpdateSeq get_modified() const;
INLINE UpdateSeq get_modified() const;
void munge_geom(const qpGeomMunger *munger,
CPT(qpGeom) &result, CPT(qpGeomVertexData) &data) const;
@ -129,6 +129,7 @@ private:
Primitives _primitives;
qpGeomUsageHint::UsageHint _usage_hint;
bool _got_usage_hint;
UpdateSeq _modified;
MungedCache _munged_cache;
};

View File

@ -197,6 +197,11 @@ get_ends() const {
INLINE CPTA_ushort qpGeomPrimitive::
get_mins() const {
CDReader cdata(_cycler);
if (!cdata->_got_minmax) {
CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
return cdataw->_mins;
}
return cdata->_mins;
}
@ -214,6 +219,11 @@ get_mins() const {
INLINE CPTA_ushort qpGeomPrimitive::
get_maxs() const {
CDReader cdata(_cycler);
if (!cdata->_got_minmax) {
CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
return cdataw->_maxs;
}
return cdata->_maxs;
}
@ -252,7 +262,9 @@ INLINE int qpGeomPrimitive::
get_min_vertex() const {
CDReader cdata(_cycler);
if (!cdata->_got_minmax) {
((qpGeomPrimitive *)this)->recompute_minmax();
CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
return cdataw->_min_vertex;
}
return cdata->_min_vertex;
}
@ -265,12 +277,9 @@ get_min_vertex() const {
////////////////////////////////////////////////////////////////////
INLINE int qpGeomPrimitive::
get_min_vertex(int i) const {
CDReader cdata(_cycler);
if (!cdata->_got_minmax) {
((qpGeomPrimitive *)this)->recompute_minmax();
}
nassertr(i >= 0 && i < (int)cdata->_mins.size(), -1);
return cdata->_mins[i];
CPTA_ushort mins = get_mins();
nassertr(i >= 0 && i < (int)mins.size(), -1);
return mins[i];
}
////////////////////////////////////////////////////////////////////
@ -283,7 +292,9 @@ INLINE int qpGeomPrimitive::
get_max_vertex() const {
CDReader cdata(_cycler);
if (!cdata->_got_minmax) {
((qpGeomPrimitive *)this)->recompute_minmax();
CDWriter cdataw(((qpGeomPrimitive *)this)->_cycler, cdata);
((qpGeomPrimitive *)this)->recompute_minmax(cdataw);
return cdataw->_max_vertex;
}
return cdata->_max_vertex;
}
@ -296,12 +307,9 @@ get_max_vertex() const {
////////////////////////////////////////////////////////////////////
INLINE int qpGeomPrimitive::
get_max_vertex(int i) const {
CDReader cdata(_cycler);
if (!cdata->_got_minmax) {
((qpGeomPrimitive *)this)->recompute_minmax();
}
nassertr(i >= 0 && i < (int)cdata->_maxs.size(), -1);
return cdata->_maxs[i];
CPTA_ushort maxs = get_maxs();
nassertr(i >= 0 && i < (int)maxs.size(), -1);
return maxs[i];
}
////////////////////////////////////////////////////////////////////

View File

@ -804,9 +804,7 @@ remove_cache_entry() const {
// necessary.
////////////////////////////////////////////////////////////////////
void qpGeomPrimitive::
recompute_minmax() {
CDWriter cdata(_cycler);
recompute_minmax(qpGeomPrimitive::CDWriter &cdata) {
if (cdata->_vertices.empty()) {
cdata->_min_vertex = 0;
cdata->_max_vertex = 0;

View File

@ -77,8 +77,8 @@ PUBLISHED:
SM_smooth,
// SM_uniform: all vertices across all faces have the same colors
// and normals. It doesn't matter which ShadeModelAttrib mode is
// used to render this primitive.
// and normals. It doesn't really matter which ShadeModelAttrib
// mode is used to render this primitive.
SM_uniform,
// SM_flat_(first,last)_vertex: each face within the primitive
@ -208,7 +208,7 @@ private:
typedef CycleDataReader<CData> CDReader;
typedef CycleDataWriter<CData> CDWriter;
void recompute_minmax();
void recompute_minmax(CDWriter &cdata);
public:
virtual void write_datagram(BamWriter *manager, Datagram &dg);

View File

@ -101,36 +101,6 @@ remove_primitive(const qpGeomPrimitive *primitive) {
remove_entry(Entry(primitive, 0));
}
////////////////////////////////////////////////////////////////////
// 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 *modifier,
int result_size) {
record_entry(Entry(source, modifier, result_size));
}
////////////////////////////////////////////////////////////////////
// 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 *modifier) {
remove_entry(Entry(source, modifier, 0));
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexCacheManager::record_geom
// Access: Private
@ -139,8 +109,8 @@ remove_data(const qpGeomVertexData *source,
// only be called by Geom.
//
// The cache manager will not hold a reference on the
// Geom pointer; if it destructs, it should
// call remove_geom() to remove itself from the cache.
// Geom pointer; if it destructs, it should call
// remove_geom() to remove itself from the cache.
////////////////////////////////////////////////////////////////////
INLINE void qpGeomVertexCacheManager::
record_geom(const qpGeom *source, const qpGeomMunger *modifier,
@ -153,13 +123,54 @@ record_geom(const qpGeom *source, const qpGeomMunger *modifier,
// 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 Geom.
// called by GeomVertexData.
////////////////////////////////////////////////////////////////////
INLINE void qpGeomVertexCacheManager::
remove_geom(const qpGeom *source, const qpGeomMunger *modifier) {
remove_entry(Entry(source, modifier, 0));
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexCacheManager::dequeue_entry
// Access: Private
// Description: Removes an Entry record from the doubly-linked list.
////////////////////////////////////////////////////////////////////
INLINE void qpGeomVertexCacheManager::
dequeue_entry(qpGeomVertexCacheManager::Entry *entry) {
nassertv(entry->_prev->_next == entry &&
entry->_next->_prev == entry);
entry->_prev->_next = entry->_next;
entry->_next->_prev = entry->_prev;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexCacheManager::enqueue_entry
// Access: Private
// Description: Adds an Entry record to the tail of the doubly-linked
// list.
////////////////////////////////////////////////////////////////////
INLINE void qpGeomVertexCacheManager::
enqueue_entry(qpGeomVertexCacheManager::Entry *entry) {
nassertv(_list->_prev->_next == _list &&
_list->_next->_prev == _list);
entry->_prev = _list->_prev;
entry->_next = _list;
entry->_prev->_next = entry;
_list->_prev = entry;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexCacheManager::Entry::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpGeomVertexCacheManager::Entry::
Entry() :
_cache_type(CT_none),
_result_size(0)
{
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexCacheManager::Entry::Constructor
// Access: Public
@ -193,22 +204,8 @@ Entry(const qpGeomPrimitive *primitive, int result_size) :
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpGeomVertexCacheManager::Entry::
Entry(const qpGeomVertexData *source, const qpGeomVertexFormat *modifier,
Entry(const qpGeom *source, const qpGeomMunger *modifier,
int result_size) :
_cache_type(CT_data),
_result_size(result_size)
{
_u._data._source = source;
_u._data._modifier = modifier;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexCacheManager::Entry::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE qpGeomVertexCacheManager::Entry::
Entry(const qpGeom *source, const qpGeomMunger *modifier, int result_size) :
_cache_type(CT_geom),
_result_size(result_size)
{
@ -269,19 +266,22 @@ INLINE qpGeomVertexCacheManager::Entry::
////////////////////////////////////////////////////////////////////
INLINE bool qpGeomVertexCacheManager::Entry::
operator < (const qpGeomVertexCacheManager::Entry &other) const {
if (_cache_type != other._cache_type) {
return (int)_cache_type < (int)other._cache_type;
}
switch (_cache_type) {
case CT_none:
// We shouldn't be adding the end-of-list token to the index.
nassertr(false, false);
return false;
case CT_munger:
return _u._munger < other._u._munger;
case CT_primitive:
return _u._primitive < other._u._primitive;
case CT_data:
if (_u._data._source != other._u._data._source) {
return _u._data._source < other._u._data._source;
}
return _u._data._modifier < other._u._data._modifier;
case CT_geom:
if (_u._geom._source != other._u._geom._source) {
return _u._geom._source < other._u._geom._source;
@ -289,7 +289,7 @@ operator < (const qpGeomVertexCacheManager::Entry &other) const {
return _u._geom._modifier < other._u._geom._modifier;
}
return 0;
return false;
}
INLINE ostream &

View File

@ -30,6 +30,9 @@ qpGeomVertexCacheManager::
qpGeomVertexCacheManager() :
_total_size(0)
{
_list = new Entry;
_list->_next = _list;
_list->_prev = _list;
}
////////////////////////////////////////////////////////////////////
@ -39,6 +42,8 @@ qpGeomVertexCacheManager() :
////////////////////////////////////////////////////////////////////
qpGeomVertexCacheManager::
~qpGeomVertexCacheManager() {
// Shouldn't be deleting this global object.
nassertv(false);
}
////////////////////////////////////////////////////////////////////
@ -61,80 +66,77 @@ get_global_ptr() {
// cache hit for a previous entry in the cache.
////////////////////////////////////////////////////////////////////
void qpGeomVertexCacheManager::
record_entry(const qpGeomVertexCacheManager::Entry &entry) {
record_entry(const qpGeomVertexCacheManager::Entry &const_entry) {
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);
EntriesIndex::iterator ii = _entries_index.find((Entry *)&const_entry);
if (ii != _entries_index.end()) {
// We already had this entry in the cache. Refresh it.
if (gobj_cat.is_spam()) {
gobj_cat.spam()
<< "refreshing cache entry: " << entry << "\n";
<< "refreshing cache entry: " << const_entry << "\n";
}
// Move the previous cache entry to the tail of the list.
Entry *entry = (*ii);
dequeue_entry(entry);
enqueue_entry(entry);
_total_size += (const_entry._result_size - entry->_result_size);
entry->_result_size = const_entry._result_size;
} else {
// There was no such entry already in the cache. Add it.
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "recording cache entry: " << entry << ", total_size = "
<< _total_size + entry._result_size << "\n";
<< "recording cache entry: " << const_entry << ", total_size = "
<< _total_size + const_entry._result_size << "\n";
}
Entry *entry = new Entry(const_entry);
enqueue_entry(entry);
_total_size += entry->_result_size;
// Also record an index entry.
bool inserted = _entries_index.insert(entry).second;
nassertv(inserted);
}
// 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) {
nassertv(!_entries.empty());
ei = _entries.begin();
entry_pointer = &(*ei);
Entry *entry = _list->_next;
nassertv(entry != _list);
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "cache total_size = " << _total_size << ", max_size = "
<< max_size << ", removing " << *entry_pointer << "\n";
<< max_size << ", removing " << *entry << "\n";
}
ii = _entries_index.find(entry_pointer);
ii = _entries_index.find(entry);
nassertv(ii != _entries_index.end());
switch (entry_pointer->_cache_type) {
case CT_data:
entry_pointer->_u._data._source->remove_cache_entry
(entry_pointer->_u._data._modifier);
break;
switch (entry->_cache_type) {
case CT_primitive:
entry_pointer->_u._primitive->remove_cache_entry();
entry->_u._primitive->remove_cache_entry();
break;
case CT_geom:
entry_pointer->_u._geom._source->remove_cache_entry
(entry_pointer->_u._geom._modifier);
entry->_u._geom._source->remove_cache_entry
(entry->_u._geom._modifier);
break;
default:
break;
}
_total_size -= entry_pointer->_result_size;
_total_size -= entry->_result_size;
_entries_index.erase(ii);
_entries.erase(ei);
dequeue_entry(entry);
delete entry;
}
}
@ -145,19 +147,20 @@ record_entry(const qpGeomVertexCacheManager::Entry &entry) {
// Quietly ignores it if it is not.
////////////////////////////////////////////////////////////////////
void qpGeomVertexCacheManager::
remove_entry(const qpGeomVertexCacheManager::Entry &entry) {
remove_entry(const qpGeomVertexCacheManager::Entry &const_entry) {
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "remove_entry(" << entry << ")\n";
<< "remove_entry(" << const_entry << ")\n";
}
MutexHolder holder(_lock);
EntriesIndex::iterator ii = _entries_index.find(&entry);
EntriesIndex::iterator ii = _entries_index.find((Entry *)&const_entry);
if (ii != _entries_index.end()) {
Entries::iterator ei = (*ii).second;
_total_size -= (*ei)._result_size;
Entry *entry = (*ii);
_total_size -= entry->_result_size;
_entries_index.erase(ii);
_entries.erase(ei);
dequeue_entry(entry);
delete entry;
}
}
@ -170,6 +173,10 @@ void qpGeomVertexCacheManager::Entry::
output(ostream &out) const {
out << "[ ";
switch (_cache_type) {
case CT_none:
out << "end-of-list token";
break;
case CT_munger:
out << "munger " << (void *)_u._munger << ":"
<< _u._munger->get_ref_count();
@ -179,11 +186,6 @@ output(ostream &out) const {
out << "primitive " << (void *)_u._primitive;
break;
case CT_data:
out << "data " << (void *)_u._data._source << ", "
<< (void *)_u._data._modifier;
break;
case CT_geom:
out << "geom " << (void *)_u._geom._source << ", "
<< (void *)_u._geom._modifier;

View File

@ -74,18 +74,17 @@ private:
INLINE void record_primitive(const qpGeomPrimitive *primitive,
int result_size);
INLINE void remove_primitive(const qpGeomPrimitive *primitive);
INLINE void record_data(const qpGeomVertexData *source,
const qpGeomVertexFormat *modifier,
int result_size);
INLINE void remove_data(const qpGeomVertexData *source,
const qpGeomVertexFormat *modifier);
INLINE void record_geom(const qpGeom *source,
const qpGeomMunger *modifier,
int result_size);
INLINE void remove_geom(const qpGeom *geom, const qpGeomMunger *modifier);
INLINE void remove_geom(const qpGeom *source,
const qpGeomMunger *modifier);
void record_entry(const Entry &entry);
void remove_entry(const Entry &entry);
void record_entry(const Entry &const_entry);
void remove_entry(const Entry &const_entry);
INLINE void dequeue_entry(Entry *entry);
INLINE void enqueue_entry(Entry *entry);
private:
// This mutex protects all operations on this object.
@ -94,9 +93,9 @@ private:
int _total_size;
enum CacheType {
CT_none,
CT_munger,
CT_primitive,
CT_data,
CT_geom,
};
@ -109,10 +108,9 @@ public:
// this C-style polymorphism.
class Entry {
public:
INLINE Entry();
INLINE Entry(const qpGeomMunger *munger, int result_size);
INLINE Entry(const qpGeomPrimitive *primitive, int result_size);
INLINE Entry(const qpGeomVertexData *source,
const qpGeomVertexFormat *modifier, int result_size);
INLINE Entry(const qpGeom *source, const qpGeomMunger *modifier,
int result_size);
INLINE Entry(const Entry &copy);
@ -127,32 +125,38 @@ public:
union {
const qpGeomMunger *_munger;
const qpGeomPrimitive *_primitive;
struct {
const qpGeomVertexData *_source;
const qpGeomVertexFormat *_modifier;
} _data;
struct {
const qpGeom *_source;
const qpGeomMunger *_modifier;
} _geom;
} _u;
Entry *_prev, *_next;
};
private:
// 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;
// We maintain a doubly-linked list to keep the cache entries in
// least-recently-used order: the items at the head of the list are
// ready to be flushed. We use our own doubly-linked list instead
// of an STL list, just so we can avoid a tiny bit of overhead,
// especially with managing the pointer to the entry in
// _entries_index.
// The tail and the head of the list are both kept by the _prev and
// _next pointers, respectively, within the following object, which
// always exists solely to keep a handle to the list. Keeping a
// token of the list this way avoids special cases for an empty
// list.
Entry *_list;
// And this indexes into the above list, for fast lookup.
typedef pmap<const Entry *, Entries::iterator, IndirectLess<Entry> > EntriesIndex;
typedef pset<Entry *, IndirectLess<Entry> > EntriesIndex;
EntriesIndex _entries_index;
static qpGeomVertexCacheManager *_global_ptr;
friend class qpGeomMunger;
friend class qpGeomPrimitive;
friend class qpGeomVertexData;
friend class qpGeom;
};

View File

@ -91,6 +91,19 @@ get_array(int i) const {
return cdata->_arrays[i];
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexData::get_modified
// Access: Published
// Description: Returns a sequence number which is guaranteed to
// change at least every time the vertex data is
// modified.
////////////////////////////////////////////////////////////////////
INLINE UpdateSeq qpGeomVertexData::
get_modified() const {
CDReader cdata(_cycler);
return cdata->_modified;
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexData::CData::Constructor
// Access: Public
@ -107,7 +120,8 @@ CData() {
////////////////////////////////////////////////////////////////////
INLINE qpGeomVertexData::CData::
CData(const qpGeomVertexData::CData &copy) :
_arrays(copy._arrays)
_arrays(copy._arrays),
_modified(copy._modified)
{
}

View File

@ -17,7 +17,6 @@
////////////////////////////////////////////////////////////////////
#include "qpgeomVertexData.h"
#include "qpgeomVertexCacheManager.h"
#include "pStatTimer.h"
#include "bamReader.h"
#include "bamWriter.h"
@ -93,25 +92,6 @@ 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);
}
}
}
////////////////////////////////////////////////////////////////////
@ -144,7 +124,6 @@ get_num_vertices() const {
////////////////////////////////////////////////////////////////////
void qpGeomVertexData::
clear_vertices() {
clear_cache();
CDWriter cdata(_cycler);
nassertv(_format->get_num_arrays() == (int)cdata->_arrays.size());
@ -154,6 +133,7 @@ clear_vertices() {
++ai) {
(*ai)->clear_vertices();
}
cdata->_modified = qpGeom::get_next_modified();
}
////////////////////////////////////////////////////////////////////
@ -170,13 +150,13 @@ modify_array(int i) {
// Perform copy-on-write: if the reference count on the vertex data
// is greater than 1, assume some other GeomVertexData has the same
// pointer, so make a copy of it first.
clear_cache();
CDWriter cdata(_cycler);
nassertr(i >= 0 && i < (int)cdata->_arrays.size(), NULL);
if (cdata->_arrays[i]->get_ref_count() > 1) {
cdata->_arrays[i] = new qpGeomVertexArrayData(*cdata->_arrays[i]);
}
cdata->_modified = qpGeom::get_next_modified();
return cdata->_arrays[i];
}
@ -191,10 +171,10 @@ modify_array(int i) {
////////////////////////////////////////////////////////////////////
void qpGeomVertexData::
set_array(int i, const qpGeomVertexArrayData *array) {
clear_cache();
CDWriter cdata(_cycler);
nassertv(i >= 0 && i < (int)cdata->_arrays.size());
cdata->_arrays[i] = (qpGeomVertexArrayData *)array;
cdata->_modified = qpGeom::get_next_modified();
}
////////////////////////////////////////////////////////////////////
@ -232,28 +212,6 @@ convert_to(const qpGeomVertexFormat *new_format) const {
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();
@ -326,19 +284,6 @@ 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;
}
@ -362,28 +307,6 @@ 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
@ -695,27 +618,6 @@ 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 *modifier) 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(modifier);
if (ci != cdata->_converted_cache.end()) {
cdata->_converted_cache.erase(ci);
}
((qpGeomVertexData *)this)->_cycler.release_write_stage(0, cdata);
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomVertexData::do_set_num_vertices
// Access: Private
@ -728,13 +630,18 @@ do_set_num_vertices(int n, qpGeomVertexData::CDWriter &cdata) {
bool any_changed = false;
for (size_t i = 0; i < cdata->_arrays.size(); i++) {
if (cdata->_arrays[i]->set_num_vertices(n)) {
if (cdata->_arrays[i]->get_num_vertices() != n) {
// Copy-on-write.
if (cdata->_arrays[i]->get_ref_count() > 1) {
cdata->_arrays[i] = new qpGeomVertexArrayData(*cdata->_arrays[i]);
}
cdata->_arrays[i]->set_num_vertices(n);
any_changed = true;
}
}
if (any_changed) {
clear_cache();
cdata->_modified = qpGeom::get_next_modified();
}
return any_changed;

View File

@ -82,14 +82,13 @@ PUBLISHED:
void set_array(int i, const qpGeomVertexArrayData *array);
int get_num_bytes() const;
INLINE UpdateSeq get_modified() 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);
@ -109,22 +108,12 @@ public:
static unsigned int pack_argb(const float data[4]);
static void unpack_argb(float data[4], unsigned int packed_argb);
private:
void remove_cache_entry(const qpGeomVertexFormat *modifier) const;
private:
CPT(qpGeomVertexFormat) _format;
qpGeomUsageHint::UsageHint _usage_hint;
typedef pvector< PT(qpGeomVertexArrayData) > Arrays;
// 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 {
public:
@ -136,7 +125,7 @@ private:
virtual void fillin(DatagramIterator &scan, BamReader *manager);
Arrays _arrays;
ConvertedCache _converted_cache;
UpdateSeq _modified;
};
PipelineCycler<CData> _cycler;
@ -172,8 +161,6 @@ public:
private:
static TypeHandle _type_handle;
friend class qpGeomVertexCacheManager;
};
INLINE ostream &operator << (ostream &out, const qpGeomVertexData &obj);

View File

@ -155,21 +155,79 @@ get_bin_index() const {
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::is_flat_shaded
// Function: RenderState::get_color
// Access: Published
// Description: Returns true if this RenderState specifies a flat
// shading model (that is, per-vertex color applies to
// entire triangles), or false if it specifies a smooth
// shading model or does not specify.
// Description: This function is provided as an optimization, to
// speed up the render-time checking for the existance
// of a ColorAttrib on this state. It returns a
// pointer to the ColorAttrib, if there is one, or
// NULL if there is not.
////////////////////////////////////////////////////////////////////
INLINE bool RenderState::
is_flat_shaded() const {
if ((_flags & F_checked_flat_shaded) == 0) {
INLINE const ColorAttrib *RenderState::
get_color() const {
if ((_flags & F_checked_color) == 0) {
// We pretend this function is const, even though it transparently
// modifies the internal flat_shaded cache.
((RenderState *)this)->determine_flat_shaded();
// modifies the internal color cache.
((RenderState *)this)->determine_color();
}
return ((_flags & F_flat_shaded) != 0);
return _color;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::get_color_scale
// Access: Published
// Description: This function is provided as an optimization, to
// speed up the render-time checking for the existance
// of a ColorScaleAttrib on this state. It returns a
// pointer to the ColorScaleAttrib, if there is one, or
// NULL if there is not.
////////////////////////////////////////////////////////////////////
INLINE const ColorScaleAttrib *RenderState::
get_color_scale() const {
if ((_flags & F_checked_color_scale) == 0) {
// We pretend this function is const, even though it transparently
// modifies the internal color_scale cache.
((RenderState *)this)->determine_color_scale();
}
return _color_scale;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::get_texture
// Access: Published
// Description: This function is provided as an optimization, to
// speed up the render-time checking for the existance
// of a TextureAttrib on this state. It returns a
// pointer to the TextureAttrib, if there is one, or
// NULL if there is not.
////////////////////////////////////////////////////////////////////
INLINE const TextureAttrib *RenderState::
get_texture() const {
if ((_flags & F_checked_texture) == 0) {
// We pretend this function is const, even though it transparently
// modifies the internal texture cache.
((RenderState *)this)->determine_texture();
}
return _texture;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::get_tex_gen
// Access: Published
// Description: This function is provided as an optimization, to
// speed up the render-time checking for the existance
// of a TexGenAttrib on this state. It returns a
// pointer to the TexGenAttrib, if there is one, or
// NULL if there is not.
////////////////////////////////////////////////////////////////////
INLINE const TexGenAttrib *RenderState::
get_tex_gen() const {
if ((_flags & F_checked_tex_gen) == 0) {
// We pretend this function is const, even though it transparently
// modifies the internal tex_gen cache.
((RenderState *)this)->determine_tex_gen();
}
return _tex_gen;
}
////////////////////////////////////////////////////////////////////

View File

@ -22,7 +22,10 @@
#include "cullBinManager.h"
#include "fogAttrib.h"
#include "transparencyAttrib.h"
#include "shadeModelAttrib.h"
#include "colorAttrib.h"
#include "colorScaleAttrib.h"
#include "textureAttrib.h"
#include "texGenAttrib.h"
#include "pStatTimer.h"
#include "config_pgraph.h"
#include "bamReader.h"
@ -1513,23 +1516,63 @@ determine_transparency() {
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::determine_flat_shaded
// Function: RenderState::determine_color
// Access: Private
// Description: This is the private implementation of
// is_flat_shaded().
// Description: This is the private implementation of get_color().
////////////////////////////////////////////////////////////////////
void RenderState::
determine_flat_shaded() {
const RenderAttrib *attrib =
get_attrib(ShadeModelAttrib::get_class_type());
determine_color() {
const RenderAttrib *attrib = get_attrib(ColorAttrib::get_class_type());
_color = (const ColorAttrib *)NULL;
if (attrib != (const RenderAttrib *)NULL) {
const ShadeModelAttrib *sma = DCAST(ShadeModelAttrib, attrib);
if (sma->get_mode() == ShadeModelAttrib::M_flat) {
_flags |= F_flat_shaded;
}
_color = DCAST(ColorAttrib, attrib);
}
_flags |= F_checked_color;
}
_flags |= F_checked_flat_shaded;
////////////////////////////////////////////////////////////////////
// Function: RenderState::determine_color_scale
// Access: Private
// Description: This is the private implementation of get_color_scale().
////////////////////////////////////////////////////////////////////
void RenderState::
determine_color_scale() {
const RenderAttrib *attrib = get_attrib(ColorScaleAttrib::get_class_type());
_color_scale = (const ColorScaleAttrib *)NULL;
if (attrib != (const RenderAttrib *)NULL) {
_color_scale = DCAST(ColorScaleAttrib, attrib);
}
_flags |= F_checked_color_scale;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::determine_texture
// Access: Private
// Description: This is the private implementation of get_texture().
////////////////////////////////////////////////////////////////////
void RenderState::
determine_texture() {
const RenderAttrib *attrib = get_attrib(TextureAttrib::get_class_type());
_texture = (const TextureAttrib *)NULL;
if (attrib != (const RenderAttrib *)NULL) {
_texture = DCAST(TextureAttrib, attrib);
}
_flags |= F_checked_texture;
}
////////////////////////////////////////////////////////////////////
// Function: RenderState::determine_tex_gen
// Access: Private
// Description: This is the private implementation of get_tex_gen().
////////////////////////////////////////////////////////////////////
void RenderState::
determine_tex_gen() {
const RenderAttrib *attrib = get_attrib(TexGenAttrib::get_class_type());
_tex_gen = (const TexGenAttrib *)NULL;
if (attrib != (const RenderAttrib *)NULL) {
_tex_gen = DCAST(TexGenAttrib, attrib);
}
_flags |= F_checked_tex_gen;
}
////////////////////////////////////////////////////////////////////

View File

@ -32,6 +32,10 @@ class GraphicsStateGuardianBase;
class FogAttrib;
class CullBinAttrib;
class TransparencyAttrib;
class ColorAttrib;
class ColorScaleAttrib;
class TextureAttrib;
class TexGenAttrib;
class FactoryParams;
////////////////////////////////////////////////////////////////////
@ -114,7 +118,10 @@ PUBLISHED:
INLINE const CullBinAttrib *get_bin() const;
INLINE const TransparencyAttrib *get_transparency() const;
INLINE int get_bin_index() const;
INLINE bool is_flat_shaded() const;
INLINE const ColorAttrib *get_color() const;
INLINE const ColorScaleAttrib *get_color_scale() const;
INLINE const TextureAttrib *get_texture() const;
INLINE const TexGenAttrib *get_tex_gen() const;
public:
CPT(RenderState) issue_delta_modify(const RenderState *other,
@ -151,7 +158,10 @@ private:
void determine_fog();
void determine_bin();
void determine_transparency();
void determine_flat_shaded();
void determine_color();
void determine_color_scale();
void determine_texture();
void determine_tex_gen();
INLINE void set_destructing();
INLINE bool is_destructing() const;
@ -225,14 +235,20 @@ private:
const FogAttrib *_fog;
const CullBinAttrib *_bin;
const TransparencyAttrib *_transparency;
const ColorAttrib *_color;
const ColorScaleAttrib *_color_scale;
const TextureAttrib *_texture;
const TexGenAttrib *_tex_gen;
enum Flags {
F_checked_bin_index = 0x0001,
F_checked_fog = 0x0002,
F_checked_bin = 0x0004,
F_checked_transparency = 0x0008,
F_checked_flat_shaded = 0x0010,
F_flat_shaded = 0x0020,
F_checked_color = 0x0010,
F_checked_color_scale = 0x0020,
F_checked_texture = 0x0040,
F_checked_tex_gen = 0x0080,
F_is_destructing = 0x8000,
};
unsigned short _flags;

View File

@ -67,8 +67,9 @@ fresh() {
// Description:
////////////////////////////////////////////////////////////////////
INLINE UpdateSeq::
UpdateSeq(const UpdateSeq &copy) {
_seq = copy._seq;
UpdateSeq(const UpdateSeq &copy) :
_seq(copy._seq)
{
}
////////////////////////////////////////////////////////////////////
@ -132,15 +133,8 @@ is_fresh() const {
////////////////////////////////////////////////////////////////////
INLINE bool UpdateSeq::
is_special() const {
switch (_seq) {
case (unsigned int)SC_initial:
case (unsigned int)SC_old:
case (unsigned int)SC_fresh:
return true;
default:
return false;
}
// This relies on the assumption that (~0 + 1) == 0.
return ((_seq + 1) <= 2);
}
////////////////////////////////////////////////////////////////////
@ -190,6 +184,26 @@ operator <= (const UpdateSeq &other) const {
return (*this) == other || (*this) < other;
}
////////////////////////////////////////////////////////////////////
// Function: UpdateSeq::Comparison operator
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool UpdateSeq::
operator > (const UpdateSeq &other) const {
return (other < (*this));
}
////////////////////////////////////////////////////////////////////
// Function: UpdateSeq::Comparison operator
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool UpdateSeq::
operator >= (const UpdateSeq &other) const {
return (other <= (*this));
}
////////////////////////////////////////////////////////////////////
// Function: UpdateSeq::Preincrement operator
// Access: Published

View File

@ -61,6 +61,8 @@ PUBLISHED:
INLINE bool operator != (const UpdateSeq &other) const;
INLINE bool operator < (const UpdateSeq &other) const;
INLINE bool operator <= (const UpdateSeq &other) const;
INLINE bool operator > (const UpdateSeq &other) const;
INLINE bool operator >= (const UpdateSeq &other) const;
INLINE UpdateSeq operator ++ ();
INLINE UpdateSeq operator ++ (int);
@ -71,7 +73,7 @@ private:
enum SpecialCases {
SC_initial = 0,
SC_old = 1,
SC_fresh = ~0,
SC_fresh = ~(unsigned int)0,
};
unsigned int _seq;