better pipelining

This commit is contained in:
David Rose 2006-02-03 18:12:08 +00:00
parent a75df81dfb
commit c75b55eb98
39 changed files with 425 additions and 126 deletions

View File

@ -46,6 +46,7 @@
#endif
PStatCollector GraphicsEngine::_wait_pcollector("Wait");
PStatCollector GraphicsEngine::_cycle_pcollector("App:Cycle");
PStatCollector GraphicsEngine::_app_pcollector("App");
PStatCollector GraphicsEngine::_yield_pcollector("App:Yield");
PStatCollector GraphicsEngine::_cull_pcollector("Cull");
@ -490,9 +491,32 @@ render_frame() {
} else {
new_windows.push_back(win);
// Let's calculate each scene's bounding volume here in App,
// before we cycle the pipeline. The cull traversal will
// calculate it anyway, but if we calculate it in App first
// before it gets calculated in the Cull thread, it will be more
// likely to stick for subsequent frames, so we won't have to
// recompute it each frame.
int num_drs = win->get_num_active_display_regions();
for (int i = 0; i < num_drs; ++i) {
DisplayRegion *dr = win->get_active_display_region(i);
if (dr != (DisplayRegion *)NULL) {
NodePath camera_np = dr->get_camera();
if (!camera_np.is_empty()) {
Camera *camera = DCAST(Camera, camera_np.node());
NodePath scene = camera->get_scene();
if (scene.is_empty()) {
scene = camera_np.get_top();
}
if (!scene.is_empty()) {
scene.get_bounds();
}
}
}
}
}
}
new_windows.swap(_windows);
// Now it's time to do any drawing from the main frame--after all of
// the App code has executed, but before we begin the next frame.
@ -500,13 +524,13 @@ render_frame() {
// Grab each thread's mutex again after all windows have flipped,
// and wait for the thread to finish.
{
PStatTimer timer(_wait_pcollector);
Threads::const_iterator ti;
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
thread->_cv_mutex.lock();
if (thread->_thread_state != TS_wait) {
PStatTimer timer(_wait_pcollector);
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
@ -514,7 +538,10 @@ render_frame() {
}
// Now cycle the pipeline and officially begin the next frame.
{
PStatTimer timer(_cycle_pcollector);
_pipeline->cycle();
}
ClockObject *global_clock = ClockObject::get_global_clock();
global_clock->tick();
if (global_clock->check_errors()) {
@ -539,6 +566,7 @@ render_frame() {
}
// Now signal all of our threads to begin their next frame.
Threads::const_iterator ti;
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
if (thread->_thread_state == TS_wait) {
@ -589,17 +617,16 @@ open_windows() {
_app.do_windows(this);
{
PStatTimer timer(_wait_pcollector);
Threads::const_iterator ti;
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
thread->_cv_mutex.lock();
if (thread->_thread_state != TS_wait) {
PStatTimer timer(_wait_pcollector);
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
}
thread->_thread_state = TS_do_windows;
thread->_cv_start.signal();
@ -612,17 +639,15 @@ open_windows() {
RenderThread *thread = (*ti).second;
thread->_cv_mutex.lock();
if (thread->_thread_state != TS_wait) {
PStatTimer timer(_wait_pcollector);
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
}
thread->_thread_state = TS_do_windows;
thread->_cv_start.signal();
thread->_cv_mutex.release();
}
}
}
////////////////////////////////////////////////////////////////////
@ -1008,13 +1033,13 @@ do_flip_frame() {
// First, wait for all the threads to finish their current frame, if
// necessary. Grabbing the mutex (and waiting for TS_wait) should
// achieve that.
{
PStatTimer timer(_wait_pcollector);
Threads::const_iterator ti;
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
thread->_cv_mutex.lock();
if (thread->_thread_state != TS_wait) {
PStatTimer timer(_wait_pcollector);
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
@ -1023,6 +1048,9 @@ do_flip_frame() {
// Now signal all of our threads to flip the windows.
_app.do_flip(this);
{
Threads::const_iterator ti;
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
nassertv(thread->_thread_state == TS_wait);
@ -1030,6 +1058,7 @@ do_flip_frame() {
thread->_cv_start.signal();
thread->_cv_mutex.release();
}
}
_flip_state = FS_flip;
}
@ -1157,6 +1186,9 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
}
}
static PStatCollector traverse("Cull:Traverse");
PStatTimer timer2(traverse);
trav.traverse(scene_setup->get_scene_root(), get_portal_cull());
}
@ -1327,6 +1359,10 @@ void GraphicsEngine::
terminate_threads() {
MutexHolder holder(_lock);
// We spend almost our entire time in this method just waiting for
// threads. Time it appropriately.
PStatTimer timer(_wait_pcollector);
// First, wait for all the threads to finish their current frame.
// Grabbing the mutex should achieve that.
Threads::const_iterator ti;
@ -1339,12 +1375,9 @@ terminate_threads() {
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
if (thread->_thread_state != TS_wait) {
PStatTimer timer(_wait_pcollector);
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
}
thread->_thread_state = TS_do_release;
thread->_cv_start.signal();
thread->_cv_mutex.release();
@ -1354,6 +1387,9 @@ terminate_threads() {
for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
RenderThread *thread = (*ti).second;
thread->_cv_mutex.lock();
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
}
// Now tell them to close their windows and terminate.
@ -1416,7 +1452,7 @@ get_window_renderer(const string &name) {
PT(RenderThread) thread = new RenderThread(name, this);
thread->set_pipeline_stage(1);
Pipeline::get_render_pipeline()->set_num_stages(2);
_pipeline->set_min_stages(2);
thread->start(TP_normal, true, true);
_threads[name] = thread;

View File

@ -246,6 +246,7 @@ private:
Mutex _lock;
static PStatCollector _wait_pcollector;
static PStatCollector _cycle_pcollector;
static PStatCollector _app_pcollector;
static PStatCollector _yield_pcollector;
static PStatCollector _cull_pcollector;

View File

@ -150,6 +150,18 @@ set_pipeline_stage(int pipeline_stage) {
_pipeline_stage = pipeline_stage;
}
////////////////////////////////////////////////////////////////////
// Function: Thread::set_min_pipeline_stage
// Access: Published
// Description: Sets this thread's pipeline stage number to at least
// the indicated value, unless it is already larger.
// See set_pipeline_stage().
////////////////////////////////////////////////////////////////////
INLINE void Thread::
set_min_pipeline_stage(int min_pipeline_stage) {
_pipeline_stage = max(_pipeline_stage, min_pipeline_stage);
}
////////////////////////////////////////////////////////////////////
// Function: Thread::get_main_thread
// Access: Published, Static

View File

@ -63,6 +63,7 @@ PUBLISHED:
INLINE int get_pipeline_stage() const;
INLINE void set_pipeline_stage(int pipeline_stage);
INLINE void set_min_pipeline_stage(int min_pipeline_stage);
INLINE static Thread *get_main_thread();
INLINE static Thread *get_external_thread();

View File

@ -82,6 +82,9 @@ private:
INLINE void operator = (const CData &copy);
virtual CycleData *make_copy() const;
virtual TypeHandle get_parent_type() const {
return BoundedObject::get_class_type();
}
int _flags;
BoundingVolumeType _bound_type;

View File

@ -177,6 +177,9 @@ private:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return Geom::get_class_type();
}
PT(GeomVertexData) _data;
Primitives _primitives;

View File

@ -209,6 +209,9 @@ private:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return GeomPrimitive::get_class_type();
}
ShadeModel _shade_model;
int _first_vertex;

View File

@ -123,6 +123,9 @@ private:
void *extra_data) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager,
void *extra_data);
virtual TypeHandle get_parent_type() const {
return GeomVertexArrayData::get_class_type();
}
UsageHint _usage_hint;
PTA_uchar _data;

View File

@ -234,6 +234,18 @@ get_modified() const {
return cdata->_modified;
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexData::clear_cache
// Access: Published
// Description: Removes all of the previously-cached results of
// convert_to().
////////////////////////////////////////////////////////////////////
INLINE void GeomVertexData::
clear_cache() {
CDWriter cdata(_cycler);
do_clear_cache(cdata);
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexData::has_vertex
// Access: Public

View File

@ -152,7 +152,7 @@ operator = (const GeomVertexData &copy) {
_morphs_pcollector = copy._morphs_pcollector;
CDWriter cdata(_cycler);
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
}
@ -218,7 +218,7 @@ set_usage_hint(GeomVertexData::UsageHint usage_hint) {
}
(*ai)->set_usage_hint(usage_hint);
}
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
}
@ -265,7 +265,7 @@ clear_rows() {
}
(*ai)->clear_rows();
}
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices.clear();
}
@ -291,7 +291,7 @@ modify_array(int i) {
if (cdata->_arrays[i]->get_ref_count() > 1) {
cdata->_arrays[i] = new GeomVertexArrayData(*cdata->_arrays[i]);
}
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
@ -311,7 +311,7 @@ set_array(int i, const GeomVertexArrayData *array) {
CDWriter cdata(_cycler);
nassertv(i >= 0 && i < (int)cdata->_arrays.size());
cdata->_arrays[i] = (GeomVertexArrayData *)array;
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
}
@ -331,7 +331,7 @@ set_transform_table(const TransformTable *table) {
CDWriter cdata(_cycler);
cdata->_transform_table = (TransformTable *)table;
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
}
@ -354,7 +354,7 @@ modify_transform_blend_table() {
if (cdata->_transform_blend_table->get_ref_count() > 1) {
cdata->_transform_blend_table = new TransformBlendTable(*cdata->_transform_blend_table);
}
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
@ -374,7 +374,7 @@ void GeomVertexData::
set_transform_blend_table(const TransformBlendTable *table) {
CDWriter cdata(_cycler);
cdata->_transform_blend_table = (TransformBlendTable *)table;
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
}
@ -396,7 +396,7 @@ set_slider_table(const SliderTable *table) {
CDWriter cdata(_cycler);
cdata->_slider_table = (SliderTable *)table;
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices_modified = UpdateSeq();
}
@ -1069,26 +1069,6 @@ write(ostream &out, int indent_level) const {
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexData::clear_cache
// Access: Published
// Description: Removes all of the previously-cached results of
// convert_to().
////////////////////////////////////////////////////////////////////
void GeomVertexData::
clear_cache() {
// Probably we shouldn't do anything at all here unless we are
// running in pipeline stage 0.
CData *cdata = CDWriter(_cycler);
for (Cache::iterator ci = cdata->_cache.begin();
ci != cdata->_cache.end();
++ci) {
CacheEntry *entry = (*ci);
entry->erase();
}
cdata->_cache.clear();
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexData::get_array_info
// Access: Public
@ -1263,7 +1243,7 @@ uint8_rgba_to_packed_argb(unsigned char *to, int to_stride,
// Description: The private implementation of set_num_rows().
////////////////////////////////////////////////////////////////////
bool GeomVertexData::
do_set_num_rows(int n, GeomVertexData::CDWriter &cdata) {
do_set_num_rows(int n, GeomVertexData::CData *cdata) {
nassertr(_format->get_num_arrays() == (int)cdata->_arrays.size(), false);
bool any_changed = false;
@ -1324,7 +1304,7 @@ do_set_num_rows(int n, GeomVertexData::CDWriter &cdata) {
}
if (any_changed) {
clear_cache();
do_clear_cache(cdata);
cdata->_modified = Geom::get_next_modified();
cdata->_animated_vertices.clear();
}
@ -1340,7 +1320,7 @@ do_set_num_rows(int n, GeomVertexData::CDWriter &cdata) {
// existing animated_vertices object.
////////////////////////////////////////////////////////////////////
void GeomVertexData::
update_animated_vertices(GeomVertexData::CDWriter &cdata) {
update_animated_vertices(GeomVertexData::CData *cdata) {
int num_rows = get_num_rows();
if (gobj_cat.is_debug()) {
@ -1474,6 +1454,24 @@ update_animated_vertices(GeomVertexData::CDWriter &cdata) {
}
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexData::do_clear_cache
// Access: Private
// Description: The private implementation of clear_cache().
////////////////////////////////////////////////////////////////////
void GeomVertexData::
do_clear_cache(GeomVertexData::CData *cdata) {
// Probably we shouldn't do anything at all here unless we are
// running in pipeline stage 0.
for (Cache::iterator ci = cdata->_cache.begin();
ci != cdata->_cache.end();
++ci) {
CacheEntry *entry = (*ci);
entry->erase();
}
cdata->_cache.clear();
}
////////////////////////////////////////////////////////////////////
// Function: GeomVertexData::register_with_read_factory
// Access: Public, Static

View File

@ -142,7 +142,7 @@ PUBLISHED:
void output(ostream &out) const;
void write(ostream &out, int indent_level = 0) const;
void clear_cache();
INLINE void clear_cache();
public:
bool get_array_info(const InternalName *name,
@ -224,6 +224,9 @@ private:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return GeomVertexData::get_class_type();
}
UsageHint _usage_hint;
Arrays _arrays;
@ -241,8 +244,9 @@ private:
typedef CycleDataWriter<CData> CDWriter;
private:
bool do_set_num_rows(int n, CDWriter &cdata);
void update_animated_vertices(CDWriter &cdata);
bool do_set_num_rows(int n, CData *cdata);
void update_animated_vertices(CData *cdata);
void do_clear_cache(CData *cdata);
static PStatCollector _convert_pcollector;
static PStatCollector _scale_color_pcollector;

View File

@ -89,6 +89,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return SliderTable::get_class_type();
}
UpdateSeq _modified;
};

View File

@ -99,6 +99,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return TransformBlendTable::get_class_type();
}
UpdateSeq _modified;
UpdateSeq _global_modified;

View File

@ -81,6 +81,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return TransformTable::get_class_type();
}
UpdateSeq _modified;
};

View File

@ -50,6 +50,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return UserVertexSlider::get_class_type();
}
float _slider;
};

View File

@ -55,6 +55,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return UserVertexTransform::get_class_type();
}
LMatrix4f _matrix;
};

View File

@ -74,6 +74,9 @@ private:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return VertexSlider::get_class_type();
}
UpdateSeq _modified;
};

View File

@ -73,6 +73,9 @@ private:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return VertexTransform::get_class_type();
}
UpdateSeq _modified;
};

