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