Further NodePath::find() optimizations

This commit is contained in:
David Rose 2004-06-14 19:02:29 +00:00
parent 1898df8d54
commit 90bf34ddcf
12 changed files with 289 additions and 220 deletions

View File

@ -41,7 +41,6 @@
depthWriteAttrib.I depthWriteAttrib.h \
directionalLight.I directionalLight.h \
drawCullHandler.I drawCullHandler.h \
findApproxLevel.I findApproxLevel.h \
findApproxLevelEntry.I findApproxLevelEntry.h \
findApproxPath.I findApproxPath.h \
fog.I fog.h \
@ -129,7 +128,6 @@
depthWriteAttrib.cxx \
directionalLight.cxx \
drawCullHandler.cxx \
findApproxLevel.cxx \
findApproxLevelEntry.cxx \
findApproxPath.cxx \
fog.cxx \
@ -267,7 +265,6 @@
workingNodePath.I workingNodePath.h
// No need to install these.
// findApproxLevel.I findApproxLevel.h \
// findApproxLevelEntry.I findApproxLevelEntry.h \
// findApproxPath.I findApproxPath.h \

View File

@ -1,29 +0,0 @@
// Filename: findApproxLevel.I
// Created by: drose (13Mar02)
//
////////////////////////////////////////////////////////////////////
//
// 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: FindApproxLevel::add_entry
// Access: Public
// Description: Adds a new entry to the level.
////////////////////////////////////////////////////////////////////
INLINE void FindApproxLevel::
add_entry(const FindApproxLevelEntry &entry) {
_v.push_back(entry);
}

View File

@ -1,34 +0,0 @@
// Filename: findApproxLevel.cxx
// Created by: drose (13Mar02)
//
////////////////////////////////////////////////////////////////////
//
// 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 "findApproxLevel.h"
////////////////////////////////////////////////////////////////////
// Function: FindApproxLevel::write
// Access: Public
// Description: Shows the entire contents of the level, one entry per
// line. For debugging only.
////////////////////////////////////////////////////////////////////
void FindApproxLevel::
write(ostream &out) const {
Vec::const_iterator vi;
for (vi = _v.begin(); vi != _v.end(); ++vi) {
(*vi).output(out);
out << "\n";
}
}

View File

@ -1,46 +0,0 @@
// Filename: findApproxLevel.h
// Created by: drose (13Mar02)
//
////////////////////////////////////////////////////////////////////
//
// 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 FINDAPPROXLEVEL_H
#define FINDAPPROXLEVEL_H
#include "pandabase.h"
#include "findApproxLevelEntry.h"
#include "pvector.h"
////////////////////////////////////////////////////////////////////
// Class : FindApproxLevel
// Description : This class is local to this package only; it doesn't
// get exported. It maintains the list of nodes
// find_approx() considers for each level of the scene
// graph it visits, in its breadth-first search.
////////////////////////////////////////////////////////////////////
class FindApproxLevel {
public:
INLINE void add_entry(const FindApproxLevelEntry &entry);
void write(ostream &out) const;
typedef pvector<FindApproxLevelEntry> Vec;
Vec _v;
};
#include "findApproxLevel.I"
#endif

View File