View File

@ -197,6 +197,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return RopeNode::get_class_type();
}
PT(NurbsCurveEvaluator) _curve;
RenderMode _render_mode;

View File

@ -84,6 +84,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return SheetNode::get_class_type();
}
PT(NurbsSurfaceEvaluator) _surface;
bool _use_vertex_color;

View File

@ -69,6 +69,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return DirectionalLight::get_class_type();
}
Colorf _specular_color;
LPoint3f _point;

View File

@ -111,6 +111,9 @@ private:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return GeomNode::get_class_type();
}
Geoms _geoms;
};

View File

@ -106,6 +106,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return Light::get_class_type();
}
Colorf _color;

View File

@ -104,6 +104,9 @@ protected:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return LODNode::get_class_type();
}
LPoint3f _center;
SwitchVector _switch_vector;

View File

@ -82,6 +82,9 @@ private:
INLINE CData();
CData(const CData &copy);
virtual CycleData *make_copy() const;
virtual TypeHandle get_parent_type() const {
return NodePathComponent::get_class_type();
}
PT(NodePathComponent) _next;
int _length;

View File

@ -322,6 +322,9 @@ private:
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return PandaNode::get_class_type();
}
#ifdef HAVE_PYTHON
void inc_py_refs();

View File

@ -70,6 +70,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return PlaneNode::get_class_type();
}
Planef _plane;
};

