Panda3D
geomVertexArrayData.cxx
1 // Filename: geomVertexArrayData.cxx
2 // Created by: drose (17Mar05)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "geomVertexArrayData.h"
16 #include "geom.h"
17 #include "preparedGraphicsObjects.h"
18 #include "reversedNumericData.h"
19 #include "bamReader.h"
20 #include "bamWriter.h"
21 #include "pset.h"
22 #include "config_gobj.h"
23 #include "pStatTimer.h"
24 #include "configVariableInt.h"
25 #include "simpleAllocator.h"
26 #include "vertexDataBuffer.h"
27 #include "texture.h"
28 
29 ConfigVariableInt max_independent_vertex_data
30 ("max-independent-vertex-data", -1,
31  PRC_DESC("Specifies the maximum number of bytes of all vertex data "
32  "that is independent of the paging system. This is an "
33  "initial buffer before max-ram-vertex-data, specifically "
34  "designed for vertex datas that are dynamic in nature and "
35  "may change size or be created and destroyed frequently."));
36 
37 ConfigVariableInt vertex_data_page_size
38 ("vertex-data-page-size", 262144,
39  PRC_DESC("The number of bytes to allocate at a time for vertex data. "
40  "This also controls the page size that is compressed or written "
41  "to disk when vertex data pages are evicted from memory."));
42 
43 SimpleLru GeomVertexArrayData::_independent_lru("independent", max_independent_vertex_data);
44 SimpleLru GeomVertexArrayData::_small_lru("small", max_independent_vertex_data);
45 
46 VertexDataBook GeomVertexArrayData::_book(vertex_data_page_size);
47 
48 
49 TypeHandle GeomVertexArrayData::_type_handle;
50 TypeHandle GeomVertexArrayData::CData::_type_handle;
51 TypeHandle GeomVertexArrayDataHandle::_type_handle;
52 
53 ALLOC_DELETED_CHAIN_DEF(GeomVertexArrayDataHandle);
54 
55 ////////////////////////////////////////////////////////////////////
56 // Function: GeomVertexArrayData::Default Constructor
57 // Access: Private
58 // Description: Constructs an invalid object. This is only used when
59 // reading from the bam file.
60 ////////////////////////////////////////////////////////////////////
61 GeomVertexArrayData::
62 GeomVertexArrayData() : SimpleLruPage(0) {
63  _contexts = NULL;
64 
65  // Can't put it in the LRU until it has been read in and made valid.
66 }
67 
68 ////////////////////////////////////////////////////////////////////
69 // Function: GeomVertexArrayData::make_cow_copy
70 // Access: Protected, Virtual
71 // Description: Required to implement CopyOnWriteObject.
72 ////////////////////////////////////////////////////////////////////
73 PT(CopyOnWriteObject) GeomVertexArrayData::
74 make_cow_copy() {
75  return new GeomVertexArrayData(*this);
76 }
77 
78 ////////////////////////////////////////////////////////////////////
79 // Function: GeomVertexArrayData::Constructor
80 // Access: Published
81 // Description:
82 ////////////////////////////////////////////////////////////////////
83 GeomVertexArrayData::
84 GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
85  GeomVertexArrayData::UsageHint usage_hint) :
86  SimpleLruPage(0),
87  _array_format(array_format)
88 {
89  OPEN_ITERATE_ALL_STAGES(_cycler) {
90  CDStageWriter cdata(_cycler, pipeline_stage);
91  cdata->_usage_hint = usage_hint;
92  }
93  CLOSE_ITERATE_ALL_STAGES(_cycler);
94 
95  _contexts = NULL;
96 
97  set_lru_size(0);
98  nassertv(_array_format->is_registered());
99 }
100 
101 ////////////////////////////////////////////////////////////////////
102 // Function: GeomVertexArrayData::Copy Constructor
103 // Access: Published
104 // Description:
105 ////////////////////////////////////////////////////////////////////
106 GeomVertexArrayData::
107 GeomVertexArrayData(const GeomVertexArrayData &copy) :
108  CopyOnWriteObject(copy),
109  SimpleLruPage(copy),
110  _array_format(copy._array_format),
111  _cycler(copy._cycler)
112 {
113  _contexts = NULL;
114 
115  copy.mark_used_lru();
116 
117  set_lru_size(get_data_size_bytes());
118  nassertv(_array_format->is_registered());
119 }
120 
121 ////////////////////////////////////////////////////////////////////
122 // Function: GeomVertexArrayData::Copy Assignment Operator
123 // Access: Published
124 // Description: The copy assignment operator is not pipeline-safe.
125 // This will completely obliterate all stages of the
126 // pipeline, so don't do it for a GeomVertexArrayData
127 // that is actively being used for rendering.
128 ////////////////////////////////////////////////////////////////////
131  CopyOnWriteObject::operator = (copy);
132  SimpleLruPage::operator = (copy);
133 
134  copy.mark_used_lru();
135 
136  _array_format = copy._array_format;
137  _cycler = copy._cycler;
138 
139  OPEN_ITERATE_ALL_STAGES(_cycler) {
140  CDStageWriter cdata(_cycler, pipeline_stage);
141  cdata->_modified = Geom::get_next_modified();
142  }
143  CLOSE_ITERATE_ALL_STAGES(_cycler);
144 
145  nassertv(_array_format->is_registered());
146 }
147 
148 ////////////////////////////////////////////////////////////////////
149 // Function: GeomVertexArrayData::Destructor
150 // Access: Published, Virtual
151 // Description:
152 ////////////////////////////////////////////////////////////////////
153 GeomVertexArrayData::
154 ~GeomVertexArrayData() {
155  release_all();
156 }
157 
158 ////////////////////////////////////////////////////////////////////
159 // Function: GeomVertexArrayData::compare_to
160 // Access: Published
161 // Description: Returns 0 if the two arrays are equivalent, even if
162 // they are not the same pointer.
163 ////////////////////////////////////////////////////////////////////
165 compare_to(const GeomVertexArrayData &other) const {
166  Thread *current_thread = Thread::get_current_thread();
167 
168  CPT(GeomVertexArrayDataHandle) handle = get_handle(current_thread);
169  CPT(GeomVertexArrayDataHandle) other_handle = other.get_handle(current_thread);
170 
171  if (handle->get_usage_hint() != other_handle->get_usage_hint()) {
172  return (int)handle->get_usage_hint() - (int)other_handle->get_usage_hint();
173  }
174  if (handle->get_array_format() != other_handle->get_array_format()) {
175  return handle->get_array_format() < other_handle->get_array_format() ? -1 : 1;
176  }
177  if (handle->get_data_size_bytes() != other_handle->get_data_size_bytes()) {
178  return (int)handle->get_data_size_bytes() - (int)other_handle->get_data_size_bytes();
179  }
180  return memcmp(handle->get_read_pointer(true),
181  other_handle->get_read_pointer(true),
182  handle->get_data_size_bytes());
183 }
184 
185 ////////////////////////////////////////////////////////////////////
186 // Function: GeomVertexArrayData::set_usage_hint
187 // Access: Published
188 // Description: Changes the UsageHint hint for this array. See
189 // get_usage_hint().
190 //
191 // Don't call this in a downstream thread unless you
192 // don't mind it blowing away other changes you might
193 // have recently made in an upstream thread.
194 ////////////////////////////////////////////////////////////////////
196 set_usage_hint(GeomVertexArrayData::UsageHint usage_hint) {
197  CDWriter cdata(_cycler, true);
198  cdata->_usage_hint = usage_hint;
199  cdata->_modified = Geom::get_next_modified();
200 }
201 
202 ////////////////////////////////////////////////////////////////////
203 // Function: GeomVertexArrayData::output
204 // Access: Published
205 // Description:
206 ////////////////////////////////////////////////////////////////////
207 void GeomVertexArrayData::
208 output(ostream &out) const {
209  out << get_num_rows() << " rows: " << *get_array_format();
210 }
211 
212 ////////////////////////////////////////////////////////////////////
213 // Function: GeomVertexArrayData::write
214 // Access: Published
215 // Description:
216 ////////////////////////////////////////////////////////////////////
217 void GeomVertexArrayData::
218 write(ostream &out, int indent_level) const {
219  _array_format->write_with_data(out, indent_level, this);
220 }
221 
222 ////////////////////////////////////////////////////////////////////
223 // Function: GeomVertexArrayData::prepare
224 // Access: Public
225 // Description: Indicates that the data should be enqueued to be
226 // prepared in the indicated prepared_objects at the
227 // beginning of the next frame. This will ensure the
228 // data is already loaded into the GSG if it is expected
229 // to be rendered soon.
230 //
231 // Use this function instead of prepare_now() to preload
232 // datas from a user interface standpoint.
233 ////////////////////////////////////////////////////////////////////
235 prepare(PreparedGraphicsObjects *prepared_objects) {
236  prepared_objects->enqueue_vertex_buffer(this);
237 }
238 
239 ////////////////////////////////////////////////////////////////////
240 // Function: GeomVertexArrayData::is_prepared
241 // Access: Published
242 // Description: Returns true if the data has already been prepared
243 // or enqueued for preparation on the indicated GSG,
244 // false otherwise.
245 ////////////////////////////////////////////////////////////////////
247 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
248  if (_contexts == (Contexts *)NULL) {
249  return false;
250  }
251  Contexts::const_iterator ci;
252  ci = _contexts->find(prepared_objects);
253  if (ci != _contexts->end()) {
254  return true;
255  }
256  return prepared_objects->is_vertex_buffer_queued(this);
257 }
258 
259 ////////////////////////////////////////////////////////////////////
260 // Function: GeomVertexArrayData::prepare_now
261 // Access: Public
262 // Description: Creates a context for the data on the particular
263 // GSG, if it does not already exist. Returns the new
264 // (or old) VertexBufferContext. This assumes that the
265 // GraphicsStateGuardian is the currently active
266 // rendering context and that it is ready to accept new
267 // datas. If this is not necessarily the case, you
268 // should use prepare() instead.
269 //
270 // Normally, this is not called directly except by the
271 // GraphicsStateGuardian; a data does not need to be
272 // explicitly prepared by the user before it may be
273 // rendered.
274 ////////////////////////////////////////////////////////////////////
278  if (_contexts == (Contexts *)NULL) {
279  _contexts = new Contexts;
280  }
281  Contexts::const_iterator ci;
282  ci = _contexts->find(prepared_objects);
283  if (ci != _contexts->end()) {
284  return (*ci).second;
285  }
286 
287  VertexBufferContext *vbc = prepared_objects->prepare_vertex_buffer_now(this, gsg);
288  if (vbc != (VertexBufferContext *)NULL) {
289  (*_contexts)[prepared_objects] = vbc;
290  }
291  return vbc;
292 }
293 
294 ////////////////////////////////////////////////////////////////////
295 // Function: GeomVertexArrayData::release
296 // Access: Public
297 // Description: Frees the data context only on the indicated object,
298 // if it exists there. Returns true if it was released,
299 // false if it had not been prepared.
300 ////////////////////////////////////////////////////////////////////
302 release(PreparedGraphicsObjects *prepared_objects) {
303  if (_contexts != (Contexts *)NULL) {
304  Contexts::iterator ci;
305  ci = _contexts->find(prepared_objects);
306  if (ci != _contexts->end()) {
307  VertexBufferContext *vbc = (*ci).second;
308  prepared_objects->release_vertex_buffer(vbc);
309  return true;
310  }
311  }
312 
313  // Maybe it wasn't prepared yet, but it's about to be.
314  return prepared_objects->dequeue_vertex_buffer(this);
315 }
316 
317 ////////////////////////////////////////////////////////////////////
318 // Function: GeomVertexArrayData::release_all
319 // Access: Public
320 // Description: Frees the context allocated on all objects for which
321 // the data has been declared. Returns the number of
322 // contexts which have been freed.
323 ////////////////////////////////////////////////////////////////////
326  int num_freed = 0;
327 
328  if (_contexts != (Contexts *)NULL) {
329  // We have to traverse a copy of the _contexts list, because the
330  // PreparedGraphicsObjects object will call clear_prepared() in response
331  // to each release_vertex_buffer(), and we don't want to be modifying the
332  // _contexts list while we're traversing it.
333  Contexts temp = *_contexts;
334  num_freed = (int)_contexts->size();
335 
336  Contexts::const_iterator ci;
337  for (ci = temp.begin(); ci != temp.end(); ++ci) {
338  PreparedGraphicsObjects *prepared_objects = (*ci).first;
339  VertexBufferContext *vbc = (*ci).second;
340  prepared_objects->release_vertex_buffer(vbc);
341  }
342 
343  // Now that we've called release_vertex_buffer() on every known context,
344  // the _contexts list should have completely emptied itself.
345  nassertr(_contexts == NULL, num_freed);
346  }
347 
348  return num_freed;
349 }
350 
351 ////////////////////////////////////////////////////////////////////
352 // Function: GeomVertexArrayData::lru_epoch
353 // Access: Published, Static
354 // Description: Marks that an epoch has passed in each LRU. Asks the
355 // LRU's to consider whether they should perform
356 // evictions.
357 ////////////////////////////////////////////////////////////////////
360  _independent_lru.begin_epoch();
361  VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->begin_epoch();
362  VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->begin_epoch();
363 }
364 
365 ////////////////////////////////////////////////////////////////////
366 // Function: GeomVertexArrayData::evict_lru
367 // Access: Public, Virtual
368 // Description: Evicts the page from the LRU. Called internally when
369 // the LRU determines that it is full. May also be
370 // called externally when necessary to explicitly evict
371 // the page.
372 //
373 // It is legal for this method to either evict the page
374 // as requested, do nothing (in which case the eviction
375 // will be requested again at the next epoch), or
376 // requeue itself on the tail of the queue (in which
377 // case the eviction will be requested again much
378 // later).
379 ////////////////////////////////////////////////////////////////////
382  dequeue_lru();
383  CDWriter cdata(_cycler, true);
384  cdata->_buffer.page_out(_book);
385 }
386 
387 ////////////////////////////////////////////////////////////////////
388 // Function: GeomVertexArrayData::clear_prepared
389 // Access: Private
390 // Description: Removes the indicated PreparedGraphicsObjects table
391 // from the data array's table, without actually
392 // releasing the data array. This is intended to be
393 // called only from
394 // PreparedGraphicsObjects::release_vertex_buffer(); it should
395 // never be called by user code.
396 ////////////////////////////////////////////////////////////////////
397 void GeomVertexArrayData::
398 clear_prepared(PreparedGraphicsObjects *prepared_objects) {
399  nassertv(_contexts != (Contexts *)NULL);
400 
401  Contexts::iterator ci;
402  ci = _contexts->find(prepared_objects);
403  if (ci != _contexts->end()) {
404  _contexts->erase(ci);
405  if (_contexts->empty()) {
406  delete _contexts;
407  _contexts = NULL;
408  }
409  } else {
410  // If this assertion fails, clear_prepared() was given a
411  // prepared_objects which the data array didn't know about.
412  nassertv(false);
413  }
414 }
415 
416 ////////////////////////////////////////////////////////////////////
417 // Function: GeomVertexArrayData::reverse_data_endianness
418 // Access: Private
419 // Description: Fills a new data array with all numeric values
420 // expressed in the indicated array reversed,
421 // byte-for-byte, to convert littleendian to bigendian
422 // and vice-versa.
423 ////////////////////////////////////////////////////////////////////
424 void GeomVertexArrayData::
425 reverse_data_endianness(unsigned char *dest, const unsigned char *source,
426  size_t size) {
427  int num_columns = _array_format->get_num_columns();
428 
429  // Walk through each row of the data.
430  for (size_t pi = 0; pi < size; pi += _array_format->get_stride()) {
431  // For each row, visit all of the columns; and for each column,
432  // visit all of the components of that column.
433  for (int ci = 0; ci < num_columns; ++ci) {
434  const GeomVertexColumn *col = _array_format->get_column(ci);
435  int component_bytes = col->get_component_bytes();
436  if (component_bytes > 1) {
437  // Get the index of the beginning of the column.
438  size_t ci = pi + col->get_start();
439 
440  int num_components = col->get_num_components();
441  for (int cj = 0; cj < num_components; ++cj) {
442  // Reverse the bytes of each component.
443  ReversedNumericData nd(source + ci, component_bytes);
444  nd.store_value(dest + ci, component_bytes);
445  ci += component_bytes;
446  }
447  }
448  }
449  }
450 }
451 
452 ////////////////////////////////////////////////////////////////////
453 // Function: GeomVertexArrayData::register_with_read_factory
454 // Access: Public, Static
455 // Description: Tells the BamReader how to create objects of type
456 // GeomVertexArrayData.
457 ////////////////////////////////////////////////////////////////////
460  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
461 }
462 
463 ////////////////////////////////////////////////////////////////////
464 // Function: GeomVertexArrayData::write_datagram
465 // Access: Public, Virtual
466 // Description: Writes the contents of this object to the datagram
467 // for shipping out to a Bam file.
468 ////////////////////////////////////////////////////////////////////
472 
473  manager->write_pointer(dg, _array_format);
474  manager->write_cdata(dg, _cycler, this);
475 }
476 
477 ////////////////////////////////////////////////////////////////////
478 // Function: GeomVertexArrayData::read_raw_data
479 // Access: Public
480 // Description: Called by CData::fillin to read the raw data
481 // of the array from the indicated datagram.
482 ////////////////////////////////////////////////////////////////////
483 PTA_uchar GeomVertexArrayData::
485  size_t size = scan.get_uint32();
486  PTA_uchar data = PTA_uchar::empty_array(size, get_class_type());
487  const unsigned char *source_data =
488  (const unsigned char *)scan.get_datagram().get_data();
489  memcpy(data, source_data + scan.get_current_index(), size);
490  scan.skip_bytes(size);
491 
492  return data;
493 }
494 
495 ////////////////////////////////////////////////////////////////////
496 // Function: GeomVertexArrayData::complete_pointers
497 // Access: Public, Virtual
498 // Description: Receives an array of pointers, one for each time
499 // manager->read_pointer() was called in fillin().
500 // Returns the number of pointers processed.
501 ////////////////////////////////////////////////////////////////////
504  int pi = CopyOnWriteObject::complete_pointers(p_list, manager);
505 
506  _array_format = DCAST(GeomVertexArrayFormat, p_list[pi++]);
507 
508  return pi;
509 }
510 
511 ////////////////////////////////////////////////////////////////////
512 // Function: GeomVertexArrayData::finalize
513 // Access: Public, Virtual
514 // Description: Called by the BamReader to perform any final actions
515 // needed for setting up the object after all objects
516 // have been read and all pointers have been completed.
517 ////////////////////////////////////////////////////////////////////
519 finalize(BamReader *manager) {
520  // Now we need to register the format that we have read from the bam
521  // file (since it doesn't come out of the bam file automatically
522  // registered). This may change the format's pointer, which we
523  // should then update our own data to reflect. But since this may
524  // cause the unregistered object to destruct, we have to also tell
525  // the BamReader to return the new object from now on.
526 
527  CDWriter cdata(_cycler, true);
528 
529  CPT(GeomVertexArrayFormat) new_array_format =
530  GeomVertexArrayFormat::register_format(_array_format);
531 
532  manager->change_pointer(_array_format, new_array_format);
533  _array_format = new_array_format;
534 
535  PT(BamAuxData) aux_data = (BamAuxData *)manager->get_aux_data(this, "");
536  if (aux_data != (BamAuxData *)NULL) {
537  if (aux_data->_endian_reversed) {
538  // Now is the time to endian-reverse the data.
539  VertexDataBuffer new_buffer(cdata->_buffer.get_size());
540  reverse_data_endianness(new_buffer.get_write_pointer(), cdata->_buffer.get_read_pointer(true), cdata->_buffer.get_size());
541  cdata->_buffer.swap(new_buffer);
542  }
543  }
544 
545  set_lru_size(cdata->_buffer.get_size());
546 }
547 
548 ////////////////////////////////////////////////////////////////////
549 // Function: GeomVertexArrayData::make_from_bam
550 // Access: Protected, Static
551 // Description: This function is called by the BamReader's factory
552 // when a new object of type GeomVertexArrayData is encountered
553 // in the Bam file. It should create the GeomVertexArrayData
554 // and extract its information from the file.
555 ////////////////////////////////////////////////////////////////////
556 TypedWritable *GeomVertexArrayData::
557 make_from_bam(const FactoryParams &params) {
559  DatagramIterator scan;
560  BamReader *manager;
561 
562  parse_params(params, scan, manager);
563  object->fillin(scan, manager);
564  manager->register_finalize(object);
565 
566  return object;
567 }
568 
569 ////////////////////////////////////////////////////////////////////
570 // Function: GeomVertexArrayData::fillin
571 // Access: Protected
572 // Description: This internal function is called by make_from_bam to
573 // read in all of the relevant data from the BamFile for
574 // the new GeomVertexArrayData.
575 ////////////////////////////////////////////////////////////////////
576 void GeomVertexArrayData::
577 fillin(DatagramIterator &scan, BamReader *manager) {
578  CopyOnWriteObject::fillin(scan, manager);
579 
580  manager->read_pointer(scan);
581  manager->read_cdata(scan, _cycler, this);
582 }
583 
584 ////////////////////////////////////////////////////////////////////
585 // Function: GeomVertexArrayData::CData::Destructor
586 // Access: Public, Virtual
587 // Description:
588 ////////////////////////////////////////////////////////////////////
589 GeomVertexArrayData::CData::
590 ~CData() {
591 }
592 
593 ////////////////////////////////////////////////////////////////////
594 // Function: GeomVertexArrayData::CData::make_copy
595 // Access: Public, Virtual
596 // Description:
597 ////////////////////////////////////////////////////////////////////
598 CycleData *GeomVertexArrayData::CData::
599 make_copy() const {
600  return new CData(*this);
601 }
602 
603 ////////////////////////////////////////////////////////////////////
604 // Function: GeomVertexArrayData::CData::write_datagram
605 // Access: Public, Virtual
606 // Description: Writes the contents of this object to the datagram
607 // for shipping out to a Bam file.
608 ////////////////////////////////////////////////////////////////////
609 void GeomVertexArrayData::CData::
610 write_datagram(BamWriter *manager, Datagram &dg, void *extra_data) const {
611  GeomVertexArrayData *array_data = (GeomVertexArrayData *)extra_data;
612  dg.add_uint8(_usage_hint);
613 
614  dg.add_uint32(_buffer.get_size());
615 
616  if (manager->get_file_endian() == BamWriter::BE_native) {
617  // For native endianness, we only have to write the data directly.
618  dg.append_data(_buffer.get_read_pointer(true), _buffer.get_size());
619 
620  } else {
621  // Otherwise, we have to convert it.
622  unsigned char *new_data = (unsigned char *)alloca(_buffer.get_size());
623  array_data->reverse_data_endianness(new_data, _buffer.get_read_pointer(true), _buffer.get_size());
624  dg.append_data(new_data, _buffer.get_size());
625  }
626 }
627 
628 ////////////////////////////////////////////////////////////////////
629 // Function: GeomVertexArrayData::CData::fillin
630 // Access: Public, Virtual
631 // Description: This internal function is called by make_from_bam to
632 // read in all of the relevant data from the BamFile for
633 // the new GeomVertexArrayData.
634 ////////////////////////////////////////////////////////////////////
635 void GeomVertexArrayData::CData::
636 fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) {
637  GeomVertexArrayData *array_data = (GeomVertexArrayData *)extra_data;
638  _usage_hint = (UsageHint)scan.get_uint8();
639 
640  if (manager->get_file_minor_ver() < 8) {
641  // Before bam version 6.8, the array data was a PTA_uchar.
642  PTA_uchar new_data;
643  READ_PTA(manager, scan, array_data->read_raw_data, new_data);
644  _buffer.unclean_realloc(new_data.size());
645  _buffer.set_size(new_data.size());
646  memcpy(_buffer.get_write_pointer(), &new_data[0], new_data.size());
647 
648  } else {
649  // Now, the array data is just stored directly.
650  size_t size = scan.get_uint32();
651  _buffer.unclean_realloc(size);
652  _buffer.set_size(size);
653 
654  const unsigned char *source_data =
655  (const unsigned char *)scan.get_datagram().get_data();
656  memcpy(_buffer.get_write_pointer(), source_data + scan.get_current_index(), size);
657  scan.skip_bytes(size);
658  }
659 
660  bool endian_reversed = false;
661 
662  if (manager->get_file_endian() != BamReader::BE_native) {
663  // For non-native endian files, we have to convert the data.
664 
665  if (array_data->_array_format == (GeomVertexArrayFormat *)NULL) {
666  // But we can't do that until we've completed the _array_format
667  // pointer, which tells us how to convert it.
668  endian_reversed = true;
669  } else {
670  // Since we have the _array_format pointer now, we can reverse
671  // it immediately (and we should, to support threaded CData
672  // updates).
673  VertexDataBuffer new_buffer(_buffer.get_size());
674  array_data->reverse_data_endianness(new_buffer.get_write_pointer(), _buffer.get_read_pointer(true), _buffer.get_size());
675  _buffer.swap(new_buffer);
676  }
677  }
678 
679  if (endian_reversed) {
680  PT(BamAuxData) aux_data = new BamAuxData;
681  aux_data->_endian_reversed = endian_reversed;
682  manager->set_aux_data(array_data, "", aux_data);
683  }
684 
685  array_data->set_lru_size(_buffer.get_size());
686 
687  _modified = Geom::get_next_modified();
688 }
689 
690 ////////////////////////////////////////////////////////////////////
691 // Function: GeomVertexArrayDataHandle::get_write_pointer
692 // Access: Public
693 // Description: Returns a writable pointer to the beginning of the
694 // actual data stream.
695 ////////////////////////////////////////////////////////////////////
696 unsigned char *GeomVertexArrayDataHandle::
698  nassertr(_writable, NULL);
699  mark_used();
700  _cdata->_modified = Geom::get_next_modified();
701  return _cdata->_buffer.get_write_pointer();
702 }
703 
704 ////////////////////////////////////////////////////////////////////
705 // Function: GeomVertexArrayDataHandle::set_num_rows
706 // Access: Public
707 // Description:
708 ////////////////////////////////////////////////////////////////////
709 bool GeomVertexArrayDataHandle::
710 set_num_rows(int n) {
711  nassertr(_writable, false);
712  mark_used();
713 
714  int stride = _object->_array_format->get_stride();
715  size_t new_size = n * stride;
716  size_t orig_size = _cdata->_buffer.get_size();
717 
718  if (gobj_cat.is_spam()) {
719  gobj_cat.spam()
720  << _object << ".set_num_rows(" << n << "), size = " << new_size << "\n";
721  }
722 
723  if (new_size != orig_size) {
724  size_t orig_reserved_size = _cdata->_buffer.get_reserved_size();
725  if (new_size > orig_reserved_size) {
726  // Add more rows. Go up to the next power of two bytes, mainly
727  // to reduce the number of allocs needed.
728  size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)new_size);
729  nassertr(new_reserved_size >= new_size, false);
730 
731  _cdata->_buffer.clean_realloc(new_reserved_size);
732 
733  } else if (new_size == 0) {
734  // If we set the number of rows to 0, go ahead and clear the
735  // buffer altogether, and let the user build it up again from
736  // nothing, to try to reduce frivolous memory waste.
737  _cdata->_buffer.clear();
738  }
739 
740  _cdata->_buffer.set_size(new_size);
741 
742  // Now ensure that the newly-added rows are initialized to 0.
743  if (new_size > orig_size) {
744  memset(_cdata->_buffer.get_write_pointer() + orig_size, 0,
745  new_size - orig_size);
746  }
747 
748  _cdata->_modified = Geom::get_next_modified();
749 
750  if (get_current_thread()->get_pipeline_stage() == 0) {
751  _object->set_lru_size(_cdata->_buffer.get_size());
752  }
753 
754  nassertr(get_num_rows() == n, true);
755  return true;
756  }
757 
758  nassertr(get_num_rows() == n, false);
759  return false;
760 }
761 
762 ////////////////////////////////////////////////////////////////////
763 // Function: GeomVertexArrayDataHandle::unclean_set_num_rows
764 // Access: Public
765 // Description:
766 ////////////////////////////////////////////////////////////////////
767 bool GeomVertexArrayDataHandle::
768 unclean_set_num_rows(int n) {
769  nassertr(_writable, false);
770  mark_used();
771 
772  int stride = _object->_array_format->get_stride();
773  size_t new_size = n * stride;
774  size_t orig_size = _cdata->_buffer.get_size();
775  size_t orig_reserved_size = _cdata->_buffer.get_reserved_size();
776 
777  if (new_size != orig_size || new_size != orig_reserved_size) {
778  // Since this is unclean_set_num_rows(), we won't be using it to
779  // incrementally increase the array; instead, it will generally be
780  // used only to create an array initially. So it makes sense to
781  // set the reserved size to precisely the same as the target size.
782 
783  _cdata->_buffer.unclean_realloc(new_size);
784  _cdata->_buffer.set_size(new_size);
785 
786  // No need to fill to zero or copy the old buffer, since this is
787  // unclean_set_num_rows().
788 
789  if (new_size != orig_size) {
790  _cdata->_modified = Geom::get_next_modified();
791 
792  if (get_current_thread()->get_pipeline_stage() == 0) {
793  _object->set_lru_size(_cdata->_buffer.get_size());
794  }
795  }
796  return true;
797  }
798 
799  return false;
800 }
801 
802 ////////////////////////////////////////////////////////////////////
803 // Function: GeomVertexArrayDataHandle::reserve_num_rows
804 // Access: Public
805 // Description:
806 ////////////////////////////////////////////////////////////////////
807 bool GeomVertexArrayDataHandle::
808 reserve_num_rows(int n) {
809  nassertr(_writable, false);
810  mark_used();
811 
812  int stride = _object->_array_format->get_stride();
813  size_t new_reserved_size = n * stride;
814  new_reserved_size = max(_cdata->_buffer.get_size(), new_reserved_size);
815  size_t orig_reserved_size = _cdata->_buffer.get_reserved_size();
816 
817  if (gobj_cat.is_debug()) {
818  gobj_cat.debug()
819  << _object << ".reserve_num_rows(" << n << "), size = " << new_reserved_size << "\n";
820  }
821 
822  if (new_reserved_size != orig_reserved_size) {
823  // We allow the user to set the alloc point smaller with this
824  // call, assuming the user knows what he's doing. This allows the
825  // user to reduce wasted memory after completely filling up a
826  // buffer.
827  _cdata->_buffer.clean_realloc(new_reserved_size);
828  return true;
829  }
830 
831  return false;
832 }
833 
834 ////////////////////////////////////////////////////////////////////
835 // Function: GeomVertexArrayDataHandle::copy_data_from
836 // Access: Public
837 // Description: Copies the entire data array from the other object.
838 ////////////////////////////////////////////////////////////////////
841  nassertv(_writable);
842  other->mark_used();
843 
844  size_t size = other->_cdata->_buffer.get_size();
845  const unsigned char *source = other->_cdata->_buffer.get_read_pointer(true);
846 
847  copy_data_from(source, size);
848 }
849 
850 ////////////////////////////////////////////////////////////////////
851 // Function: GeomVertexArrayDataHandle::copy_subdata_from
852 // Access: Public
853 // Description: Copies a portion of the data array from the other
854 // object into a portion of the data array of this
855 // object. If to_size != from_size, the size of this
856 // data array is adjusted accordingly.
857 ////////////////////////////////////////////////////////////////////
859 copy_subdata_from(size_t to_start, size_t to_size,
860  const GeomVertexArrayDataHandle *other,
861  size_t from_start, size_t from_size) {
862  other->mark_used();
863 
864  const VertexDataBuffer &from_buffer = other->_cdata->_buffer;
865  size_t from_buffer_orig_size = from_buffer.get_size();
866  from_start = min(from_start, from_buffer_orig_size);
867  from_size = min(from_size, from_buffer_orig_size - from_start);
868 
869  copy_subdata_from(to_start, to_size,
870  other->get_read_pointer(true),
871  from_start, from_size);
872 }
873 
874 ////////////////////////////////////////////////////////////////////
875 // Function: GeomVertexArrayDataHandle::copy_data_from
876 // Access: Public
877 // Description: Copies the entire data array from the buffer.
878 ////////////////////////////////////////////////////////////////////
880 copy_data_from(const unsigned char *source, size_t size) {
881  nassertv(_writable);
882  mark_used();
883 
884  _cdata->_buffer.unclean_realloc(size);
885  _cdata->_buffer.set_size(size);
886 
887  unsigned char *dest = _cdata->_buffer.get_write_pointer();
888  memcpy(dest, source, size);
889 
890  _cdata->_modified = Geom::get_next_modified();
891 
892  if (get_current_thread()->get_pipeline_stage() == 0) {
893  _object->set_lru_size(_cdata->_buffer.get_size());
894  }
895 }
896 
897 ////////////////////////////////////////////////////////////////////
898 // Function: GeomVertexArrayDataHandle::copy_subdata_from
899 // Access: Public
900 // Description: Copies a portion of the data array from the buffer
901 // into a portion of the data array of this object.
902 // If to_size != from_size, the size of this data
903 // array is adjusted accordingly.
904 ////////////////////////////////////////////////////////////////////
906 copy_subdata_from(size_t to_start, size_t to_size,
907  const unsigned char *source,
908  size_t from_start, size_t from_size) {
909  nassertv(_writable);
910  mark_used();
911 
912  VertexDataBuffer &to_buffer = _cdata->_buffer;
913  size_t to_buffer_orig_size = to_buffer.get_size();
914  to_start = min(to_start, to_buffer_orig_size);
915  to_size = min(to_size, to_buffer_orig_size - to_start);
916 
917  if (from_size < to_size) {
918  // Reduce the array.
919  unsigned char *pointer = to_buffer.get_write_pointer();
920  memmove(pointer + to_start + to_size,
921  pointer + to_start + from_size,
922  to_buffer_orig_size - (to_start + to_size));
923  to_buffer.set_size(to_buffer_orig_size + from_size - to_size);
924 
925  } else if (to_size < from_size) {
926  // Expand the array.
927  size_t needed_size = to_buffer_orig_size + from_size - to_size;
928  size_t to_buffer_orig_reserved_size = to_buffer.get_reserved_size();
929  if (needed_size > to_buffer_orig_reserved_size) {
930  size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)needed_size);
931  to_buffer.clean_realloc(new_reserved_size);
932  }
933  to_buffer.set_size(needed_size);
934 
935  unsigned char *pointer = to_buffer.get_write_pointer();
936  memmove(pointer + to_start + to_size,
937  pointer + to_start + from_size,
938  to_buffer_orig_size - (to_start + to_size));
939  }
940 
941  // Now copy the data.
942  memcpy(to_buffer.get_write_pointer() + to_start,
943  source + from_start, from_size);
944  _cdata->_modified = Geom::get_next_modified();
945 
946  if (get_current_thread()->get_pipeline_stage() == 0) {
947  _object->set_lru_size(_cdata->_buffer.get_size());
948  }
949 }
950 
951 ////////////////////////////////////////////////////////////////////
952 // Function: GeomVertexArrayDataHandle::set_data
953 // Access: Public
954 // Description: Replaces the entire raw data array with the contents
955 // of the indicated string. This is primarily for the
956 // benefit of high-level languages like Python.
957 ////////////////////////////////////////////////////////////////////
959 set_data(const string &data) {
960  nassertv(_writable);
961  mark_used();
962 
963  _cdata->_buffer.unclean_realloc(data.size());
964  _cdata->_buffer.set_size(data.size());
965  memcpy(_cdata->_buffer.get_write_pointer(), data.data(), data.size());
966 
967  _cdata->_modified = Geom::get_next_modified();
968 
969  if (get_current_thread()->get_pipeline_stage() == 0) {
970  _object->set_lru_size(_cdata->_buffer.get_size());
971  }
972 }
973 
974 ////////////////////////////////////////////////////////////////////
975 // Function: GeomVertexArrayDataHandle::set_subdata
976 // Access: Public
977 // Description: Replaces a portion of the data array from the
978 // indicated string. If size != data.size(), the size
979 // of this data array is adjusted accordingly.
980 //
981 // This is primarily for the benefit of high-level
982 // languages like Python.
983 ////////////////////////////////////////////////////////////////////
985 set_subdata(size_t start, size_t size, const string &data) {
986  nassertv(_writable);
987  mark_used();
988 
989  VertexDataBuffer &to_buffer = _cdata->_buffer;
990  size_t to_buffer_orig_size = to_buffer.get_size();
991  start = min(start, to_buffer_orig_size);
992  size = min(size, to_buffer_orig_size - start);
993 
994  size_t from_size = data.size();
995 
996  if (from_size < size) {
997  // Reduce the array.
998  unsigned char *pointer = to_buffer.get_write_pointer();
999  memmove(pointer + start + from_size,
1000  pointer + start + size,
1001  to_buffer_orig_size - (start + size));
1002  to_buffer.set_size(to_buffer_orig_size + from_size - size);
1003 
1004  } else if (size < from_size) {
1005  // Expand the array.
1006  size_t needed_size = to_buffer_orig_size + from_size - size;
1007  size_t to_buffer_orig_reserved_size = to_buffer.get_reserved_size();
1008  if (needed_size > to_buffer_orig_reserved_size) {
1009  size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)needed_size);
1010  to_buffer.clean_realloc(new_reserved_size);
1011  }
1012  to_buffer.set_size(needed_size);
1013 
1014  unsigned char *pointer = to_buffer.get_write_pointer();
1015  memmove(pointer + start + from_size,
1016  pointer + start + size,
1017  to_buffer_orig_size - (start + size));
1018  }
1019 
1020  // Now copy the data.
1021  memcpy(to_buffer.get_write_pointer() + start, data.data(), from_size);
1022  _cdata->_modified = Geom::get_next_modified();
1023 
1024  if (get_current_thread()->get_pipeline_stage() == 0) {
1025  _object->set_lru_size(_cdata->_buffer.get_size());
1026  }
1027 }
VertexBufferContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the data on the particular GSG, if it does not already exist.
unsigned char * get_write_pointer()
Returns a writable pointer to the raw data.
size_t get_size() const
Returns the number of bytes in the buffer.
An implementation of a very simple LRU algorithm.
Definition: simpleLru.h:31
int compare_to(const GeomVertexArrayData &other) const
Returns 0 if the two arrays are equivalent, even if they are not the same pointer.
void add_uint8(PN_uint8 value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:138
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
PTA_uchar read_raw_data(BamReader *manager, DatagramIterator &source)
Called by CData::fillin to read the raw data of the array from the indicated datagram.
void operator=(const GeomVertexArrayData &copy)
The copy assignment operator is not pipeline-safe.
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:144
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:753
void mark_used_lru() const
To be called when the page is used; this will move it to the tail of the SimpleLru queue it is alread...
Definition: simpleLru.I:184
const Datagram & get_datagram() const
Return the datagram of this iterator.
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:398
bool is_vertex_buffer_queued(const GeomVertexArrayData *data) const
Returns true if the vertex buffer has been queued on this GSG, false otherwise.
BamEndian get_file_endian() const
Returns the endian preference indicated by the Bam file currently being written.
Definition: bamWriter.I:54
int release_all()
Frees the context allocated on all objects for which the data has been declared.
void release_vertex_buffer(VertexBufferContext *vbc)
Indicates that a data context, created by a previous call to prepare_vertex_buffer(), is no longer needed.
BamEndian get_file_endian() const
Returns the endian preference indicated by the Bam file currently being read.
Definition: bamReader.I:119
void begin_epoch()
Marks the end of the previous epoch and the beginning of the next one.
Definition: simpleLru.I:94
VertexBufferContext * prepare_vertex_buffer_now(GeomVertexArrayData *data, GraphicsStateGuardianBase *gsg)
Immediately creates a new VertexBufferContext for the indicated data and returns it.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
static SimpleLru * get_global_lru(RamClass rclass)
Returns a pointer to the global LRU object that manages the VertexDataPage&#39;s with the indicated RamCl...
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
A table of objects that are saved within the graphics context for reference by handle later...
static void lru_epoch()
Marks that an epoch has passed in each LRU.
void set_data(const string &data)
Replaces the entire raw data array with the contents of the indicated string.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:105
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class&#39;s make_from_bam() method to read in all...
This defines how a single column is interleaved within a vertex array stored within a Geom...
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
void prepare(PreparedGraphicsObjects *prepared_objects)
Indicates that the data should be enqueued to be prepared in the indicated prepared_objects at the be...
void clean_realloc(size_t reserved_size)
Changes the "reserved" size of the buffer, preserving its data (except for any data beyond the new en...
virtual void evict_lru()
Evicts the page from the LRU.
size_t get_current_index() const
Returns the current position within the datagram of the next piece of data to extract.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin()...
void set_subdata(size_t start, size_t size, const string &data)
Replaces a portion of the data array from the indicated string.
void skip_bytes(size_t size)
Skips over the indicated number of bytes in the datagram.
bool change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_pointer)
Indicates that an object recently read from the bam stream should be replaced with a new object...
Definition: bamReader.cxx:503
One atomic piece that may be managed by a SimpleLru chain.
Definition: simpleLru.h:70
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
bool dequeue_vertex_buffer(GeomVertexArrayData *data)
Removes a buffer from the queued list of data arrays to be prepared.
A block of bytes that stores the actual raw vertex data referenced by a GeomVertexArrayData object...
size_t get_reserved_size() const
Returns the total number of bytes "reserved" in the buffer.
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the data context only on the indicated object, if it exists there.
void enqueue_vertex_buffer(GeomVertexArrayData *data)
Indicates that a buffer would like to be put on the list to be prepared when the GSG is next ready to...
void set_size(size_t size)
Changes the size of the buffer.
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:886
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the data has already been prepared or enqueued for preparation on the indicated GSG...
unsigned char * get_write_pointer()
Returns a writable pointer to the beginning of the actual data stream.
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
NativeNumericData and ReversedNumericData work together to provide a sneaky interface for automatical...
void copy_data_from(const GeomVertexArrayDataHandle *other)
Copies the entire data array from the other object.
A collection of VertexDataPages, which can be used to allocate new VertexDataBlock objects...
int get_start() const
Returns the byte within the array record at which this column starts.
const unsigned char * get_read_pointer(bool force) const
Returns a readable pointer to the beginning of the actual data stream, or NULL if the data is not cur...
int get_component_bytes() const
Returns the number of bytes used by each component (that is, by one element of the numeric type)...
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:1845
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
void mark_used() const
Marks the array data recently-used.
A thread; that is, a lightweight process.
Definition: thread.h:51
AuxData * get_aux_data(TypedWritable *obj, const string &name) const
Returns the pointer previously associated with the bam reader by a previous call to set_aux_data()...
Definition: bamReader.cxx:206
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:192
void copy_subdata_from(size_t to_start, size_t to_size, const GeomVertexArrayDataHandle *other, size_t from_start, size_t from_size)
Copies a portion of the data array from the other object into a portion of the data array of this obj...
UsageHint get_usage_hint() const
Returns the usage hint that describes to the rendering backend how often the vertex data will be modi...
void set_usage_hint(UsageHint usage_hint)
Changes the UsageHint hint for this array.
static UpdateSeq get_next_modified()
Returns a monotonically increasing sequence.
Definition: geom.cxx:1324
This is a special class object that holds all the information returned by a particular GSG to indicat...
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin()...
This is a convenience class to specialize ConfigVariable as an integer type.
static void register_with_read_factory()
Tells the BamReader how to create objects of type GeomVertexArrayData.
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
int get_num_components() const
Returns the number of components of the column: the number of instances of the NumericType in each el...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
void set_aux_data(TypedWritable *obj, const string &name, AuxData *data)
Associates an arbitrary block of data with the indicated object (or NULL), and the indicated name...
Definition: bamReader.cxx:182
void store_value(void *dest, size_t length) const
Copies the data, with byte reversal if appropriate, into the indicated numeric variable, whose address and sizeof are given.
This is the data for one array of a GeomVertexData structure.
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
const void * get_data() const
Returns a pointer to the beginning of the datagram&#39;s data.
Definition: datagram.I:447
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:279
void read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:658