@ -28,6 +28,25 @@ FindApproxLevelEntry(const WorkingNodePath &node_path, FindApproxPath &approx_pa
_approx_path(approx_path)
{
_i = 0;
_next = NULL;
nassertv(_node_path.is_valid());
}
////////////////////////////////////////////////////////////////////
// Function: FindApproxLevelEntry::Constructor
// Access: Public
// Description: This constructor is used to construct the next entry
// based on a child node of the previous entry's node.
////////////////////////////////////////////////////////////////////
INLINE FindApproxLevelEntry::
FindApproxLevelEntry(const FindApproxLevelEntry &parent,
PandaNode *child_node, int i,
FindApproxLevelEntry *next) :
_node_path(parent._node_path, child_node),
_i(i),
_approx_path(parent._approx_path),
_next(next)
{
nassertv(_node_path.is_valid());
}
@ -37,11 +56,12 @@ FindApproxLevelEntry(const WorkingNodePath &node_path, FindApproxPath &approx_pa
// Description:
////////////////////////////////////////////////////////////////////
INLINE FindApproxLevelEntry::
FindApproxLevelEntry(const FindApproxLevelEntry &copy, int increment) :
FindApproxLevelEntry(const FindApproxLevelEntry &copy) :
_node_path(copy._node_path),
_i(copy._i + increment),
_i(copy._i),
_approx_path(copy._approx_path)
{
_next = NULL;
nassertv(_node_path.is_valid());
}
@ -81,3 +101,50 @@ INLINE bool FindApproxLevelEntry::
is_solution(int increment) const {
return (_i + increment >= _approx_path.get_num_components());
}
////////////////////////////////////////////////////////////////////
// Function: FindApproxLevelEntry::operator new
// Access: Public
// Description: Allocates the memory for a new FindApproxLevelEntry.
// This is specialized here to provide for fast
// allocation of these things.
////////////////////////////////////////////////////////////////////
INLINE void *FindApproxLevelEntry::
operator new(size_t size) {
if (_deleted_chain != (FindApproxLevelEntry *)NULL) {
FindApproxLevelEntry *obj = _deleted_chain;
_deleted_chain = _deleted_chain->_next;
return obj;
}
#ifndef NDEBUG
_num_ever_allocated++;
#endif // NDEBUG
return ::operator new(size);
}
////////////////////////////////////////////////////////////////////
// Function: FindApproxLevelEntry::operator delete
// Access: Public
// Description: Frees the memory for a deleted FindApproxLevelEntry.
// This is specialized here to provide for fast
// allocation of these things.
////////////////////////////////////////////////////////////////////
INLINE void FindApproxLevelEntry::
operator delete(void *ptr) {
FindApproxLevelEntry *obj = (FindApproxLevelEntry *)ptr;
obj->_next = _deleted_chain;
_deleted_chain = obj;
}
////////////////////////////////////////////////////////////////////
// Function: FindApproxLevelEntry::get_num_ever_allocated
// Access: Published, Static
// Description: Returns the number of FindApproxLevelEntry pointers
// ever simultaneously allocated; these are now either
// in active use or have been recycled into the deleted
// FindApproxLevelEntry pool to be used again.
////////////////////////////////////////////////////////////////////
INLINE int FindApproxLevelEntry::
get_num_ever_allocated() {
return _num_ever_allocated;
}

View File

@ -19,6 +19,10 @@
#include "findApproxLevelEntry.h"
#include "nodePathCollection.h"
#include "pandaNode.h"
#include "indent.h"
FindApproxLevelEntry *FindApproxLevelEntry::_deleted_chain = (FindApproxLevelEntry *)NULL;
int FindApproxLevelEntry::_num_ever_allocated = 0;
////////////////////////////////////////////////////////////////////
@ -40,14 +44,49 @@ output(ostream &out) const {
}
////////////////////////////////////////////////////////////////////
// Function: FindApproxLevelEntry::consider_node
// Function: FindApproxLevelEntry::write_level
// Access: Public
// Description:
// Description: Writes the entire level (a linked list of entries
// beginning at this entry). For debugging only.
////////////////////////////////////////////////////////////////////
void FindApproxLevelEntry::
consider_node(NodePathCollection &result, FindApproxLevel &next_level,
write_level(ostream &out, int indent_level) const {
for (const FindApproxLevelEntry *entry = this;
entry != (const FindApproxLevelEntry *)NULL;
entry = entry->_next) {
indent(out, indent_level);
out << *entry << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: FindApproxLevelEntry::consider_node
// Access: Public
// Description: Considers the node represented by the entry for
// matching the find path. If a solution is found, it
// is added to result; if the children of this node
// should be considered, the appropriate entries are
// added to next_level.
//
// The return value is true if result now contains
// max_matches solutions, or false if we should keep
// looking.
////////////////////////////////////////////////////////////////////
bool FindApproxLevelEntry::
consider_node(NodePathCollection &result, FindApproxLevelEntry *&next_level,
int max_matches, int increment) const {
nassertv(_i + increment < _approx_path.get_num_components());
if (is_solution(increment)) {
// If the entry represents a solution, save it and we're done with
// the entry.
result.add_path(_node_path.get_node_path());
if (max_matches > 0 && result.get_num_paths() >= max_matches) {
return true;
}
return false;
}
// If the entry is not itself a solution, consider its children.
if (_approx_path.is_component_match_many(_i + increment)) {
// Match any number, zero or more, levels of nodes. This is the
@ -64,32 +103,24 @@ consider_node(NodePathCollection &result, FindApproxLevel &next_level,
// FindApproxLevelEntry objects. Instead, we pass around the
// increment parameter, which increments _i on the fly.
if (is_solution(increment + 1)) {
// Does this now represent a solution?
result.add_path(_node_path.get_node_path());
if (max_matches > 0 && result.get_num_paths() >= max_matches) {
return;
}
} else {
consider_node(result, next_level, max_matches, increment + 1);
if (consider_node(result, next_level, max_matches, increment + 1)) {
return true;
}
}
PandaNode *this_node = _node_path.node();
nassertv(this_node != (PandaNode *)NULL);
nassertr(this_node != (PandaNode *)NULL, false);
bool stashed_only = next_is_stashed(increment);
if (!stashed_only) {
// Check the normal list of children.
int num_children = this_node->get_num_children();
PandaNode::Children children = this_node->get_children();
int num_children = children.get_num_children();
for (int i = 0; i < num_children; i++) {
PandaNode *child_node = this_node->get_child(i);
PandaNode *child_node = children.get_child(i);
consider_next_step(result, child_node, next_level, max_matches, increment);
if (max_matches > 0 && result.get_num_paths() >= max_matches) {
return;
}
consider_next_step(child_node, next_level, increment);
}
}
@ -99,12 +130,11 @@ consider_node(NodePathCollection &result, FindApproxLevel &next_level,
for (int i = 0; i < num_stashed; i++) {
PandaNode *stashed_node = this_node->get_stashed(i);
consider_next_step(result, stashed_node, next_level, max_matches, increment);
if (max_matches > 0 && result.get_num_paths() >= max_matches) {
return;
}
consider_next_step(stashed_node, next_level, increment);
}
}
return false;
}
////////////////////////////////////////////////////////////////////
@ -115,12 +145,9 @@ consider_node(NodePathCollection &result, FindApproxLevel &next_level,
// of the path. If it matches, generates whatever
// additional entries are appropriate and stores them in
// next_level.
//
// If a complete solution is found, stores it in result.
////////////////////////////////////////////////////////////////////
void FindApproxLevelEntry::
consider_next_step(NodePathCollection &result, PandaNode *child_node,
FindApproxLevel &next_level, int max_matches,
consider_next_step(PandaNode *child_node, FindApproxLevelEntry *&next_level,
int increment) const {
if (!_approx_path.return_hidden() &&
child_node->get_draw_mask().is_zero()) {
@ -139,17 +166,14 @@ consider_next_step(NodePathCollection &result, PandaNode *child_node,
// And now we just add the next entry without incrementing its
// path entry.
FindApproxLevelEntry next(*this, increment);
next._node_path = WorkingNodePath(_node_path, child_node);
next_level.add_entry(next);
next_level = new FindApproxLevelEntry
(*this, child_node, _i + increment, next_level);
} else {
if (_approx_path.matches_component(_i + increment, child_node)) {
// That matched, and it consumes one path entry.
FindApproxLevelEntry next(*this, increment);
next._i++;
next._node_path = WorkingNodePath(_node_path, child_node);
next_level.add_entry(next);
next_level = new FindApproxLevelEntry
(*this, child_node, _i + increment + 1, next_level);
}
}
}

View File

@ -24,7 +24,6 @@
#include "findApproxPath.h"
#include "workingNodePath.h"
class FindApproxLevel;
class NodePathCollection;
////////////////////////////////////////////////////////////////////
@ -38,19 +37,33 @@ class FindApproxLevelEntry {
public:
INLINE FindApproxLevelEntry(const WorkingNodePath &node_path,
FindApproxPath &approx_path);
INLINE FindApproxLevelEntry(const FindApproxLevelEntry &copy, int increment = 0);
INLINE FindApproxLevelEntry(const FindApproxLevelEntry &parent,
PandaNode *child_node, int i,
FindApproxLevelEntry *next);
INLINE FindApproxLevelEntry(const FindApproxLevelEntry &copy);
INLINE void operator = (const FindApproxLevelEntry &copy);
INLINE bool next_is_stashed(int increment) const;
void consider_node(NodePathCollection &result, FindApproxLevel &next_level,
bool consider_node(NodePathCollection &result,
FindApproxLevelEntry *&next_level,
int max_matches, int increment) const;
void consider_next_step(NodePathCollection &result,
PandaNode *child_node, FindApproxLevel &next_level,
int max_matches, int increment) const;
void consider_next_step(PandaNode *child_node,
FindApproxLevelEntry *&next_level,
int increment) const;
INLINE bool is_solution(int increment) const;
// We will allocate and destroy thousands of these during a typical
// NodePath::find() or find_all_matches() operation. As an
// optimization, then, we implement operator new and delete here to
// minimize this overhead.
INLINE void *operator new(size_t size);
INLINE void operator delete(void *ptr);
INLINE static int get_num_ever_allocated();
void output(ostream &out) const;
void write_level(ostream &out, int indent_level) const;
// _node_path represents the most recent node that we have
// previously accepted as being a partial solution.
@ -62,6 +75,11 @@ public:
// solution.
int _i;
FindApproxPath &_approx_path;
FindApproxLevelEntry *_next;
private:
static FindApproxLevelEntry *_deleted_chain;
static int _num_ever_allocated;
};
INLINE ostream &

View File

@ -20,7 +20,6 @@
#include "nodePathCollection.h"
#include "findApproxPath.h"
#include "findApproxLevelEntry.h"
#include "findApproxLevel.h"
#include "config_pgraph.h"
#include "colorAttrib.h"
#include "colorScaleAttrib.h"
@ -3701,64 +3700,97 @@ find_matches(NodePathCollection &result, FindApproxPath &approx_path,
<< "Attempt to extend an empty NodePath by: " << approx_path << ".\n";
return;
}
FindApproxLevelEntry start(WorkingNodePath(*this), approx_path);
nassertv(start._node_path.is_valid());
FindApproxLevel level;
level.add_entry(start);
r_find_matches(result, level, max_matches, _max_search_depth);
// We start with just one entry on the level.
FindApproxLevelEntry *level =
new FindApproxLevelEntry(WorkingNodePath(*this), approx_path);
nassertv(level->_node_path.is_valid());
find_matches(result, level, max_matches);
}
////////////////////////////////////////////////////////////////////
// Function: NodePath::r_find_matches
// Function: NodePath::find_matches
// Access: Private
// Description: The recursive implementation of find_matches.
// Description: The fundamental implementation of find_matches(),
// given a starting level (a linked list of
// FindApproxLevelEntry objects).
////////////////////////////////////////////////////////////////////
void NodePath::
r_find_matches(NodePathCollection &result,
const FindApproxLevel &level,
int max_matches, int num_levels_remaining) const {
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< "r_find_matches(" << result << ", level, "
<< max_matches << ", " << num_levels_remaining << ")\n";
level.write(pgraph_cat.debug(false));
}
find_matches(NodePathCollection &result, FindApproxLevelEntry *level,
int max_matches) const {
int num_levels_remaining = _max_search_depth;
// Go on to the next level. If we exceeded the requested maximum
// depth (or if there are no more levels to visit), stop.
if (num_levels_remaining <= 0 || level._v.empty()) {
return;
}
num_levels_remaining--;
FindApproxLevelEntry *deleted_entries = NULL;
FindApproxLevel next_level;
bool okflag = true;
// For each node in the current level, build up the set of possible
// matches in the next level.
FindApproxLevel::Vec::const_iterator li;
for (li = level._v.begin(); li != level._v.end() && okflag; ++li) {
const FindApproxLevelEntry &entry = (*li);
if (entry.is_solution(0)) {
// Does this entry already represent a solution?
result.add_path(entry._node_path.get_node_path());
} else {
entry.consider_node(result, next_level, max_matches, 0);
while (num_levels_remaining > 0 && level != NULL) {
if (pgraph_cat.is_debug()) {
pgraph_cat.debug()
<< "find_matches pass: " << result << ", "
<< max_matches << ", " << num_levels_remaining << "\n";
level->write_level(pgraph_cat.debug(false), 4);
}
if (max_matches > 0 && result.get_num_paths() >= max_matches) {
// Really, we just want to return here. But returning from
// within the conditional within the for loop seems to sometimes
// cause a compiler fault in GCC. We'll use a semaphore
// variable instead.
okflag = false;
num_levels_remaining--;
FindApproxLevelEntry *next_level = NULL;
// For each node in the current level, build up the set of possible
// matches in the next level.
FindApproxLevelEntry *entry = level;
while (entry != (FindApproxLevelEntry *)NULL) {
if (entry->consider_node(result, next_level, max_matches, 0)) {
// If we found the requisite number of matches, we can stop.
// Delete all remaining entries and return immediately.
while (entry != (FindApproxLevelEntry *)NULL) {
FindApproxLevelEntry *next = entry->_next;
delete entry;
entry = next;
}
while (next_level != (FindApproxLevelEntry *)NULL) {
FindApproxLevelEntry *next = next_level->_next;
delete next_level;
next_level = next;
}
while (deleted_entries != (FindApproxLevelEntry *)NULL) {
FindApproxLevelEntry *next = deleted_entries->_next;
delete deleted_entries;
deleted_entries = next;
}
return;
}
// Move the entry to the delete chain so we can delete it before
// we return from this method. (We can't delete it immediately,
// because there might be WorkingNodePaths in the next_level
// that reference the WorkingNodePath object within the entry.)
FindApproxLevelEntry *next = entry->_next;
entry->_next = deleted_entries;
deleted_entries = entry;
entry = next;
}
// Make sure the remaining entries from this level are added to
// the delete chain.
while (entry != (FindApproxLevelEntry *)NULL) {
FindApproxLevelEntry *next = entry->_next;
entry->_next = deleted_entries;
deleted_entries = entry;
entry = next;
}
level = next_level;
}
// Now recurse on the next level.
if (okflag) {
r_find_matches(result, next_level, max_matches, num_levels_remaining);
// Now it's safe to delete all entries on the delete chain.
while (deleted_entries != (FindApproxLevelEntry *)NULL) {
FindApproxLevelEntry *next = deleted_entries->_next;
delete deleted_entries;
deleted_entries = next;
}
}

View File

@ -33,8 +33,8 @@
class NodePathCollection;
class TextureCollection;
class FindApproxLevel;
class FindApproxPath;
class FindApproxLevelEntry;
class Texture;
class Material;
class Fog;
@ -621,9 +621,9 @@ private:
void find_matches(NodePathCollection &result,
FindApproxPath &approx_path,
int max_matches) const;
void r_find_matches(NodePathCollection &result,
const FindApproxLevel &level,
int max_matches, int num_levels_remaining) const;
void find_matches(NodePathCollection &result,
FindApproxLevelEntry *level,
int max_matches) const;
void r_adjust_all_priorities(PandaNode *node, int adjustment);

View File

@ -18,7 +18,7 @@
#include "nodePathCollection.h"
#include "findApproxPath.h"
#include "findApproxLevel.h"
#include "findApproxLevelEntry.h"
#include "indent.h"
@ -278,13 +278,14 @@ find_all_matches(const string &path) const {
FindApproxPath approx_path;
if (approx_path.add_string(path)) {
if (!is_empty()) {
FindApproxLevel level;
FindApproxLevelEntry *level = NULL;
for (int i = 0; i < get_num_paths(); i++) {
FindApproxLevelEntry start(get_path(i), approx_path);
level.add_entry(start);
FindApproxLevelEntry *start =
new FindApproxLevelEntry(get_path(i), approx_path);
start->_next = level;
level = start;
}
get_path(0).r_find_matches(result, level, -1,
NodePath::get_max_search_depth());
get_path(0).find_matches(result, level, -1);
}
}

View File

@ -32,7 +32,6 @@
#include "directionalLight.cxx"
#include "drawCullHandler.cxx"
#include "findApproxPath.cxx"
#include "findApproxLevel.cxx"
#include "findApproxLevelEntry.cxx"
#include "fog.cxx"
#include "fogAttrib.cxx"

View File

@ -18,21 +18,61 @@
#include "pandaNode.h"
#include "nodePath.h"
#include "nodePathCollection.h"
#include "findApproxLevelEntry.h"
#include "clockObject.h"
NodePath
build_tree(const string &name, int depth) {
NodePath node(name);
if (depth > 1) {
for (int i = 0; i < 3; i++) {
char letter = 'a' + i;
string child_name = name + string(1, letter);
NodePath child = build_tree(child_name, depth - 1);
child.reparent_to(node);
}
}
return node;
}
int
main(int argc, char *argv[]) {
NodePath h("h");
NodePath n1("n1");
NodePath n2("n2");
PT(PandaNode) node = new PandaNode("node");
// Build up a tree of height 6. Each level has three children, so
// that there are 3^5 + 3^4 + 3^3 + 3^2 + 3^1 + 3^0 = 364 total nodes.
NodePath t1 = h.attach_new_node(node);
t1.reparent_to(n1);
NodePath root = build_tree("a", 6);
t1 = NodePath();
NodePath abba = root.find("**/abba");
cerr << abba << "\n";
cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
NodePath abaab = root.find("ab/aba/abaa/abaab");
cerr << abaab << "\n";
cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
NodePathCollection col = root.find_all_matches("**");
cerr << col << "\n";
cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
// Now time a bunch of find operations.
ClockObject *clock = ClockObject::get_global_clock();
static const int num_ops = 10000;
volatile double start, end;
{
start = clock->get_real_time();
for (int i = 0; i < num_ops; i++) {
root.find_all_matches("**/aabca");
}
end = clock->get_real_time();
}
double avg_time = (end - start) / (double)num_ops;
cerr << "Find operation took " << avg_time * 1000 << " ms\n";
cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
NodePath t2 = h.attach_new_node(node);
t2.reparent_to(n2);
return 0;
}