View File

@ -69,6 +69,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return PointLight::get_class_type();
}
Colorf _specular_color;
LVecBase3f _attenuation;

View File

@ -89,6 +89,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return Spotlight::get_class_type();
}
float _exponent;
Colorf _specular_color;

View File

@ -54,6 +54,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return SwitchNode::get_class_type();
}
int _visible_child;
};

View File

@ -95,6 +95,9 @@ private:
virtual CycleData *make_copy() const;
virtual void write_datagram(BamWriter *manager, Datagram &dg) const;
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual TypeHandle get_parent_type() const {
return AnimInterface::get_class_type();
}
void play(double from, double to);
void loop(bool restart, double from, double to);

View File

@ -85,3 +85,25 @@ void CycleData::
fillin(DatagramIterator &, BamReader *, void *) {
}
////////////////////////////////////////////////////////////////////
// Function: CycleData::get_parent_type
// Access: Public, Virtual
// Description: Returns the type of the container that owns the
// CycleData. This is useful mainly for debugging.
////////////////////////////////////////////////////////////////////
TypeHandle CycleData::
get_parent_type() const {
return TypeHandle::none();
}
////////////////////////////////////////////////////////////////////
// Function: CycleData::output
// Access: Public, Virtual
// Description: Formats the contents of the CycleData in some
// meaningful way for humans. This is useful mainly for
// debugging.
////////////////////////////////////////////////////////////////////
void CycleData::
output(ostream &out) const {
out << get_parent_type() << "::CData";
}

