diff --git a/panda/src/cull/drawCullHandler.cxx b/panda/src/cull/drawCullHandler.cxx index cc7e6f556d..3a3e621b42 100644 --- a/panda/src/cull/drawCullHandler.cxx +++ b/panda/src/cull/drawCullHandler.cxx @@ -22,6 +22,7 @@ #include "transformState.h" #include "renderState.h" #include "graphicsStateGuardianBase.h" +#include "config_pgraph.h" //////////////////////////////////////////////////////////////////// @@ -35,10 +36,16 @@ void DrawCullHandler:: record_object(CullableObject *object, const CullTraverser *traverser) { // Munge vertices as needed for the GSG's requirements, and the // object's current state. + bool force = !allow_incomplete_render; Thread *current_thread = traverser->get_current_thread(); - object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser); - // And draw the object, then dispense with it. - draw(object, _gsg, current_thread); + if (object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser, force)) { + if (force || object->request_resident()) { + // Now we can immediately draw the object. + draw(object, _gsg, current_thread); + } + } + + // Dispense with the object. delete object; } diff --git a/panda/src/display/standardMunger.cxx b/panda/src/display/standardMunger.cxx index bcc8cd0664..1a763ec5e1 100644 --- a/panda/src/display/standardMunger.cxx +++ b/panda/src/display/standardMunger.cxx @@ -169,8 +169,9 @@ munge_data_impl(const GeomVertexData *data) { // Access: Protected, Virtual // Description: Converts a Geom and/or its data as necessary. //////////////////////////////////////////////////////////////////// -bool StandardMunger:: -munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) { +void StandardMunger:: +munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, + Thread *) { int supported_geom_rendering = get_gsg()->get_supported_geom_rendering(); int unsupported_bits = geom->get_geom_rendering() & ~supported_geom_rendering; @@ -208,8 +209,6 @@ munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) { vertex_data = new_geom->get_vertex_data(); } } - - return true; } //////////////////////////////////////////////////////////////////// @@ -217,7 +216,7 @@ munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data, Thread *) { // Access: Protected, Virtual // Description: Converts a Geom and/or its data as necessary. //////////////////////////////////////////////////////////////////// -bool StandardMunger:: +void StandardMunger:: premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) { int supported_geom_rendering = get_gsg()->get_supported_geom_rendering(); @@ -256,8 +255,6 @@ premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) { vertex_data = new_geom->get_vertex_data(); } } - - return true; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/display/standardMunger.h b/panda/src/display/standardMunger.h index 1f69e84a43..ddb8432655 100644 --- a/panda/src/display/standardMunger.h +++ b/panda/src/display/standardMunger.h @@ -47,9 +47,9 @@ public: protected: virtual CPT(GeomVertexData) munge_data_impl(const GeomVertexData *data); virtual int compare_to_impl(const GeomMunger *other) const; - virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data, + virtual void munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data, Thread *current_thread); - virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data); + virtual void premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data); virtual int geom_compare_to_impl(const GeomMunger *other) const; virtual CPT(RenderState) munge_state_impl(const RenderState *state); diff --git a/panda/src/distort/projectionScreen.cxx b/panda/src/distort/projectionScreen.cxx index 67c3897b28..f79a64ee23 100644 --- a/panda/src/distort/projectionScreen.cxx +++ b/panda/src/distort/projectionScreen.cxx @@ -542,7 +542,7 @@ recompute_geom(Geom *geom, const LMatrix4f &rel_mat) { PT(GeomVertexData) modify_vdata = geom->modify_vertex_data(); // Maybe the vdata has animation that we should consider. - CPT(GeomVertexData) animated_vdata = geom->get_vertex_data(current_thread)->animate_vertices(current_thread); + CPT(GeomVertexData) animated_vdata = geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread); GeomVertexWriter texcoord(modify_vdata, _texcoord_name, current_thread); GeomVertexWriter color(modify_vdata, current_thread); diff --git a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx index d23368b437..0c185fd362 100644 --- a/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx +++ b/panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx @@ -930,9 +930,9 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) { (D3DPT_TRIANGLELIST, min_vertex, max_vertex, reader->get_num_primitives(), - reader->get_read_pointer(), + reader->get_read_pointer(true), index_type, - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } else { @@ -949,7 +949,7 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) { draw_primitive_up(D3DPT_TRIANGLELIST, reader->get_num_primitives(), reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } @@ -990,8 +990,8 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { (D3DPT_TRIANGLESTRIP, min_vertex, max_vertex, reader->get_num_vertices() - 2, - reader->get_read_pointer(), index_type, - _data_reader->get_array_reader(0)->get_read_pointer(), + reader->get_read_pointer(true), index_type, + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } else { @@ -1008,7 +1008,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { reader->get_num_vertices() - 2, reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } @@ -1050,9 +1050,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { } else { // Indexed, client arrays, individual triangle strips. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); - const unsigned char *vertices = reader->get_read_pointer(); + const unsigned char *vertices = reader->get_read_pointer(true); D3DFORMAT index_type = get_index_type(reader->get_index_type()); unsigned int start = 0; @@ -1087,7 +1087,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { } else { // Nonindexed, client arrays, individual triangle strips. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); unsigned int start = 0; @@ -1150,9 +1150,9 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) { } else { // Indexed, client arrays. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); - const unsigned char *vertices = reader->get_read_pointer(); + const unsigned char *vertices = reader->get_read_pointer(true); D3DFORMAT index_type = get_index_type(reader->get_index_type()); unsigned int start = 0; @@ -1187,7 +1187,7 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) { } else { // Nonindexed, client arrays. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); unsigned int start = 0; @@ -1238,9 +1238,9 @@ draw_lines(const GeomPrimitivePipelineReader *reader) { (D3DPT_LINELIST, min_vertex, max_vertex, reader->get_num_primitives(), - reader->get_read_pointer(), + reader->get_read_pointer(true), index_type, - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } else { @@ -1256,7 +1256,7 @@ draw_lines(const GeomPrimitivePipelineReader *reader) { draw_primitive_up(D3DPT_LINELIST, reader->get_num_primitives(), reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } @@ -1299,7 +1299,7 @@ draw_points(const GeomPrimitivePipelineReader *reader) { draw_primitive_up(D3DPT_POINTLIST, reader->get_num_primitives(), reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index d36a83f4cd..adc10f0511 100755 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -1493,9 +1493,9 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) { (D3DPT_TRIANGLELIST, min_vertex, max_vertex, reader->get_num_primitives(), - reader->get_read_pointer(), + reader->get_read_pointer(true), index_type, - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } else { @@ -1517,7 +1517,7 @@ draw_triangles(const GeomPrimitivePipelineReader *reader) { draw_primitive_up(D3DPT_TRIANGLELIST, reader->get_num_primitives(), reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } @@ -1568,8 +1568,8 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { (D3DPT_TRIANGLESTRIP, min_vertex, max_vertex, reader->get_num_vertices() - 2, - reader->get_read_pointer(), index_type, - _data_reader->get_array_reader(0)->get_read_pointer(), + reader->get_read_pointer(true), index_type, + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } else { @@ -1589,7 +1589,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { reader->get_num_vertices() - 2, reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } @@ -1632,9 +1632,9 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { } else { // Indexed, client arrays, individual triangle strips. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); - const unsigned char *vertices = reader->get_read_pointer(); + const unsigned char *vertices = reader->get_read_pointer(true); D3DFORMAT index_type = get_index_type(reader->get_index_type()); unsigned int start = 0; @@ -1669,7 +1669,7 @@ draw_tristrips(const GeomPrimitivePipelineReader *reader) { } else { // Nonindexed, client arrays, individual triangle strips. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); unsigned int start = 0; @@ -1735,9 +1735,9 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) { } else { // Indexed, client arrays. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); - const unsigned char *vertices = reader->get_read_pointer(); + const unsigned char *vertices = reader->get_read_pointer(true); D3DFORMAT index_type = get_index_type(reader->get_index_type()); unsigned int start = 0; @@ -1772,7 +1772,7 @@ draw_trifans(const GeomPrimitivePipelineReader *reader) { } else { // Nonindexed, client arrays. - const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(); + const unsigned char *array_data = _data_reader->get_array_reader(0)->get_read_pointer(true); int stride = _data_reader->get_format()->get_array(0)->get_stride(); unsigned int start = 0; @@ -1824,9 +1824,9 @@ draw_lines(const GeomPrimitivePipelineReader *reader) { (D3DPT_LINELIST, min_vertex, max_vertex, reader->get_num_primitives(), - reader->get_read_pointer(), + reader->get_read_pointer(true), index_type, - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } else { @@ -1842,7 +1842,7 @@ draw_lines(const GeomPrimitivePipelineReader *reader) { draw_primitive_up(D3DPT_LINELIST, reader->get_num_primitives(), reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } @@ -1885,7 +1885,7 @@ draw_points(const GeomPrimitivePipelineReader *reader) { draw_primitive_up(D3DPT_POINTLIST, reader->get_num_primitives(), reader->get_first_vertex(), reader->get_num_vertices(), - _data_reader->get_array_reader(0)->get_read_pointer(), + _data_reader->get_array_reader(0)->get_read_pointer(true), _data_reader->get_format()->get_array(0)->get_stride()); } } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 8684acd78b..3b210bb762 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -2745,12 +2745,12 @@ apply_vertex_buffer(VertexBufferContext *vbc, if (num_bytes != 0) { if (gvbc->changed_size(reader) || gvbc->changed_usage_hint(reader)) { _glBufferData(GL_ARRAY_BUFFER, num_bytes, - reader->get_read_pointer(), + reader->get_read_pointer(true), get_usage(reader->get_usage_hint())); } else { _glBufferSubData(GL_ARRAY_BUFFER, 0, num_bytes, - reader->get_read_pointer()); + reader->get_read_pointer(true)); } _data_transferred_pcollector.add_level(num_bytes); } @@ -2819,7 +2819,7 @@ const unsigned char *CLP(GraphicsStateGuardian):: setup_array_data(const GeomVertexArrayDataHandle *array_reader) { if (!_supports_buffers) { // No support for buffer objects; always render from client. - return array_reader->get_read_pointer(); + return array_reader->get_read_pointer(true); } if (!vertex_buffers || _geom_display_list != 0 || array_reader->get_usage_hint() == Geom::UH_client) { @@ -2833,12 +2833,12 @@ setup_array_data(const GeomVertexArrayDataHandle *array_reader) { _glBindBuffer(GL_ARRAY_BUFFER, 0); _current_vbuffer_index = 0; } - return array_reader->get_read_pointer(); + return array_reader->get_read_pointer(true); } // Prepare the buffer object and bind it. VertexBufferContext *vbc = ((GeomVertexArrayData *)array_reader->get_object())->prepare_now(get_prepared_objects(), this); - nassertr(vbc != (VertexBufferContext *)NULL, array_reader->get_read_pointer()); + nassertr(vbc != (VertexBufferContext *)NULL, array_reader->get_read_pointer(true)); apply_vertex_buffer(vbc, array_reader); // NULL is the OpenGL convention for the first byte of the buffer object. @@ -2915,12 +2915,12 @@ apply_index_buffer(IndexBufferContext *ibc, if (num_bytes != 0) { if (gibc->changed_size(reader) || gibc->changed_usage_hint(reader)) { _glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_bytes, - reader->get_read_pointer(), + reader->get_read_pointer(true), get_usage(reader->get_usage_hint())); } else { _glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, num_bytes, - reader->get_read_pointer()); + reader->get_read_pointer(true)); } _data_transferred_pcollector.add_level(num_bytes); } @@ -2988,7 +2988,7 @@ const unsigned char *CLP(GraphicsStateGuardian):: setup_primitive(const GeomPrimitivePipelineReader *reader) { if (!_supports_buffers) { // No support for buffer objects; always render from client. - return reader->get_read_pointer(); + return reader->get_read_pointer(true); } if (!vertex_buffers || _geom_display_list != 0 || reader->get_usage_hint() == Geom::UH_client) { @@ -3002,12 +3002,12 @@ setup_primitive(const GeomPrimitivePipelineReader *reader) { _glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); _current_ibuffer_index = 0; } - return reader->get_read_pointer(); + return reader->get_read_pointer(true); } // Prepare the buffer object and bind it. IndexBufferContext *ibc = ((GeomPrimitive *)reader->get_object())->prepare_now(get_prepared_objects(), this); - nassertr(ibc != (IndexBufferContext *)NULL, reader->get_read_pointer()); + nassertr(ibc != (IndexBufferContext *)NULL, reader->get_read_pointer(true)); apply_index_buffer(ibc, reader); // NULL is the OpenGL convention for the first byte of the buffer object. diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 2e8c2bd231..d84ff60e4d 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -710,6 +710,35 @@ get_num_bytes() const { return num_bytes; } +//////////////////////////////////////////////////////////////////// +// Function: Geom::request_resident +// Access: Published +// Description: Returns true if all the primitive arrays are +// currently resident in memory. If this returns false, +// the data will be brought back into memory shortly; +// try again later. +// +// This does not also test the Geom's associated +// GeomVertexData. That must be tested separately. +//////////////////////////////////////////////////////////////////// +bool Geom:: +request_resident() const { + CDReader cdata(_cycler); + + bool resident = true; + + Primitives::const_iterator pi; + for (pi = cdata->_primitives.begin(); + pi != cdata->_primitives.end(); + ++pi) { + if (!(*pi).get_read_pointer()->request_resident()) { + resident = false; + } + } + + return resident; +} + //////////////////////////////////////////////////////////////////// // Function: Geom::transform_vertices // Access: Published @@ -1078,7 +1107,7 @@ compute_internal_bounds(Geom::CData *cdata, Thread *current_thread) const { // Get the vertex data, after animation. CPT(GeomVertexData) vertex_data = cdata->_data.get_read_pointer(); - vertex_data = vertex_data->animate_vertices(current_thread); + vertex_data = vertex_data->animate_vertices(true, current_thread); // Now actually compute the bounding volume. We do this by using // calc_tight_bounds to determine our box first. diff --git a/panda/src/gobj/geom.h b/panda/src/gobj/geom.h index 9746bd35c1..d6597682ff 100644 --- a/panda/src/gobj/geom.h +++ b/panda/src/gobj/geom.h @@ -110,6 +110,8 @@ PUBLISHED: int get_num_bytes() const; INLINE UpdateSeq get_modified(Thread *current_thread = Thread::get_current_thread()) const; + bool request_resident() const; + void transform_vertices(const LMatrix4f &mat); bool check_valid() const; bool check_valid(const GeomVertexData *vertex_data) const; diff --git a/panda/src/gobj/geomMunger.cxx b/panda/src/gobj/geomMunger.cxx index bdfeff53ec..374e9d5223 100644 --- a/panda/src/gobj/geomMunger.cxx +++ b/panda/src/gobj/geomMunger.cxx @@ -104,10 +104,15 @@ remove_data(const GeomVertexData *data) { // The assumption is that for a particular geom and a // particular munger, the result will always be the // same; so this result may be cached. +// +// If force is false, this may do nothing and return +// false if the vertex data is nonresident. If force is +// true, this will always return true, but it may have +// to block while the vertex data is paged in. //////////////////////////////////////////////////////////////////// -void GeomMunger:: +bool GeomMunger:: munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, - Thread *current_thread) { + bool force, Thread *current_thread) { CPT(GeomVertexData) source_data = data; // Look up the munger in the geom's cache--maybe we've recently @@ -123,7 +128,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, } else { entry = (*ci).second; geom->_cache_lock.release(); - nassertv(entry->_source == geom); + nassertr(entry->_source == geom, false); // Here's an element in the cache for this computation. Record a // cache hit, so this element will stay in the cache a while @@ -140,7 +145,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, geom = cdata->_geom_result; data = cdata->_data_result; - return; + return true; } // The cache entry is stale, but we'll recompute it below. Note @@ -149,6 +154,11 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, // compute the same result. } + if (!force && (!geom->request_resident() || !data->request_resident())) { + // Oh dear, the data isn't resident. We can't munge it, so give up. + return false; + } + // Ok, invoke the munger. PStatTimer timer(_munge_pcollector, current_thread); @@ -166,7 +176,7 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, if (!inserted) { // Some other thread must have beat us to the punch. Never // mind. - return; + return true; } } @@ -180,6 +190,8 @@ munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, Geom::CDCacheWriter cdata(entry->_cycler, true, current_thread); cdata->_source = (Geom *)orig_geom.p(); cdata->set_result(geom, data); + + return true; } //////////////////////////////////////////////////////////////////// @@ -255,11 +267,10 @@ munge_data_impl(const GeomVertexData *data) { // Access: Protected, Virtual // Description: Converts a Geom and/or its data as necessary. //////////////////////////////////////////////////////////////////// -bool GeomMunger:: +void GeomMunger:: munge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &, Thread *) { // The default implementation does nothing (the work has already // been done in munge_format_impl() and munge_data_impl()). - return true; } //////////////////////////////////////////////////////////////////// @@ -331,11 +342,10 @@ premunge_data_impl(const GeomVertexData *data) { // Access: Protected, Virtual // Description: Converts a Geom and/or its data as necessary. //////////////////////////////////////////////////////////////////// -bool GeomMunger:: +void GeomMunger:: premunge_geom_impl(CPT(Geom) &, CPT(GeomVertexData) &) { // The default implementation does nothing (the work has already // been done in premunge_format_impl() and premunge_data_impl()). - return true; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/geomMunger.h b/panda/src/gobj/geomMunger.h index d81487f956..768f53633e 100644 --- a/panda/src/gobj/geomMunger.h +++ b/panda/src/gobj/geomMunger.h @@ -76,8 +76,8 @@ public: INLINE CPT(GeomVertexData) munge_data(const GeomVertexData *data) const; void remove_data(const GeomVertexData *data); - void munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, - Thread *current_thread); + bool munge_geom(CPT(Geom) &geom, CPT(GeomVertexData) &data, + bool force, Thread *current_thread); INLINE CPT(GeomVertexFormat) premunge_format(const GeomVertexFormat *format) const; INLINE CPT(GeomVertexData) premunge_data(const GeomVertexData *data) const; @@ -96,14 +96,14 @@ protected: virtual CPT(GeomVertexFormat) munge_format_impl(const GeomVertexFormat *orig, const GeomVertexAnimationSpec &animation); virtual CPT(GeomVertexData) munge_data_impl(const GeomVertexData *data); - virtual bool munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data, + virtual void munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data, Thread *current_thread); CPT(GeomVertexFormat) do_premunge_format(const GeomVertexFormat *format); virtual CPT(GeomVertexFormat) premunge_format_impl(const GeomVertexFormat *orig); virtual CPT(GeomVertexData) premunge_data_impl(const GeomVertexData *data); - virtual bool premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data); + virtual void premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &data); virtual int compare_to_impl(const GeomMunger *other) const; virtual int geom_compare_to_impl(const GeomMunger *other) const; diff --git a/panda/src/gobj/geomPrimitive.I b/panda/src/gobj/geomPrimitive.I index 94c02717ec..2feb8ea6ba 100644 --- a/panda/src/gobj/geomPrimitive.I +++ b/panda/src/gobj/geomPrimitive.I @@ -666,8 +666,8 @@ get_vertices_reader() const { // Description: //////////////////////////////////////////////////////////////////// INLINE const unsigned char *GeomPrimitivePipelineReader:: -get_read_pointer() const { - return _vertices_reader->get_read_pointer(); +get_read_pointer(bool force) const { + return _vertices_reader->get_read_pointer(force); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index 85a8e1bc45..2be40b5858 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -798,6 +798,39 @@ get_num_bytes() const { return num_bytes; } +//////////////////////////////////////////////////////////////////// +// Function: GeomPrimitive::request_resident +// Access: Published +// Description: Returns true if the primitive data is currently +// resident in memory. If this returns false, the +// primitive data will be brought back into memory +// shortly; try again later. +//////////////////////////////////////////////////////////////////// +bool GeomPrimitive:: +request_resident() const { + CDReader cdata(_cycler); + + bool resident = true; + + if (!cdata->_vertices.is_null() && + !cdata->_vertices.get_read_pointer()->request_resident()) { + resident = false; + } + + if (is_composite() && cdata->_got_minmax) { + if (!cdata->_mins.is_null() && + !cdata->_mins.get_read_pointer()->request_resident()) { + resident = false; + } + if (!cdata->_maxs.is_null() && + !cdata->_maxs.get_read_pointer()->request_resident()) { + resident = false; + } + } + + return resident; +} + //////////////////////////////////////////////////////////////////// // Function: GeomPrimitive::output // Access: Published, Virtual diff --git a/panda/src/gobj/geomPrimitive.h b/panda/src/gobj/geomPrimitive.h index fb67188fa7..c148bb398a 100644 --- a/panda/src/gobj/geomPrimitive.h +++ b/panda/src/gobj/geomPrimitive.h @@ -136,6 +136,8 @@ PUBLISHED: INLINE int get_data_size_bytes() const; INLINE UpdateSeq get_modified() const; + bool request_resident() const; + INLINE bool check_valid(const GeomVertexData *vertex_data) const; virtual void output(ostream &out) const; @@ -346,7 +348,7 @@ public: bool check_valid(const GeomVertexDataPipelineReader *data_reader) const; INLINE int get_index_stride() const; INLINE const GeomVertexArrayDataHandle *get_vertices_reader() const; - INLINE const unsigned char *get_read_pointer() const; + INLINE const unsigned char *get_read_pointer(bool force) const; INLINE CPTA_int get_ends() const; INLINE CPT(GeomVertexArrayData) get_mins() const; INLINE CPT(GeomVertexArrayData) get_maxs() const; diff --git a/panda/src/gobj/geomVertexArrayData.I b/panda/src/gobj/geomVertexArrayData.I index 4f1ce01493..6c4c072fe9 100644 --- a/panda/src/gobj/geomVertexArrayData.I +++ b/panda/src/gobj/geomVertexArrayData.I @@ -145,6 +145,21 @@ get_modified() const { return cdata->_modified; } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexArrayData::request_resident +// Access: Published +// Description: Returns true if the vertex data is currently resident +// in memory. If this returns true, the next call to +// get_handle()->get_read_pointer() will probably not +// block. If this returns false, the vertex data will +// be brought back into memory shortly; try again later. +//////////////////////////////////////////////////////////////////// +INLINE bool GeomVertexArrayData:: +request_resident() const { + CPT(GeomVertexArrayDataHandle) handle = get_handle(); + return (handle->get_read_pointer(false) != (const unsigned char *)NULL); +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexArrayData::get_handle // Access: Published @@ -379,7 +394,7 @@ get_object() { //////////////////////////////////////////////////////////////////// INLINE const unsigned char *GeomVertexArrayDataHandle:: get_read_pointer(bool force) const { - check_resident(); + mark_used(); return _cdata->_buffer.get_read_pointer(force); } @@ -453,7 +468,7 @@ get_modified() const { //////////////////////////////////////////////////////////////////// INLINE string GeomVertexArrayDataHandle:: get_data() const { - check_resident(); + mark_used(); return string((const char *)_cdata->_buffer.get_read_pointer(true), _cdata->_buffer.get_size()); } @@ -467,20 +482,19 @@ get_data() const { //////////////////////////////////////////////////////////////////// INLINE string GeomVertexArrayDataHandle:: get_subdata(size_t start, size_t size) const { - check_resident(); + mark_used(); start = min(start, _cdata->_buffer.get_size()); size = min(size, _cdata->_buffer.get_size() - start); return string((const char *)_cdata->_buffer.get_read_pointer(true) + start, size); } //////////////////////////////////////////////////////////////////// -// Function: GeomVertexArrayDataHandle::check_resident +// Function: GeomVertexArrayDataHandle::mark_used // Access: Published -// Description: Forces the vertex data into system RAM, if it is not -// already there; also, marks it recently-used. +// Description: Marks the array data recently-used. //////////////////////////////////////////////////////////////////// void GeomVertexArrayDataHandle:: -check_resident() const { +mark_used() const { _object->set_lru_size(_object->get_lru_size()); } diff --git a/panda/src/gobj/geomVertexArrayData.cxx b/panda/src/gobj/geomVertexArrayData.cxx index c9bdce73f7..7eafd0e131 100644 --- a/panda/src/gobj/geomVertexArrayData.cxx +++ b/panda/src/gobj/geomVertexArrayData.cxx @@ -642,7 +642,7 @@ fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) { unsigned char *GeomVertexArrayDataHandle:: get_write_pointer() { nassertr(_writable, NULL); - check_resident(); + mark_used(); _cdata->_modified = Geom::get_next_modified(); return _cdata->_buffer.get_write_pointer(); } @@ -655,7 +655,7 @@ get_write_pointer() { bool GeomVertexArrayDataHandle:: set_num_rows(int n) { nassertr(_writable, false); - check_resident(); + mark_used(); int stride = _object->_array_format->get_stride(); size_t new_size = n * stride; @@ -689,7 +689,7 @@ set_num_rows(int n) { bool GeomVertexArrayDataHandle:: unclean_set_num_rows(int n) { nassertr(_writable, false); - check_resident(); + mark_used(); int stride = _object->_array_format->get_stride(); size_t new_size = n * stride; @@ -719,8 +719,8 @@ unclean_set_num_rows(int n) { void GeomVertexArrayDataHandle:: copy_data_from(const GeomVertexArrayDataHandle *other) { nassertv(_writable); - check_resident(); - other->check_resident(); + mark_used(); + other->mark_used(); _cdata->_buffer.unclean_realloc(other->_cdata->_buffer.get_size()); memcpy(_cdata->_buffer.get_write_pointer(), @@ -747,8 +747,8 @@ copy_subdata_from(size_t to_start, size_t to_size, const GeomVertexArrayDataHandle *other, size_t from_start, size_t from_size) { nassertv(_writable); - check_resident(); - other->check_resident(); + mark_used(); + other->mark_used(); VertexDataBuffer &to_buffer = _cdata->_buffer; size_t to_buffer_orig_size = to_buffer.get_size(); @@ -779,7 +779,7 @@ copy_subdata_from(size_t to_start, size_t to_size, // Now copy the data. memcpy(to_buffer.get_write_pointer() + to_start, - other->get_read_pointer() + from_start, + other->get_read_pointer(true) + from_start, from_size); _cdata->_modified = Geom::get_next_modified(); @@ -798,7 +798,7 @@ copy_subdata_from(size_t to_start, size_t to_size, void GeomVertexArrayDataHandle:: set_data(const string &data) { nassertv(_writable); - check_resident(); + mark_used(); _cdata->_buffer.unclean_realloc(data.size()); memcpy(_cdata->_buffer.get_write_pointer(), data.data(), data.size()); @@ -823,7 +823,7 @@ set_data(const string &data) { void GeomVertexArrayDataHandle:: set_subdata(size_t start, size_t size, const string &data) { nassertv(_writable); - check_resident(); + mark_used(); VertexDataBuffer &to_buffer = _cdata->_buffer; size_t to_buffer_orig_size = to_buffer.get_size(); diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index 56c9e5829b..ff2ac9b491 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -96,6 +96,8 @@ PUBLISHED: void output(ostream &out) const; void write(ostream &out, int indent_level = 0) const; + INLINE bool request_resident() const; + INLINE CPT(GeomVertexArrayDataHandle) get_handle(Thread *current_thread = Thread::get_current_thread()) const; INLINE PT(GeomVertexArrayDataHandle) modify_handle(Thread *current_thread = Thread::get_current_thread()); @@ -258,7 +260,7 @@ public: INLINE const GeomVertexArrayData *get_object() const; INLINE GeomVertexArrayData *get_object(); - INLINE const unsigned char *get_read_pointer(bool force = true) const; + INLINE const unsigned char *get_read_pointer(bool force) const; unsigned char *get_write_pointer(); PUBLISHED: @@ -283,7 +285,7 @@ PUBLISHED: INLINE string get_subdata(size_t start, size_t size) const; void set_subdata(size_t start, size_t size, const string &data); - INLINE void check_resident() const; + INLINE void mark_used() const; private: ReMutexHolder _holder; diff --git a/panda/src/gobj/geomVertexData.cxx b/panda/src/gobj/geomVertexData.cxx index da3e85dc7f..00009a822f 100644 --- a/panda/src/gobj/geomVertexData.cxx +++ b/panda/src/gobj/geomVertexData.cxx @@ -415,6 +415,31 @@ set_slider_table(const SliderTable *table) { cdata->_animated_vertices_modified = UpdateSeq(); } +//////////////////////////////////////////////////////////////////// +// Function: GeomVertexData::request_resident +// Access: Published +// Description: Returns true if the vertex data is currently resident +// in memory. If this returns false, the vertex data will +// be brought back into memory shortly; try again later. +//////////////////////////////////////////////////////////////////// +bool GeomVertexData:: +request_resident() const { + CDReader cdata(_cycler); + + bool resident = true; + + Arrays::const_iterator ai; + for (ai = cdata->_arrays.begin(); + ai != cdata->_arrays.end(); + ++ai) { + if (!(*ai).get_read_pointer()->request_resident()) { + resident = false; + } + } + + return resident; +} + //////////////////////////////////////////////////////////////////// // Function: GeomVertexData::copy_from // Access: Published @@ -489,7 +514,7 @@ copy_from(const GeomVertexData *source, bool keep_data_objects, for (source_i = 0; source_i < num_arrays; ++source_i) { CPT(GeomVertexArrayData) array_obj = source->get_array(source_i); CPT(GeomVertexArrayDataHandle) array_handle = array_obj->get_handle(); - const unsigned char *array_data = array_handle->get_read_pointer(); + const unsigned char *array_data = array_handle->get_read_pointer(true); const GeomVertexArrayFormat *source_array_format = source_format->get_array(source_i); int num_columns = source_array_format->get_num_columns(); for (int di = 0; di < num_columns; ++di) { @@ -660,7 +685,7 @@ copy_row_from(int dest_row, const GeomVertexData *source, CPT(GeomVertexArrayData) source_array_obj = source->get_array(i); CPT(GeomVertexArrayDataHandle) source_array_handle = source_array_obj->get_handle(); - const unsigned char *source_array_data = source_array_handle->get_read_pointer(); + const unsigned char *source_array_data = source_array_handle->get_read_pointer(true); const GeomVertexArrayFormat *array_format = source_format->get_array(i); int stride = array_format->get_stride(); @@ -915,9 +940,15 @@ set_color(const Colorf &color, int num_components, // still return the same pointer, but with its contents // modified (this is preferred, since it allows the // graphics backend to update vertex buffers optimally). +// +// If force is false, this method may return immediately +// with stale data, if the vertex data is not completely +// resident. If force is true, this method will never +// return stale data, but may block until the data is +// available. //////////////////////////////////////////////////////////////////// CPT(GeomVertexData) GeomVertexData:: -animate_vertices(Thread *current_thread) const { +animate_vertices(bool force, Thread *current_thread) const { CDLockedReader cdata(_cycler, current_thread); if (cdata->_format->get_animation().get_animation_type() != AT_panda) { @@ -948,6 +979,16 @@ animate_vertices(Thread *current_thread) const { // No changes. return cdata->_animated_vertices; } + + if (!force && !request_resident()) { + // The vertex data isn't resident. Return the best information + // we've got. + if (cdata->_animated_vertices != (GeomVertexData *)NULL) { + return cdata->_animated_vertices; + } + return this; + } + CDWriter cdataw(((GeomVertexData *)this)->_cycler, cdata, false); cdataw->_animated_vertices_modified = modified; ((GeomVertexData *)this)->update_animated_vertices(cdataw, current_thread); diff --git a/panda/src/gobj/geomVertexData.h b/panda/src/gobj/geomVertexData.h index 5bf0a27bbe..953a31f321 100644 --- a/panda/src/gobj/geomVertexData.h +++ b/panda/src/gobj/geomVertexData.h @@ -130,6 +130,8 @@ PUBLISHED: INLINE int get_num_bytes() const; INLINE UpdateSeq get_modified(Thread *current_thread = Thread::get_current_thread()) const; + bool request_resident() const; + void copy_from(const GeomVertexData *source, bool keep_data_objects, Thread *current_thread = Thread::get_current_thread()); void copy_row_from(int dest_row, const GeomVertexData *source, @@ -146,7 +148,7 @@ PUBLISHED: set_color(const Colorf &color, int num_components, NumericType numeric_type, Contents contents) const; - CPT(GeomVertexData) animate_vertices(Thread *current_thread) const; + CPT(GeomVertexData) animate_vertices(bool force, Thread *current_thread) const; PT(GeomVertexData) replace_column(InternalName *name, int num_components, diff --git a/panda/src/gobj/geomVertexFormat.cxx b/panda/src/gobj/geomVertexFormat.cxx index fea14de2f9..ec40d3d964 100644 --- a/panda/src/gobj/geomVertexFormat.cxx +++ b/panda/src/gobj/geomVertexFormat.cxx @@ -576,7 +576,7 @@ write_with_data(ostream &out, int indent_level, << data->get_num_rows() << " rows.\n"; for (size_t i = 0; i < _arrays.size(); i++) { CPT(GeomVertexArrayDataHandle) handle = data->get_array(i)->get_handle(); - const unsigned char *array_data = handle->get_read_pointer(); + const unsigned char *array_data = handle->get_read_pointer(true); indent(out, indent_level) << "Array " << i << " (" << (void *)array_data << ", " << *_arrays[i] << "):\n"; diff --git a/panda/src/gobj/geomVertexReader.I b/panda/src/gobj/geomVertexReader.I index 545a6f8d2c..3502d05c0d 100644 --- a/panda/src/gobj/geomVertexReader.I +++ b/panda/src/gobj/geomVertexReader.I @@ -508,7 +508,7 @@ get_packer() const { //////////////////////////////////////////////////////////////////// INLINE void GeomVertexReader:: set_pointer(int row) { - _pointer_begin = _handle->get_read_pointer(); + _pointer_begin = _handle->get_read_pointer(true); _pointer_end = _pointer_begin + _handle->get_data_size_bytes(); quick_set_pointer(row); } @@ -526,7 +526,7 @@ quick_set_pointer(int row) { #if defined(_DEBUG) // Make sure we still have the same pointer as stored in the array. - nassertv(_pointer_begin == _handle->get_read_pointer()); + nassertv(_pointer_begin == _handle->get_read_pointer(true)); #endif _pointer = _pointer_begin + _packer->_column->get_start() + _stride * row; @@ -547,7 +547,7 @@ inc_pointer() { #if defined(_DEBUG) nassertr(_pointer < _pointer_end, empty_buffer); // Make sure we still have the same pointer as stored in the array. - nassertr(_pointer_begin == _handle->get_read_pointer(), empty_buffer); + nassertr(_pointer_begin == _handle->get_read_pointer(true), empty_buffer); nassertr(_pointer < _pointer_begin + _handle->get_data_size_bytes(), empty_buffer); #endif diff --git a/panda/src/gobj/vertexDataBook.I b/panda/src/gobj/vertexDataBook.I index 29c60f7e71..5989d42d96 100644 --- a/panda/src/gobj/vertexDataBook.I +++ b/panda/src/gobj/vertexDataBook.I @@ -48,5 +48,5 @@ get_page(int n) const { INLINE VertexDataPage *VertexDataBook:: create_new_page(size_t size) { size_t page_size = ((size + _block_size - 1) / _block_size) * _block_size; - return new VertexDataPage(page_size); + return new VertexDataPage(this, page_size); } diff --git a/panda/src/gobj/vertexDataBook.cxx b/panda/src/gobj/vertexDataBook.cxx index 2a4c1a728d..491b1e2c16 100644 --- a/panda/src/gobj/vertexDataBook.cxx +++ b/panda/src/gobj/vertexDataBook.cxx @@ -49,21 +49,26 @@ alloc(size_t size) { MutexHolder holder(_lock); // First, try to allocate from the last page that worked; then - // continue to the end of the list. + // continue to the end of the list. We consider only pages that are + // currently resident (or that are empty), to minimize unnecessary + // swapping. size_t pi = _next_pi; while (pi < _pages.size()) { - VertexDataBlock *block = _pages[pi]->alloc(size); - if (block != (VertexDataBlock *)NULL) { - _next_pi = pi; - return block; - } - if (_pages[pi]->is_empty()) { - // This page is empty, but must have been too small. Create a - // new page in its place. - delete _pages[pi]; - _pages[pi] = create_new_page(size); + if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident || + _pages[pi]->is_empty()) { VertexDataBlock *block = _pages[pi]->alloc(size); - return block; + if (block != (VertexDataBlock *)NULL) { + _next_pi = pi; + return block; + } + if (_pages[pi]->is_empty()) { + // This page is empty, but must have been too small. Create a + // new page in its place. + delete _pages[pi]; + _pages[pi] = create_new_page(size); + VertexDataBlock *block = _pages[pi]->alloc(size); + return block; + } } ++pi; } @@ -72,17 +77,20 @@ alloc(size_t size) { pi = 0; _next_pi = min(_next_pi, _pages.size()); while (pi < _next_pi) { - VertexDataBlock *block = _pages[pi]->alloc(size); - if (block != (VertexDataBlock *)NULL) { - _next_pi = pi; - return block; - } - if (_pages[pi]->is_empty()) { - // This page is empty, but must have been too small. Create a - // new page in its place. - delete _pages[pi]; - _pages[pi] = create_new_page(size); - return _pages[pi]->alloc(size); + if (_pages[pi]->get_ram_class() == VertexDataPage::RC_resident || + _pages[pi]->is_empty()) { + VertexDataBlock *block = _pages[pi]->alloc(size); + if (block != (VertexDataBlock *)NULL) { + _next_pi = pi; + return block; + } + if (_pages[pi]->is_empty()) { + // This page is empty, but must have been too small. Create a + // new page in its place. + delete _pages[pi]; + _pages[pi] = create_new_page(size); + return _pages[pi]->alloc(size); + } } ++pi; } @@ -95,6 +103,76 @@ alloc(size_t size) { return block; } +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBook::count_total_page_size +// Access: Published +// Description: Returns the total size of all bytes owned by all +// pages owned by this book. +//////////////////////////////////////////////////////////////////// +size_t VertexDataBook:: +count_total_page_size() const { + size_t total = 0; + Pages::const_iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + total += (*pi)->get_max_size(); + } + return total; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBook::count_total_page_size +// Access: Published +// Description: Returns the total size of all bytes owned by all +// pages owned by this book that have the indicated ram +// class. +//////////////////////////////////////////////////////////////////// +size_t VertexDataBook:: +count_total_page_size(VertexDataPage::RamClass ram_class) const { + size_t total = 0; + Pages::const_iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + if ((*pi)->get_ram_class() == ram_class) { + total += (*pi)->get_max_size(); + } + } + return total; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBook::count_allocated_size +// Access: Published +// Description: Returns the total size of all bytes allocated within +// pages owned by this book. +//////////////////////////////////////////////////////////////////// +size_t VertexDataBook:: +count_allocated_size() const { + size_t total = 0; + Pages::const_iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + total += (*pi)->get_total_size(); + } + return total; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBook::count_allocated_size +// Access: Published +// Description: Returns the total size of all bytes allocated within +// pages owned by this book that have the indicated ram +// class. +//////////////////////////////////////////////////////////////////// +size_t VertexDataBook:: +count_allocated_size(VertexDataPage::RamClass ram_class) const { + size_t total = 0; + Pages::const_iterator pi; + for (pi = _pages.begin(); pi != _pages.end(); ++pi) { + if ((*pi)->get_ram_class() == ram_class) { + total += (*pi)->get_total_size(); + } + } + return total; +} + //////////////////////////////////////////////////////////////////// // Function: VertexDataBook::save_to_disk // Access: Published diff --git a/panda/src/gobj/vertexDataBook.h b/panda/src/gobj/vertexDataBook.h index cbdcea2e39..95a43515e2 100644 --- a/panda/src/gobj/vertexDataBook.h +++ b/panda/src/gobj/vertexDataBook.h @@ -41,6 +41,11 @@ PUBLISHED: INLINE int get_num_pages() const; INLINE VertexDataPage *get_page(int n) const; + size_t count_total_page_size() const; + size_t count_total_page_size(VertexDataPage::RamClass ram_class) const; + size_t count_allocated_size() const; + size_t count_allocated_size(VertexDataPage::RamClass ram_class) const; + void save_to_disk(); private: diff --git a/panda/src/gobj/vertexDataBuffer.I b/panda/src/gobj/vertexDataBuffer.I index 83e7d8bf56..994dfcb135 100644 --- a/panda/src/gobj/vertexDataBuffer.I +++ b/panda/src/gobj/vertexDataBuffer.I @@ -55,19 +55,6 @@ VertexDataBuffer(const VertexDataBuffer ©) : (*this) = copy; } -//////////////////////////////////////////////////////////////////// -// Function: VertexDataBuffer::Copy Assignment Operator -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -INLINE void VertexDataBuffer:: -operator = (const VertexDataBuffer ©) { - MutexHolder holder(_lock); - - do_unclean_realloc(copy.get_size()); - memcpy(_resident_data, copy.get_read_pointer(true), _size); -} - //////////////////////////////////////////////////////////////////// // Function: VertexDataBuffer::Destructor // Access: Public @@ -93,14 +80,16 @@ INLINE const unsigned char *VertexDataBuffer:: get_read_pointer(bool force) const { MutexHolder holder(_lock); - if (_block != (VertexDataBlock *)NULL) { - // We don't necessarily need to page the buffer all the way into - // independent status; it's sufficient just to return the block's - // pointer, which will force its page to resident status. - return _block->get_pointer(force); + if (_resident_data != (unsigned char *)NULL || _size == 0) { + return _resident_data; } - return _resident_data; + nassertr(_block != (VertexDataBlock *)NULL, NULL); + + // We don't necessarily need to page the buffer all the way into + // independent status; it's sufficient just to return the block's + // pointer, which will force its page to resident status. + return _block->get_pointer(force); } //////////////////////////////////////////////////////////////////// @@ -112,8 +101,7 @@ INLINE unsigned char *VertexDataBuffer:: get_write_pointer() { MutexHolder holder(_lock); - if (_block != (VertexDataBlock *)NULL || - _resident_data == (unsigned char *)NULL) { + if (_resident_data == (unsigned char *)NULL && _size != 0) { do_page_in(); } return _resident_data; @@ -205,19 +193,3 @@ swap(VertexDataBuffer &other) { other._size = size; other._block = block; } - -//////////////////////////////////////////////////////////////////// -// Function: VertexDataBuffer::do_unclean_realloc -// Access: Private -// Description: Changes the size of the buffer, without regard to -// preserving its data. The buffer may contain random -// data after this call. -// -// Assumes the lock is already held. -//////////////////////////////////////////////////////////////////// -INLINE void VertexDataBuffer:: -do_unclean_realloc(size_t size) { - // At the moment, this has no distinct definition, since the system - // realloc() call doesn't have an unclean variant. - do_clean_realloc(size); -} diff --git a/panda/src/gobj/vertexDataBuffer.cxx b/panda/src/gobj/vertexDataBuffer.cxx index f2e64e614a..48c655ade6 100644 --- a/panda/src/gobj/vertexDataBuffer.cxx +++ b/panda/src/gobj/vertexDataBuffer.cxx @@ -21,6 +21,32 @@ TypeHandle VertexDataBuffer::_type_handle; +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBuffer::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void VertexDataBuffer:: +operator = (const VertexDataBuffer ©) { + MutexHolder holder(_lock); + MutexHolder holder2(copy._lock); + + if (_resident_data != (unsigned char *)NULL) { + nassertv(_size != 0); + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); + free(_resident_data); + _resident_data = NULL; + } + if (copy._resident_data != (unsigned char *)NULL) { + nassertv(copy._size != 0); + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)copy._size); + _resident_data = (unsigned char *)malloc(copy._size); + memcpy(_resident_data, copy._resident_data, copy._size); + } + _size = copy._size; + _block = copy._block; +} + //////////////////////////////////////////////////////////////////// // Function: VertexDataBuffer::do_clean_realloc // Access: Private @@ -35,32 +61,56 @@ void VertexDataBuffer:: do_clean_realloc(size_t size) { if (size != _size) { if (size == 0) { - // If we're going to size 0, we don't necessarily need to page - // in first. But if we're paged out, discard the page. - _block = NULL; - - if (_resident_data != (unsigned char *)NULL) { - free(_resident_data); - _resident_data = NULL; - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); - } - _block = NULL; - + do_unclean_realloc(size); + return; + } + + // Page in if we're currently paged out. + if (_size != 0 && _resident_data == (unsigned char *)NULL) { + do_page_in(); + } + + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size - (int)_size); + if (_size == 0) { + nassertv(_resident_data == (unsigned char *)NULL); + _resident_data = (unsigned char *)malloc(size); } else { - // Page in if we're currently paged out. - if (_block != (VertexDataBlock *)NULL || - _resident_data == (unsigned char *)NULL) { - do_page_in(); - } - - if (_resident_data == (unsigned char *)NULL) { - _resident_data = (unsigned char *)malloc(size); - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size); - } else { - _resident_data = (unsigned char *)::realloc(_resident_data, size); - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size - (int)_size); - } nassertv(_resident_data != (unsigned char *)NULL); + _resident_data = (unsigned char *)realloc(_resident_data, size); + } + nassertv(_resident_data != (unsigned char *)NULL); + _size = size; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataBuffer::do_unclean_realloc +// Access: Private +// Description: Changes the size of the buffer, without regard to +// preserving its data. The buffer may contain random +// data after this call. +// +// Assumes the lock is already held. +//////////////////////////////////////////////////////////////////// +void VertexDataBuffer:: +do_unclean_realloc(size_t size) { + if (size != _size || _resident_data == (unsigned char *)NULL) { + // If we're paged out, discard the page. + _block = NULL; + + if (_resident_data != (unsigned char *)NULL) { + nassertv(_size != 0); + + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); + free(_resident_data); + _resident_data = NULL; + _size = 0; + } + + if (size != 0) { + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)size); + nassertv(_resident_data == (unsigned char *)NULL); + _resident_data = (unsigned char *)malloc(size); } _size = size; @@ -93,9 +143,9 @@ do_page_out(VertexDataBook &book) { nassertv(pointer != (unsigned char *)NULL); memcpy(pointer, _resident_data, _size); + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); free(_resident_data); _resident_data = NULL; - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); } //////////////////////////////////////////////////////////////////// @@ -109,17 +159,16 @@ do_page_out(VertexDataBook &book) { //////////////////////////////////////////////////////////////////// void VertexDataBuffer:: do_page_in() { - if (_block == (VertexDataBlock *)NULL) { + if (_resident_data != (unsigned char *)NULL || _size == 0) { // We're already paged in. return; } - nassertv(_resident_data == (unsigned char *)NULL); + nassertv(_block != (VertexDataBlock *)NULL); + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size); _resident_data = (unsigned char *)malloc(_size); nassertv(_resident_data != (unsigned char *)NULL); - get_class_type().inc_memory_usage(TypeHandle::MC_array, _size); - + memcpy(_resident_data, _block->get_pointer(true), _size); - _block = NULL; } diff --git a/panda/src/gobj/vertexDataBuffer.h b/panda/src/gobj/vertexDataBuffer.h index bcf13a011d..d118abea86 100644 --- a/panda/src/gobj/vertexDataBuffer.h +++ b/panda/src/gobj/vertexDataBuffer.h @@ -65,7 +65,7 @@ public: INLINE VertexDataBuffer(); INLINE VertexDataBuffer(size_t size); INLINE VertexDataBuffer(const VertexDataBuffer ©); - INLINE void operator = (const VertexDataBuffer ©); + void operator = (const VertexDataBuffer ©); INLINE ~VertexDataBuffer(); INLINE const unsigned char *get_read_pointer(bool force) const; @@ -82,7 +82,7 @@ public: private: void do_clean_realloc(size_t size); - INLINE void do_unclean_realloc(size_t size); + void do_unclean_realloc(size_t size); void do_page_out(VertexDataBook &book); void do_page_in(); diff --git a/panda/src/gobj/vertexDataPage.I b/panda/src/gobj/vertexDataPage.I index 91e8d6da20..abc0728253 100644 --- a/panda/src/gobj/vertexDataPage.I +++ b/panda/src/gobj/vertexDataPage.I @@ -72,14 +72,13 @@ get_first_block() const { } //////////////////////////////////////////////////////////////////// -// Function: VertexDataPage::get_total_page_size -// Access: Published, Static -// Description: Returns the byte count allocated to all -// VertexDataPages currently in existance. +// Function: VertexDataPage::get_book +// Access: Published +// Description: Returns a pointer to the book that owns this page. //////////////////////////////////////////////////////////////////// -INLINE size_t VertexDataPage:: -get_total_page_size() { - return _total_page_size; +INLINE VertexDataBook *VertexDataPage:: +get_book() const { + return _book; } //////////////////////////////////////////////////////////////////// @@ -155,7 +154,9 @@ get_page_data(bool force) { make_resident_now(); } else { request_ram_class(RC_resident); - return NULL; + if (_ram_class != RC_resident) { + return NULL; + } } } diff --git a/panda/src/gobj/vertexDataPage.cxx b/panda/src/gobj/vertexDataPage.cxx index 5fbb4bb9a4..3176c88258 100644 --- a/panda/src/gobj/vertexDataPage.cxx +++ b/panda/src/gobj/vertexDataPage.cxx @@ -19,6 +19,7 @@ #include "vertexDataPage.h" #include "configVariableInt.h" #include "vertexDataSaveFile.h" +#include "vertexDataBook.h" #include "pStatTimer.h" #include "mutexHolder.h" @@ -68,13 +69,13 @@ SimpleLru *VertexDataPage::_global_lru[RC_end_of_list] = { &VertexDataPage::_disk_lru, }; -size_t VertexDataPage::_total_page_size = 0; VertexDataSaveFile *VertexDataPage::_save_file; PStatCollector VertexDataPage::_vdata_compress_pcollector("*:Vertex Data:Compress"); PStatCollector VertexDataPage::_vdata_decompress_pcollector("*:Vertex Data:Decompress"); PStatCollector VertexDataPage::_vdata_save_pcollector("*:Vertex Data:Save"); PStatCollector VertexDataPage::_vdata_restore_pcollector("*:Vertex Data:Restore"); +PStatCollector VertexDataPage::_thread_wait_pcollector("*:Wait:Idle"); TypeHandle VertexDataPage::_type_handle; @@ -84,12 +85,18 @@ TypeHandle VertexDataPage::_type_handle; // Description: //////////////////////////////////////////////////////////////////// VertexDataPage:: -VertexDataPage(size_t page_size) : SimpleAllocator(page_size), SimpleLruPage(page_size) { - _page_data = new unsigned char[get_max_size()]; +VertexDataPage(VertexDataBook *book, size_t page_size) : + SimpleAllocator(page_size), + SimpleLruPage(page_size), + _book(book) +{ + nassertv(page_size == get_max_size()); + + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)page_size); + _page_data = new unsigned char[page_size]; _size = page_size; + _uncompressed_size = _size; - _total_page_size += _size; - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size); _pending_ram_class = RC_resident; set_ram_class(RC_resident); } @@ -101,11 +108,20 @@ VertexDataPage(size_t page_size) : SimpleAllocator(page_size), SimpleLruPage(pag //////////////////////////////////////////////////////////////////// VertexDataPage:: ~VertexDataPage() { - _total_page_size -= _size; - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); + MutexHolder holder(_lock); + + { + MutexHolder holder2(_tlock); + if (_pending_ram_class != _ram_class) { + nassertv(_thread != (PageThread *)NULL); + _thread->remove_page(this); + } + } if (_page_data != NULL) { + get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); delete[] _page_data; + _size = 0; } } @@ -121,15 +137,11 @@ VertexDataPage:: VertexDataBlock *VertexDataPage:: alloc(size_t size) { MutexHolder holder(_lock); - if (_ram_class != RC_resident) { - make_resident_now(); - } - VertexDataBlock *block = (VertexDataBlock *)SimpleAllocator::alloc(size); - if (block != (VertexDataBlock *)NULL) { - // When we allocate a new block within the page, we have to clear - // the disk cache (since we have just invalidated it). + if (block != (VertexDataBlock *)NULL && _ram_class != RC_disk) { + // When we allocate a new block within a resident page, we have to + // clear the disk cache (since we have just invalidated it). _saved_block.clear(); } @@ -269,15 +281,11 @@ make_resident() { } nassertv(dest_len == _uncompressed_size); - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); - _total_page_size -= _size; - + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_uncompressed_size - (int)_size); delete[] _page_data; _page_data = new_data; _size = _uncompressed_size; - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size); - _total_page_size += _size; #endif set_lru_size(_size); @@ -329,16 +337,11 @@ make_compressed() { unsigned char *new_data = new unsigned char[buffer_size]; memcpy(new_data, buffer, buffer_size); - get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); - _total_page_size -= _size; - + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size - (int)_size); delete[] _page_data; _page_data = new_data; _size = buffer_size; - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size); - _total_page_size += _size; - if (gobj_cat.is_debug()) { gobj_cat.debug() << "Compressed " << *this << " from " << _uncompressed_size @@ -376,8 +379,6 @@ make_disk() { } get_class_type().dec_memory_usage(TypeHandle::MC_array, (int)_size); - _total_page_size -= _size; - delete[] _page_data; _page_data = NULL; _size = 0; @@ -454,12 +455,11 @@ do_restore_from_disk() { nassert_raise("read error"); } + get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)buffer_size); + nassertv(_page_data == (unsigned char *)NULL); _page_data = new_data; _size = buffer_size; - get_class_type().inc_memory_usage(TypeHandle::MC_array, (int)_size); - _total_page_size += _size; - set_lru_size(_size); if (_saved_block->get_compressed()) { set_ram_class(RC_compressed); @@ -501,7 +501,11 @@ request_ram_class(RamClass ram_class) { case RC_disk: make_disk(); break; + + case RC_end_of_list: + break; } + _pending_ram_class = ram_class; return; } @@ -580,12 +584,15 @@ add_page(VertexDataPage *page, RamClass ram_class) { //////////////////////////////////////////////////////////////////// void VertexDataPage::PageThread:: remove_page(VertexDataPage *page) { + nassertv(page != (VertexDataPage *)NULL); if (page == _working_page) { // Oops, the thread is currently working on this one. We'll have // to wait for the thread to finish. + page->_lock.release(); while (page == _working_page) { _working_cvar.wait(); } + page->_lock.lock(); return; } @@ -621,6 +628,7 @@ thread_main() { _tlock.release(); return; } + PStatTimer timer(_thread_wait_pcollector); _pending_cvar.wait(); } @@ -650,6 +658,9 @@ thread_main() { case RC_disk: _working_page->make_disk(); break; + + case RC_end_of_list: + break; } } diff --git a/panda/src/gobj/vertexDataPage.h b/panda/src/gobj/vertexDataPage.h index 95e838d718..3cb03d2421 100644 --- a/panda/src/gobj/vertexDataPage.h +++ b/panda/src/gobj/vertexDataPage.h @@ -30,6 +30,7 @@ #include "mutexHolder.h" #include "pdeque.h" +class VertexDataBook; class VertexDataBlock; //////////////////////////////////////////////////////////////////// @@ -40,8 +41,9 @@ class VertexDataBlock; // cache file, if necessary. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA VertexDataPage : public SimpleAllocator, public SimpleLruPage { +public: + VertexDataPage(VertexDataBook *book, size_t page_size); PUBLISHED: - VertexDataPage(size_t page_size); ~VertexDataPage(); // These are used to indicate the current residency state of the @@ -62,7 +64,8 @@ PUBLISHED: VertexDataBlock *alloc(size_t size); INLINE VertexDataBlock *get_first_block() const; - INLINE static size_t get_total_page_size(); + INLINE VertexDataBook *get_book() const; + INLINE static SimpleLru *get_global_lru(RamClass rclass); INLINE static SimpleLru *get_pending_lru(); INLINE static VertexDataSaveFile *get_save_file(); @@ -126,19 +129,21 @@ private: RamClass _pending_ram_class; // Protected by _tlock. + VertexDataBook *_book; // never changes. + static SimpleLru _resident_lru; static SimpleLru _compressed_lru; static SimpleLru _disk_lru; static SimpleLru _pending_lru; static SimpleLru *_global_lru[RC_end_of_list]; - static size_t _total_page_size; static VertexDataSaveFile *_save_file; static PStatCollector _vdata_compress_pcollector; static PStatCollector _vdata_decompress_pcollector; static PStatCollector _vdata_save_pcollector; static PStatCollector _vdata_restore_pcollector; + static PStatCollector _thread_wait_pcollector; public: static TypeHandle get_class_type() { diff --git a/panda/src/grutil/multitexReducer.cxx b/panda/src/grutil/multitexReducer.cxx index 8ead100d5d..0726bf8f66 100644 --- a/panda/src/grutil/multitexReducer.cxx +++ b/panda/src/grutil/multitexReducer.cxx @@ -894,7 +894,7 @@ transfer_geom(GeomNode *geom_node, const InternalName *texcoord_name, PT(Geom) geom = orig_geom->make_copy(); // Ensure that any vertex animation has been applied. - geom->set_vertex_data(geom->get_vertex_data(current_thread)->animate_vertices(current_thread)); + geom->set_vertex_data(geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread)); // Now get a modifiable pointer to the vertex data in the new // Geom. This will actually perform a deep copy of the vertex diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index b04c2a3bf7..5201fffa64 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -319,6 +319,15 @@ ConfigVariableString default_model_extension "Panda's loader; new code should probably give the correct name " "for each model file they intend to load.")); +ConfigVariableBool allow_incomplete_render +("allow-incomplete-render", false, + PRC_DESC("When this is true, the frame may be rendered even if some of the " + "geometry in the scene has been paged out. The nonresident " + "geometry will be rendered as soon as it can be paged back in, " + "which may be several frames in the future. When this is false, " + "geometry is always paged in when needed, holding up the frame " + "render if necessary.")); + //////////////////////////////////////////////////////////////////// // Function: init_libpgraph // Description: Initializes the library. This must be called at diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index 5dc6860eb3..861e7755e8 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -69,6 +69,7 @@ extern ConfigVariableBool m_dual_flash; extern ConfigVariableList load_file_type; extern ConfigVariableString default_model_extension; +extern ConfigVariableBool allow_incomplete_render; extern EXPCL_PANDA void init_libpgraph(); diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index 39246a887e..cc64a7aeac 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -98,6 +98,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) { static const Colorf flash_multisample_color(0.78f, 0.05f, 0.81f, 1.0f); static const Colorf flash_dual_color(0.92f, 0.01f, 0.01f, 1.0f); + bool force = !allow_incomplete_render; Thread *current_thread = traverser->get_current_thread(); // Check to see if there's a special transparency setting. @@ -167,15 +168,18 @@ add_object(CullableObject *object, const CullTraverser *traverser) { get_dual_transparent_state_decals() : get_dual_transparent_state(); transparent_part->_state = state->compose(transparent_state); - transparent_part->munge_geom - (_gsg, _gsg->get_geom_munger(transparent_part->_state, current_thread), - traverser); - CullBin *bin = get_bin(transparent_part->_state->get_bin_index()); - nassertv(bin != (CullBin *)NULL); + if (transparent_part->munge_geom + (_gsg, _gsg->get_geom_munger(transparent_part->_state, current_thread), + traverser, force)) { + CullBin *bin = get_bin(transparent_part->_state->get_bin_index()); + nassertv(bin != (CullBin *)NULL); #ifndef NDEBUG - check_flash_bin(transparent_part->_state, bin); + check_flash_bin(transparent_part->_state, bin); #endif - bin->add_object(transparent_part, current_thread); + if (force || transparent_part->request_resident()) { + bin->add_object(transparent_part, current_thread); + } + } } // Now we can draw the opaque part, with decals. This will @@ -208,8 +212,11 @@ add_object(CullableObject *object, const CullTraverser *traverser) { // Munge vertices as needed for the GSG's requirements, and the // object's current state. - object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser); - bin->add_object(object, current_thread); + if (object->munge_geom(_gsg, _gsg->get_geom_munger(object->_state, current_thread), traverser, force)) { + if (force || object->request_resident()) { + bin->add_object(object, current_thread); + } + } } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/cullableObject.I b/panda/src/pgraph/cullableObject.I index 1c3ca3e790..63fa43c384 100644 --- a/panda/src/pgraph/cullableObject.I +++ b/panda/src/pgraph/cullableObject.I @@ -130,6 +130,26 @@ draw(GraphicsStateGuardianBase *gsg, Thread *current_thread) { _geom->draw(gsg, _munger, _munged_data, current_thread); } +//////////////////////////////////////////////////////////////////// +// Function: CullableObject::request_resident +// Access: Published +// Description: Returns true if all the data necessary to render this +// object is currently resident in memory. If this +// returns false, the data will be brought back into +// memory shortly; try again later. +//////////////////////////////////////////////////////////////////// +INLINE bool CullableObject:: +request_resident() const { + bool resident = true; + if (!_geom->request_resident()) { + resident = false; + } + if (!_munged_data->request_resident()) { + resident = false; + } + return resident; +} + //////////////////////////////////////////////////////////////////// // Function: CullableObject::flush_level // Access: Public, Static diff --git a/panda/src/pgraph/cullableObject.cxx b/panda/src/pgraph/cullableObject.cxx index 7202a79d37..13732022b1 100644 --- a/panda/src/pgraph/cullableObject.cxx +++ b/panda/src/pgraph/cullableObject.cxx @@ -50,10 +50,16 @@ TypeHandle CullableObject::_type_handle; // Access: Public // Description: Uses the indicated GeomMunger to transform the geom // and/or its vertices. +// +// If force is false, this may do nothing and return +// false if the vertex data is nonresident. If force is +// true, this will always return true, but it may have +// to block while the vertex data is paged in. //////////////////////////////////////////////////////////////////// -void CullableObject:: +bool CullableObject:: munge_geom(GraphicsStateGuardianBase *gsg, - GeomMunger *munger, const CullTraverser *traverser) { + GeomMunger *munger, const CullTraverser *traverser, + bool force) { Thread *current_thread = traverser->get_current_thread(); if (_geom != (Geom *)NULL) { _munger = munger; @@ -65,7 +71,7 @@ munge_geom(GraphicsStateGuardianBase *gsg, { GeomVertexDataPipelineReader data_reader(_munged_data, current_thread); data_reader.check_array_readers(); - nassertv(geom_reader.check_valid(&data_reader)); + nassertr(geom_reader.check_valid(&data_reader), false); } #endif // _DEBUG @@ -94,7 +100,9 @@ munge_geom(GraphicsStateGuardianBase *gsg, << hex << geom_rendering << ", unsupported: " << (unsupported_bits & Geom::GR_point_bits) << dec << "\n"; } - munge_points_to_quads(traverser); + if (!munge_points_to_quads(traverser, force)) { + return false; + } } bool cpu_animated = false; @@ -104,20 +112,24 @@ munge_geom(GraphicsStateGuardianBase *gsg, // the vertices in the CPU--and we have to do it before we call // munge_geom(), which might lose the tangent and binormal. CPT(GeomVertexData) animated_vertices = - _munged_data->animate_vertices(current_thread); + _munged_data->animate_vertices(force, current_thread); if (animated_vertices != _munged_data) { cpu_animated = true; _munged_data = animated_vertices; } - munge_texcoord_light_vector(traverser); + if (!munge_texcoord_light_vector(traverser, force)) { + return false; + } } // Now invoke the munger to ensure the resulting geometry is in // a GSG-friendly form. - munger->munge_geom(_geom, _munged_data, current_thread); + if (!munger->munge_geom(_geom, _munged_data, force, current_thread)) { + return false; + } StateMunger *state_munger; - DCAST_INTO_V(state_munger, munger); + DCAST_INTO_R(state_munger, munger, false); _state = state_munger->munge_state(_state); if (!cpu_animated) { @@ -126,7 +138,7 @@ munge_geom(GraphicsStateGuardianBase *gsg, // animation in hardware--then we have to calculate that // animation now. CPT(GeomVertexData) animated_vertices = - _munged_data->animate_vertices(current_thread); + _munged_data->animate_vertices(force, current_thread); if (animated_vertices != _munged_data) { cpu_animated = true; _munged_data = animated_vertices; @@ -151,11 +163,13 @@ munge_geom(GraphicsStateGuardianBase *gsg, if (_next != (CullableObject *)NULL) { if (_next->_state != (RenderState *)NULL) { _next->munge_geom(gsg, gsg->get_geom_munger(_next->_state, current_thread), - traverser); + traverser, force); } else { - _next->munge_geom(gsg, munger, traverser); + _next->munge_geom(gsg, munger, traverser, force); } } + + return true; } //////////////////////////////////////////////////////////////////// @@ -193,8 +207,12 @@ output(ostream &out) const { // // This may replace _geom, _munged_data, and _state. //////////////////////////////////////////////////////////////////// -void CullableObject:: -munge_points_to_quads(const CullTraverser *traverser) { +bool CullableObject:: +munge_points_to_quads(const CullTraverser *traverser, bool force) { + if (!force && !_munged_data->request_resident()) { + return false; + } + Thread *current_thread = traverser->get_current_thread(); PStatTimer timer(_munge_sprites_pcollector, current_thread); _sw_sprites_pcollector.add_level(_munged_data->get_num_rows()); @@ -462,8 +480,8 @@ munge_points_to_quads(const CullTraverser *traverser) { ++vi; } - nassertv(vi == orig_verts); - nassertv(new_data->get_num_rows() == new_verts); + nassertr(vi == orig_verts, false); + nassertr(new_data->get_num_rows() == new_verts, false); } PT(Geom) new_geom = new Geom(new_data); @@ -508,7 +526,7 @@ munge_points_to_quads(const CullTraverser *traverser) { GeomVertexReader index(primitive->get_vertices(), 0, current_thread); for (unsigned int *vi = vertices; vi != vertices_end; ++vi) { unsigned int v = index.get_data1i(); - nassertv(v < (unsigned int)orig_verts); + nassertr(v < (unsigned int)orig_verts, false); (*vi) = v; } } else { @@ -516,7 +534,7 @@ munge_points_to_quads(const CullTraverser *traverser) { unsigned int first_vertex = primitive->get_first_vertex(); for (int i = 0; i < num_vertices; ++i) { unsigned int v = i + first_vertex; - nassertv(v < (unsigned int)orig_verts); + nassertr(v < (unsigned int)orig_verts, false); vertices[i] = v; } } @@ -540,7 +558,7 @@ munge_points_to_quads(const CullTraverser *traverser) { GeomVertexWriter index(new_index, 0); for (unsigned int *vi = vertices; vi != vertices_end; ++vi) { int new_vi = (*vi) * 4; - nassertv(new_vi + 3 < new_prim_verts); + nassertr(new_vi + 3 < new_prim_verts, false); index.set_data1i(new_vi); index.set_data1i(new_vi + 1); index.set_data1i(new_vi + 2); @@ -561,6 +579,8 @@ munge_points_to_quads(const CullTraverser *traverser) { _geom = new_geom.p(); _munged_data = new_data; + + return true; } //////////////////////////////////////////////////////////////////// @@ -571,29 +591,24 @@ munge_points_to_quads(const CullTraverser *traverser) { // // This may replace _geom, _munged_data, and _state. //////////////////////////////////////////////////////////////////// -void CullableObject:: -munge_texcoord_light_vector(const CullTraverser *traverser) { +bool CullableObject:: +munge_texcoord_light_vector(const CullTraverser *traverser, bool force) { Thread *current_thread = traverser->get_current_thread(); PStatTimer timer(_munge_light_vector_pcollector, current_thread); if (_net_transform->is_singular()) { // If we're under a singular transform, never mind. - return; + return true; } - /* - CPT(TransformState) net_transform = - traverser->get_camera_transform()->compose(_modelview_transform); - */ - if (!_munged_data->has_column(InternalName::get_vertex()) || !_munged_data->has_column(InternalName::get_normal())) { // No vertex or normal; can't compute light vector. - return; + return true; } CPT(TexGenAttrib) tex_gen = _state->get_tex_gen(); - nassertv(tex_gen != (TexGenAttrib *)NULL); + nassertr(tex_gen != (TexGenAttrib *)NULL, false); const TexGenAttrib::LightVectors &light_vectors = tex_gen->get_light_vectors(); TexGenAttrib::LightVectors::const_iterator lvi; @@ -609,19 +624,12 @@ munge_texcoord_light_vector(const CullTraverser *traverser) { if (attrib != (RenderAttrib *)NULL) { CPT(LightAttrib) la = DCAST(LightAttrib, attrib); light = la->get_most_important_light(); - /* - if (!light.is_empty()) { - // Remove that light, now that we're accounting for it in - // the normal map. - _state->set_attrib(la->remove_on_light(light)); - } - */ } } if (!light.is_empty()) { string source_name = tex_gen->get_source_name(stage); Light *light_obj = light.node()->as_light(); - nassertv(light_obj != (Light *)NULL); + nassertr(light_obj != (Light *)NULL, false); // Determine the names of the tangent and binormal columns // associated with the stage's texcoord name. @@ -632,6 +640,12 @@ munge_texcoord_light_vector(const CullTraverser *traverser) { if (_munged_data->has_column(tangent_name) && _munged_data->has_column(binormal_name)) { + + if (!force && !_munged_data->request_resident()) { + // The data isn't resident; give up. + return false; + } + // Create a new column for the new texcoords. PT(GeomVertexData) new_data = _munged_data->replace_column (texcoord_name, 3, Geom::NT_float32, Geom::C_texcoord); @@ -668,6 +682,8 @@ munge_texcoord_light_vector(const CullTraverser *traverser) { } } } + + return true; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/cullableObject.h b/panda/src/pgraph/cullableObject.h index acb8522942..a8d482c37b 100644 --- a/panda/src/pgraph/cullableObject.h +++ b/panda/src/pgraph/cullableObject.h @@ -61,11 +61,13 @@ public: INLINE bool has_decals() const; - void munge_geom(GraphicsStateGuardianBase *gsg, - GeomMunger *munger, const CullTraverser *traverser); + bool munge_geom(GraphicsStateGuardianBase *gsg, + GeomMunger *munger, const CullTraverser *traverser, + bool force); INLINE void draw(GraphicsStateGuardianBase *gsg, Thread *current_thread); + INLINE bool request_resident() const; INLINE static void flush_level(); public: @@ -85,8 +87,8 @@ public: CullableObject *_next; private: - void munge_points_to_quads(const CullTraverser *traverser); - void munge_texcoord_light_vector(const CullTraverser *traverser); + bool munge_points_to_quads(const CullTraverser *traverser, bool force); + bool munge_texcoord_light_vector(const CullTraverser *traverser, bool force); static CPT(RenderState) get_flash_cpu_state(); static CPT(RenderState) get_flash_hardware_state(); diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index 7f571f46b4..d378689d13 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -326,7 +326,7 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any, for (gi = geoms->begin(); gi != geoms->end(); ++gi) { CPT(Geom) geom = (*gi)._geom.get_read_pointer(); geom->calc_tight_bounds(min_point, max_point, found_any, - geom->get_vertex_data(current_thread)->animate_vertices(current_thread), + geom->get_vertex_data(current_thread)->animate_vertices(true, current_thread), !next_transform->is_identity(), mat, current_thread); }