View File

@ -20,7 +20,7 @@
#define CYCLEDATA_H
#include "pandabase.h"
#include "typeHandle.h"
#include "referenceCount.h"
class BamWriter;
@ -64,8 +64,17 @@ public:
virtual void fillin(DatagramIterator &scan, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager,
void *extra_data);
virtual TypeHandle get_parent_type() const;
virtual void output(ostream &out) const;
};
INLINE ostream &
operator << (ostream &out, const CycleData &cd) {
cd.output(out);
return out;
}
#include "cycleData.I"
#endif

View File

@ -29,3 +29,14 @@ get_render_pipeline() {
}
return _render_pipeline;
}
////////////////////////////////////////////////////////////////////
// Function: Pipeline::set_min_stages
// Access: Public
// Description: Ensures that at least the indicated number of stages
// are in the pipeline.
////////////////////////////////////////////////////////////////////
INLINE void Pipeline::
set_min_stages(int min_stages) {
set_num_stages(max(min_stages, get_num_stages()));
}

View File

@ -18,6 +18,7 @@
#include "pipeline.h"
#include "pipelineCyclerTrueImpl.h"
#include "mutexHolder.h"
Pipeline *Pipeline::_render_pipeline = (Pipeline *)NULL;
@ -53,6 +54,7 @@ Pipeline::
void Pipeline::
cycle() {
#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
MutexHolder holder(_lock);
Cyclers::iterator ci;
for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) {
(*ci)->cycle();
@ -69,16 +71,32 @@ cycle() {
void Pipeline::
set_num_stages(int num_stages) {
nassertv(num_stages >= 1);
#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
MutexHolder holder(_lock);
if (num_stages != _num_stages) {
// We need to lock every PipelineCycler object in the world before
// we can adjust the number of stages.
Cyclers::iterator ci;
for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) {
(*ci)->_lock.lock();
}
_num_stages = num_stages;
#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
Cyclers::iterator ci;
for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) {
(*ci)->set_num_stages(num_stages);
}
#endif // DO_PIPELINING && HAVE_THREADS
// Now release them all.
for (ci = _cyclers.begin(); ci != _cyclers.end(); ++ci) {
(*ci)->_lock.release();
}
}
#else // DO_PIPELINING && HAVE_THREADS
_num_stages = num_stages;
#endif // DO_PIPELINING && HAVE_THREADS
}
////////////////////////////////////////////////////////////////////
@ -103,6 +121,7 @@ get_num_stages() const {
////////////////////////////////////////////////////////////////////
void Pipeline::
add_cycler(PipelineCyclerTrueImpl *cycler) {
MutexHolder holder(_lock);
bool inserted = _cyclers.insert(cycler).second;
nassertv(inserted);
}
@ -118,6 +137,7 @@ add_cycler(PipelineCyclerTrueImpl *cycler) {
////////////////////////////////////////////////////////////////////
void Pipeline::
remove_cycler(PipelineCyclerTrueImpl *cycler) {
MutexHolder holder(_lock);
Cyclers::iterator ci = _cyclers.find(cycler);
nassertv(ci != _cyclers.end());
_cyclers.erase(ci);

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "namable.h"
#include "pset.h"
#include "pmutex.h"
class PipelineCyclerTrueImpl;
@ -48,6 +49,7 @@ public:
void cycle();
void set_num_stages(int num_stages);
INLINE void set_min_stages(int min_stages);
int get_num_stages() const;
#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
@ -64,6 +66,8 @@ private:
#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
typedef pset<PipelineCyclerTrueImpl *> Cyclers;
Cyclers _cyclers;
Mutex _lock;
#endif // DO_PIPELINING && HAVE_THREADS
};

View File

@ -32,6 +32,7 @@ INLINE const CycleData *PipelineCyclerTrueImpl::
read() const {
int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
_lock.lock();
return _data[pipeline_stage]._cycle_data;
}
@ -44,6 +45,12 @@ read() const {
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerTrueImpl::
increment_read(const CycleData *pointer) const {
#ifndef NDEBUG
int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
nassertv(_data[pipeline_stage]._cycle_data == pointer);
#endif
_lock.lock();
}
////////////////////////////////////////////////////////////////////
@ -54,6 +61,12 @@ increment_read(const CycleData *pointer) const {
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerTrueImpl::
release_read(const CycleData *pointer) const {
#ifndef NDEBUG
int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
nassertv(pipeline_stage >= 0 && pipeline_stage < _num_stages);
nassertv(_data[pipeline_stage]._cycle_data == pointer);
#endif
_lock.release();
}
////////////////////////////////////////////////////////////////////
@ -75,8 +88,7 @@ release_read(const CycleData *pointer) const {
INLINE CycleData *PipelineCyclerTrueImpl::
write() {
int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, NULL);
return _data[pipeline_stage]._cycle_data;
return write_stage(pipeline_stage);
}
////////////////////////////////////////////////////////////////////
@ -90,7 +102,9 @@ write() {
////////////////////////////////////////////////////////////////////
INLINE CycleData *PipelineCyclerTrueImpl::
elevate_read(const CycleData *pointer) {
return (CycleData *)pointer;
CycleData *new_pointer = write();
_lock.release();
return new_pointer;
}
////////////////////////////////////////////////////////////////////
@ -101,6 +115,12 @@ elevate_read(const CycleData *pointer) {
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerTrueImpl::
release_write(CycleData *pointer) {
#ifdef NDEBUG
int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
return release_write_stage(pipeline_stage);
#else
_lock.release();
#endif
}
////////////////////////////////////////////////////////////////////
@ -123,23 +143,11 @@ get_num_stages() {
INLINE bool PipelineCyclerTrueImpl::
is_stage_unique(int n) const {
nassertr(n >= 0 && n < _num_stages, false);
if (n == 0) {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerTrueImpl::write_stage
// Access: Public
// Description: Returns a pointer suitable for writing to the nth
// stage of the pipeline. This is for special
// applications that need to update the entire pipeline
// at once (for instance, to remove an invalid pointer).
// This pointer should later be released with
// release_write_stage().
////////////////////////////////////////////////////////////////////
INLINE CycleData *PipelineCyclerTrueImpl::
write_stage(int n) {
nassertr(n >= 0 && n < _num_stages, (CycleData *)NULL);
return _data[n]._cycle_data;
} else {
return _data[n]._cycle_data == _data[n - 1]._cycle_data;
}
}
////////////////////////////////////////////////////////////////////
@ -150,7 +158,11 @@ write_stage(int n) {
////////////////////////////////////////////////////////////////////
INLINE void PipelineCyclerTrueImpl::
release_write_stage(int n, CycleData *pointer) {
#ifndef NDEBUG
nassertv(n >= 0 && n < _num_stages);
nassertv(_data[n]._cycle_data == pointer);
#endif
_lock.release();
}
////////////////////////////////////////////////////////////////////
@ -179,7 +191,10 @@ cheat() const {
////////////////////////////////////////////////////////////////////
INLINE int PipelineCyclerTrueImpl::
get_read_count() const {
return 0;
int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, 0);
_lock.lock();
return _lock.get_lock_count();
}
////////////////////////////////////////////////////////////////////
@ -192,5 +207,8 @@ get_read_count() const {
////////////////////////////////////////////////////////////////////
INLINE int PipelineCyclerTrueImpl::
get_write_count() const {
return 0;
int pipeline_stage = Thread::get_current_thread()->get_pipeline_stage();
nassertr(pipeline_stage >= 0 && pipeline_stage < _num_stages, 0);
_lock.lock();
return _lock.get_lock_count();
}

View File

@ -20,7 +20,9 @@
#if defined(DO_PIPELINING) && defined(HAVE_THREADS)
#include "config_util.h"
#include "pipeline.h"
#include "reMutexHolder.h"
////////////////////////////////////////////////////////////////////
@ -40,7 +42,7 @@ PipelineCyclerTrueImpl(CycleData *initial_data, Pipeline *pipeline) :
_num_stages = _pipeline->get_num_stages();
_data = new StageData[_num_stages];
for (int i = 0; i < _num_stages; ++i) {
_data[i]._cycle_data = initial_data->make_copy();
_data[i]._cycle_data = initial_data;
}
}
@ -55,6 +57,7 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
{
_pipeline->add_cycler(this);
ReMutexHolder holder(copy._lock);
_num_stages = _pipeline->get_num_stages();
nassertv(_num_stages == copy._num_stages);
_data = new StageData[_num_stages];
@ -70,9 +73,12 @@ PipelineCyclerTrueImpl(const PipelineCyclerTrueImpl &copy) :
////////////////////////////////////////////////////////////////////
void PipelineCyclerTrueImpl::
operator = (const PipelineCyclerTrueImpl &copy) {
ReMutexHolder holder1(_lock);
ReMutexHolder holder2(copy._lock);
nassertv(_num_stages == copy._num_stages);
for (int i = 0; i < _num_stages; ++i) {
_data[i]._cycle_data = copy._data[i]._cycle_data->make_copy();
_data[i]._cycle_data = copy._data[i]._cycle_data;
}
}
@ -83,11 +89,77 @@ operator = (const PipelineCyclerTrueImpl &copy) {
////////////////////////////////////////////////////////////////////
PipelineCyclerTrueImpl::
~PipelineCyclerTrueImpl() {
ReMutexHolder holder(_lock);
_pipeline->remove_cycler(this);
delete[] _data;
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerTrueImpl::write_stage
// Access: Public
// Description: Returns a pointer suitable for writing to the nth
// stage of the pipeline. This is for special
// applications that need to update the entire pipeline
// at once (for instance, to remove an invalid pointer).
// This pointer should later be released with
// release_write_stage().
////////////////////////////////////////////////////////////////////
CycleData *PipelineCyclerTrueImpl::
write_stage(int n) {
_lock.lock();
#ifndef NDEBUG
nassertd(n >= 0 && n < _num_stages) {
_lock.release();
return NULL;
}
#endif // NDEBUG
CycleData *old_data = _data[n]._cycle_data;
if (old_data->get_ref_count() != 1) {
// Copy-on-write.
// There's a special problem that happens when we write to a stage
// other than stage 0. If we do this, when the next frame cycles,
// the changes that we record to stage n will be lost when the
// data from stage (n - 1) is cycled into place. This can be
// wasteful, especially if we are updating a cached value (which
// is generally the case when we are writing to stages other than
// stage 0).
// To minimize this, we make a special exception: whenever we
// write to stage n, if stage (n - 1) has the same pointer, we
// will write to stage (n - 1) at the same time, and so on all the
// way back to stage 0 or the last different stage.
// On the other hand, if *all* of the instances of this pointer
// are found in stages k .. n, then we don't need to do anything
// at all.
int count = old_data->get_ref_count() - 1;
int k = n - 1;
while (k >= 0 && _data[k]._cycle_data == old_data) {
--k;
--count;
}
if (count > 0) {
PT(CycleData) new_data = old_data->make_copy();
int k = n - 1;
while (k >= 0 && _data[k]._cycle_data == old_data) {
_data[k]._cycle_data = new_data;
--k;
}
_data[n]._cycle_data = new_data;
}
}
return _data[n]._cycle_data;
}
////////////////////////////////////////////////////////////////////
// Function: PipelineCyclerTrueImpl::cycle
// Access: Private
@ -96,10 +168,11 @@ PipelineCyclerTrueImpl::
////////////////////////////////////////////////////////////////////
void PipelineCyclerTrueImpl::
cycle() {
ReMutexHolder holder(_lock);
for (int i = _num_stages - 1; i > 0; --i) {
_data[i] = _data[i - 1];
}
_data[0]._cycle_data = _data[0]._cycle_data->make_copy();
}
////////////////////////////////////////////////////////////////////
@ -110,8 +183,11 @@ cycle() {
////////////////////////////////////////////////////////////////////
void PipelineCyclerTrueImpl::
set_num_stages(int num_stages) {
nassertv(_lock.debug_is_locked());
if (num_stages <= _num_stages) {
// Don't bother to reallocate the array.
// Don't bother to reallocate the array smaller; we just won't use
// the rest of the array.
for (int i = _num_stages; i < num_stages; ++i) {
_data[i]._cycle_data.clear();
}
@ -120,14 +196,14 @@ set_num_stages(int num_stages) {
} else {
// Increase the array.
// To increase the array, we must reallocate it larger.
StageData *new_data = new StageData[num_stages];
int i;
for (i = 0; i < _num_stages; ++i) {
new_data[i] = _data[i];
}
for (i = _num_stages; i < num_stages; ++i) {
new_data[i]._cycle_data = _data[_num_stages - 1]._cycle_data->make_copy();
new_data[i]._cycle_data = _data[_num_stages - 1]._cycle_data;
}
delete[] _data;

View File

@ -26,6 +26,7 @@
#include "cycleData.h"
#include "pointerTo.h"
#include "thread.h"
#include "reMutex.h"
class Pipeline;
@ -61,7 +62,7 @@ public:
INLINE int get_num_stages();
INLINE bool is_stage_unique(int n) const;
INLINE CycleData *write_stage(int n);
CycleData *write_stage(int n);
INLINE void release_write_stage(int n, CycleData *pointer);
INLINE CycleData *cheat() const;
@ -83,6 +84,8 @@ private:
StageData *_data;
int _num_stages;
ReMutex _lock;
friend class Pipeline;
};